diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 0903765c31..0000000000 --- a/.editorconfig +++ /dev/null @@ -1,1101 +0,0 @@ -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -max_line_length = 80 -tab_width = 4 -ij_continuation_indent_size = 8 -ij_formatter_off_tag = @formatter:off -ij_formatter_on_tag = @formatter:on -ij_formatter_tags_enabled = false -ij_smart_tabs = false -ij_visual_guides = 80,120 -ij_wrap_on_typing = false - -[] -indent_size = 2 -tab_width = 2 - -[*.bib] -ij_bibtex_keep_first_column_comment = true -ij_bibtex_keep_indents_on_empty_lines = false -ij_bibtex_wrap_long_lines = false - -[*.css] -ij_css_align_closing_brace_with_properties = false -ij_css_blank_lines_around_nested_selector = 1 -ij_css_blank_lines_between_blocks = 1 -ij_css_brace_placement = end_of_line -ij_css_enforce_quotes_on_format = false -ij_css_hex_color_long_format = false -ij_css_hex_color_lower_case = false -ij_css_hex_color_short_format = false -ij_css_hex_color_upper_case = false -ij_css_keep_blank_lines_in_code = 2 -ij_css_keep_indents_on_empty_lines = false -ij_css_keep_single_line_blocks = false -ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow -ij_css_space_after_colon = true -ij_css_space_before_opening_brace = true -ij_css_use_double_quotes = true -ij_css_value_alignment = do_not_align - -[*.java] -ij_java_align_consecutive_assignments = false -ij_java_align_consecutive_variable_declarations = false -ij_java_align_group_field_declarations = false -ij_java_align_multiline_annotation_parameters = false -ij_java_align_multiline_array_initializer_expression = false -ij_java_align_multiline_assignment = false -ij_java_align_multiline_binary_operation = false -ij_java_align_multiline_chained_methods = false -ij_java_align_multiline_extends_list = false -ij_java_align_multiline_for = true -ij_java_align_multiline_method_parentheses = false -ij_java_align_multiline_parameters = true -ij_java_align_multiline_parameters_in_calls = false -ij_java_align_multiline_parenthesized_expression = false -ij_java_align_multiline_records = true -ij_java_align_multiline_resources = true -ij_java_align_multiline_ternary_operation = false -ij_java_align_multiline_text_blocks = false -ij_java_align_multiline_throws_list = false -ij_java_align_subsequent_simple_methods = false -ij_java_align_throws_keyword = false -ij_java_annotation_parameter_wrap = off -ij_java_array_initializer_new_line_after_left_brace = false -ij_java_array_initializer_right_brace_on_new_line = false -ij_java_array_initializer_wrap = off -ij_java_assert_statement_colon_on_next_line = false -ij_java_assert_statement_wrap = off -ij_java_assignment_wrap = off -ij_java_binary_operation_sign_on_next_line = false -ij_java_binary_operation_wrap = off -ij_java_blank_lines_after_anonymous_class_header = 0 -ij_java_blank_lines_after_class_header = 0 -ij_java_blank_lines_after_imports = 1 -ij_java_blank_lines_after_package = 1 -ij_java_blank_lines_around_class = 1 -ij_java_blank_lines_around_field = 0 -ij_java_blank_lines_around_field_in_interface = 0 -ij_java_blank_lines_around_initializer = 1 -ij_java_blank_lines_around_method = 1 -ij_java_blank_lines_around_method_in_interface = 1 -ij_java_blank_lines_before_class_end = 0 -ij_java_blank_lines_before_imports = 1 -ij_java_blank_lines_before_method_body = 0 -ij_java_blank_lines_before_package = 0 -ij_java_block_brace_style = end_of_line -ij_java_block_comment_at_first_column = true -ij_java_call_parameters_new_line_after_left_paren = false -ij_java_call_parameters_right_paren_on_new_line = false -ij_java_call_parameters_wrap = off -ij_java_case_statement_on_separate_line = true -ij_java_catch_on_new_line = false -ij_java_class_annotation_wrap = split_into_lines -ij_java_class_brace_style = end_of_line -ij_java_class_count_to_use_import_on_demand = 5 -ij_java_class_names_in_javadoc = 1 -ij_java_do_not_indent_top_level_class_members = false -ij_java_do_not_wrap_after_single_annotation = false -ij_java_do_while_brace_force = never -ij_java_doc_add_blank_line_after_description = true -ij_java_doc_add_blank_line_after_param_comments = false -ij_java_doc_add_blank_line_after_return = false -ij_java_doc_add_p_tag_on_empty_lines = true -ij_java_doc_align_exception_comments = true -ij_java_doc_align_param_comments = true -ij_java_doc_do_not_wrap_if_one_line = false -ij_java_doc_enable_formatting = true -ij_java_doc_enable_leading_asterisks = true -ij_java_doc_indent_on_continuation = false -ij_java_doc_keep_empty_lines = true -ij_java_doc_keep_empty_parameter_tag = true -ij_java_doc_keep_empty_return_tag = true -ij_java_doc_keep_empty_throws_tag = true -ij_java_doc_keep_invalid_tags = true -ij_java_doc_param_description_on_new_line = false -ij_java_doc_preserve_line_breaks = false -ij_java_doc_use_throws_not_exception_tag = true -ij_java_else_on_new_line = false -ij_java_enum_constants_wrap = off -ij_java_extends_keyword_wrap = off -ij_java_extends_list_wrap = off -ij_java_field_annotation_wrap = split_into_lines -ij_java_finally_on_new_line = false -ij_java_for_brace_force = never -ij_java_for_statement_new_line_after_left_paren = false -ij_java_for_statement_right_paren_on_new_line = false -ij_java_for_statement_wrap = off -ij_java_generate_final_locals = false -ij_java_generate_final_parameters = false -ij_java_if_brace_force = never -ij_java_imports_layout = *,|,javax.**,java.**,|,$* -ij_java_indent_case_from_switch = true -ij_java_insert_inner_class_imports = false -ij_java_insert_override_annotation = true -ij_java_keep_blank_lines_before_right_brace = 2 -ij_java_keep_blank_lines_between_package_declaration_and_header = 2 -ij_java_keep_blank_lines_in_code = 2 -ij_java_keep_blank_lines_in_declarations = 2 -ij_java_keep_control_statement_in_one_line = true -ij_java_keep_first_column_comment = true -ij_java_keep_indents_on_empty_lines = false -ij_java_keep_line_breaks = true -ij_java_keep_multiple_expressions_in_one_line = false -ij_java_keep_simple_blocks_in_one_line = false -ij_java_keep_simple_classes_in_one_line = false -ij_java_keep_simple_lambdas_in_one_line = false -ij_java_keep_simple_methods_in_one_line = false -ij_java_label_indent_absolute = false -ij_java_label_indent_size = 0 -ij_java_lambda_brace_style = end_of_line -ij_java_layout_static_imports_separately = true -ij_java_line_comment_add_space = false -ij_java_line_comment_at_first_column = true -ij_java_method_annotation_wrap = split_into_lines -ij_java_method_brace_style = end_of_line -ij_java_method_call_chain_wrap = off -ij_java_method_parameters_new_line_after_left_paren = false -ij_java_method_parameters_right_paren_on_new_line = false -ij_java_method_parameters_wrap = off -ij_java_modifier_list_wrap = false -ij_java_names_count_to_use_import_on_demand = 3 -ij_java_new_line_after_lparen_in_record_header = false -ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.* -ij_java_parameter_annotation_wrap = off -ij_java_parentheses_expression_new_line_after_left_paren = false -ij_java_parentheses_expression_right_paren_on_new_line = false -ij_java_place_assignment_sign_on_next_line = false -ij_java_prefer_longer_names = true -ij_java_prefer_parameters_wrap = false -ij_java_record_components_wrap = normal -ij_java_repeat_synchronized = true -ij_java_replace_instanceof_and_cast = false -ij_java_replace_null_check = true -ij_java_replace_sum_lambda_with_method_ref = true -ij_java_resource_list_new_line_after_left_paren = false -ij_java_resource_list_right_paren_on_new_line = false -ij_java_resource_list_wrap = off -ij_java_rparen_on_new_line_in_record_header = false -ij_java_space_after_closing_angle_bracket_in_type_argument = false -ij_java_space_after_colon = true -ij_java_space_after_comma = true -ij_java_space_after_comma_in_type_arguments = true -ij_java_space_after_for_semicolon = true -ij_java_space_after_quest = true -ij_java_space_after_type_cast = true -ij_java_space_before_annotation_array_initializer_left_brace = false -ij_java_space_before_annotation_parameter_list = false -ij_java_space_before_array_initializer_left_brace = false -ij_java_space_before_catch_keyword = true -ij_java_space_before_catch_left_brace = true -ij_java_space_before_catch_parentheses = true -ij_java_space_before_class_left_brace = true -ij_java_space_before_colon = true -ij_java_space_before_colon_in_foreach = true -ij_java_space_before_comma = false -ij_java_space_before_do_left_brace = true -ij_java_space_before_else_keyword = true -ij_java_space_before_else_left_brace = true -ij_java_space_before_finally_keyword = true -ij_java_space_before_finally_left_brace = true -ij_java_space_before_for_left_brace = true -ij_java_space_before_for_parentheses = true -ij_java_space_before_for_semicolon = false -ij_java_space_before_if_left_brace = true -ij_java_space_before_if_parentheses = true -ij_java_space_before_method_call_parentheses = false -ij_java_space_before_method_left_brace = true -ij_java_space_before_method_parentheses = false -ij_java_space_before_opening_angle_bracket_in_type_parameter = false -ij_java_space_before_quest = true -ij_java_space_before_switch_left_brace = true -ij_java_space_before_switch_parentheses = true -ij_java_space_before_synchronized_left_brace = true -ij_java_space_before_synchronized_parentheses = true -ij_java_space_before_try_left_brace = true -ij_java_space_before_try_parentheses = true -ij_java_space_before_type_parameter_list = false -ij_java_space_before_while_keyword = true -ij_java_space_before_while_left_brace = true -ij_java_space_before_while_parentheses = true -ij_java_space_inside_one_line_enum_braces = false -ij_java_space_within_empty_array_initializer_braces = false -ij_java_space_within_empty_method_call_parentheses = false -ij_java_space_within_empty_method_parentheses = false -ij_java_spaces_around_additive_operators = true -ij_java_spaces_around_assignment_operators = true -ij_java_spaces_around_bitwise_operators = true -ij_java_spaces_around_equality_operators = true -ij_java_spaces_around_lambda_arrow = true -ij_java_spaces_around_logical_operators = true -ij_java_spaces_around_method_ref_dbl_colon = false -ij_java_spaces_around_multiplicative_operators = true -ij_java_spaces_around_relational_operators = true -ij_java_spaces_around_shift_operators = true -ij_java_spaces_around_type_bounds_in_type_parameters = true -ij_java_spaces_around_unary_operator = false -ij_java_spaces_within_angle_brackets = false -ij_java_spaces_within_annotation_parentheses = false -ij_java_spaces_within_array_initializer_braces = false -ij_java_spaces_within_braces = false -ij_java_spaces_within_brackets = false -ij_java_spaces_within_cast_parentheses = false -ij_java_spaces_within_catch_parentheses = false -ij_java_spaces_within_for_parentheses = false -ij_java_spaces_within_if_parentheses = false -ij_java_spaces_within_method_call_parentheses = false -ij_java_spaces_within_method_parentheses = false -ij_java_spaces_within_parentheses = false -ij_java_spaces_within_switch_parentheses = false -ij_java_spaces_within_synchronized_parentheses = false -ij_java_spaces_within_try_parentheses = false -ij_java_spaces_within_while_parentheses = false -ij_java_special_else_if_treatment = true -ij_java_subclass_name_suffix = Impl -ij_java_ternary_operation_signs_on_next_line = false -ij_java_ternary_operation_wrap = off -ij_java_test_name_suffix = Test -ij_java_throws_keyword_wrap = off -ij_java_throws_list_wrap = off -ij_java_use_external_annotations = false -ij_java_use_fq_class_names = false -ij_java_use_relative_indents = false -ij_java_use_single_class_imports = true -ij_java_variable_annotation_wrap = off -ij_java_visibility = public -ij_java_while_brace_force = never -ij_java_while_on_new_line = false -ij_java_wrap_comments = false -ij_java_wrap_first_method_in_call_chain = false -ij_java_wrap_long_lines = false - -[*.properties] -ij_properties_align_group_field_declarations = false -ij_properties_keep_blank_lines = false -ij_properties_key_value_delimiter = equals -ij_properties_spaces_around_key_value_delimiter = false - -[*.scala] -ij_continuation_indent_size = 2 -ij_scala_align_composite_pattern = true -ij_scala_align_extends_with = 0 -ij_scala_align_group_field_declarations = false -ij_scala_align_if_else = false -ij_scala_align_in_columns_case_branch = false -ij_scala_align_multiline_binary_operation = false -ij_scala_align_multiline_chained_methods = false -ij_scala_align_multiline_for = true -ij_scala_align_multiline_parameters = true -ij_scala_align_multiline_parameters_in_calls = false -ij_scala_align_multiline_parenthesized_expression = false -ij_scala_align_tuple_elements = false -ij_scala_align_types_in_multiline_declarations = false -ij_scala_alternate_continuation_indent_for_params = 4 -ij_scala_binary_operation_wrap = off -ij_scala_blank_lines_after_anonymous_class_header = 0 -ij_scala_blank_lines_after_class_header = 0 -ij_scala_blank_lines_after_imports = 1 -ij_scala_blank_lines_after_package = 1 -ij_scala_blank_lines_around_class = 1 -ij_scala_blank_lines_around_field = 0 -ij_scala_blank_lines_around_field_in_inner_scopes = 0 -ij_scala_blank_lines_around_field_in_interface = 0 -ij_scala_blank_lines_around_method = 1 -ij_scala_blank_lines_around_method_in_inner_scopes = 1 -ij_scala_blank_lines_around_method_in_interface = 1 -ij_scala_blank_lines_before_imports = 1 -ij_scala_blank_lines_before_method_body = 0 -ij_scala_blank_lines_before_package = 0 -ij_scala_block_brace_style = end_of_line -ij_scala_block_comment_at_first_column = true -ij_scala_call_parameters_new_line_after_lparen = 0 -ij_scala_call_parameters_right_paren_on_new_line = false -ij_scala_call_parameters_wrap = off -ij_scala_case_clause_brace_force = never -ij_scala_catch_on_new_line = false -ij_scala_class_annotation_wrap = split_into_lines -ij_scala_class_brace_style = end_of_line -ij_scala_closure_brace_force = never -ij_scala_do_not_align_block_expr_params = true -ij_scala_do_not_indent_case_clause_body = false -ij_scala_do_not_indent_tuples_close_brace = true -ij_scala_do_while_brace_force = never -ij_scala_else_on_new_line = false -ij_scala_enable_scaladoc_formatting = true -ij_scala_enforce_functional_syntax_for_unit = true -ij_scala_extends_keyword_wrap = off -ij_scala_extends_list_wrap = off -ij_scala_field_annotation_wrap = split_into_lines -ij_scala_finally_brace_force = never -ij_scala_finally_on_new_line = false -ij_scala_for_brace_force = never -ij_scala_for_statement_wrap = off -ij_scala_formatter = 0 -ij_scala_if_brace_force = never -ij_scala_implicit_value_class_suffix = Ops -ij_scala_indent_braced_function_args = true -ij_scala_indent_case_from_switch = true -ij_scala_indent_first_parameter = true -ij_scala_indent_first_parameter_clause = false -ij_scala_indent_type_arguments = true -ij_scala_indent_type_parameters = true -ij_scala_insert_whitespaces_in_simple_one_line_method = true -ij_scala_keep_blank_lines_before_right_brace = 2 -ij_scala_keep_blank_lines_in_code = 2 -ij_scala_keep_blank_lines_in_declarations = 2 -ij_scala_keep_comments_on_same_line = true -ij_scala_keep_first_column_comment = false -ij_scala_keep_indents_on_empty_lines = false -ij_scala_keep_line_breaks = true -ij_scala_keep_one_line_lambdas_in_arg_list = false -ij_scala_keep_simple_blocks_in_one_line = false -ij_scala_keep_simple_methods_in_one_line = false -ij_scala_keep_xml_formatting = false -ij_scala_line_comment_at_first_column = true -ij_scala_method_annotation_wrap = split_into_lines -ij_scala_method_brace_force = never -ij_scala_method_brace_style = end_of_line -ij_scala_method_call_chain_wrap = off -ij_scala_method_parameters_new_line_after_left_paren = false -ij_scala_method_parameters_right_paren_on_new_line = false -ij_scala_method_parameters_wrap = off -ij_scala_modifier_list_wrap = false -ij_scala_multiline_string_align_dangling_closing_quotes = false -ij_scala_multiline_string_closing_quotes_on_new_line = false -ij_scala_multiline_string_insert_margin_on_enter = true -ij_scala_multiline_string_margin_char = | -ij_scala_multiline_string_margin_indent = 2 -ij_scala_multiline_string_opening_quotes_on_new_line = true -ij_scala_multiline_string_process_margin_on_copy_paste = true -ij_scala_newline_after_annotations = false -ij_scala_not_continuation_indent_for_params = false -ij_scala_parameter_annotation_wrap = off -ij_scala_parentheses_expression_new_line_after_left_paren = false -ij_scala_parentheses_expression_right_paren_on_new_line = false -ij_scala_place_closure_parameters_on_new_line = false -ij_scala_place_self_type_on_new_line = true -ij_scala_prefer_parameters_wrap = false -ij_scala_preserve_space_after_method_declaration_name = false -ij_scala_reformat_on_compile = false -ij_scala_replace_case_arrow_with_unicode_char = false -ij_scala_replace_for_generator_arrow_with_unicode_char = false -ij_scala_replace_lambda_with_greek_letter = false -ij_scala_replace_map_arrow_with_unicode_char = false -ij_scala_scalafmt_reformat_on_files_save = false -ij_scala_scalafmt_show_invalid_code_warnings = true -ij_scala_scalafmt_use_intellij_formatter_for_range_format = true -ij_scala_sd_align_exception_comments = true -ij_scala_sd_align_other_tags_comments = true -ij_scala_sd_align_parameters_comments = true -ij_scala_sd_align_return_comments = true -ij_scala_sd_blank_line_after_parameters_comments = false -ij_scala_sd_blank_line_after_return_comments = false -ij_scala_sd_blank_line_before_parameters = false -ij_scala_sd_blank_line_before_tags = true -ij_scala_sd_blank_line_between_parameters = false -ij_scala_sd_keep_blank_lines_between_tags = false -ij_scala_sd_preserve_spaces_in_tags = false -ij_scala_space_after_comma = true -ij_scala_space_after_for_semicolon = true -ij_scala_space_after_modifiers_constructor = false -ij_scala_space_after_type_colon = true -ij_scala_space_before_brace_method_call = true -ij_scala_space_before_class_left_brace = true -ij_scala_space_before_infix_like_method_parentheses = false -ij_scala_space_before_infix_method_call_parentheses = false -ij_scala_space_before_infix_operator_like_method_call_parentheses = true -ij_scala_space_before_method_call_parentheses = false -ij_scala_space_before_method_left_brace = true -ij_scala_space_before_method_parentheses = false -ij_scala_space_before_type_colon = false -ij_scala_space_before_type_parameter_in_def_list = false -ij_scala_space_before_type_parameter_leading_context_bound_colon = false -ij_scala_space_before_type_parameter_leading_context_bound_colon_hk = true -ij_scala_space_before_type_parameter_list = false -ij_scala_space_before_type_parameter_rest_context_bound_colons = true -ij_scala_space_inside_closure_braces = true -ij_scala_space_inside_self_type_braces = true -ij_scala_space_within_empty_method_call_parentheses = false -ij_scala_spaces_around_at_in_patterns = false -ij_scala_spaces_in_imports = false -ij_scala_spaces_in_one_line_blocks = false -ij_scala_spaces_within_brackets = false -ij_scala_spaces_within_for_parentheses = false -ij_scala_spaces_within_if_parentheses = false -ij_scala_spaces_within_method_call_parentheses = false -ij_scala_spaces_within_method_parentheses = false -ij_scala_spaces_within_parentheses = false -ij_scala_spaces_within_while_parentheses = false -ij_scala_special_else_if_treatment = true -ij_scala_trailing_comma_arg_list_enabled = true -ij_scala_trailing_comma_import_selector_enabled = false -ij_scala_trailing_comma_mode = trailing_comma_keep -ij_scala_trailing_comma_params_enabled = true -ij_scala_trailing_comma_pattern_arg_list_enabled = false -ij_scala_trailing_comma_tuple_enabled = false -ij_scala_trailing_comma_tuple_type_enabled = false -ij_scala_trailing_comma_type_params_enabled = false -ij_scala_try_brace_force = never -ij_scala_type_annotation_exclude_constant = true -ij_scala_type_annotation_exclude_in_dialect_sources = true -ij_scala_type_annotation_exclude_in_test_sources = false -ij_scala_type_annotation_exclude_member_of_anonymous_class = false -ij_scala_type_annotation_exclude_member_of_private_class = false -ij_scala_type_annotation_exclude_when_type_is_stable = true -ij_scala_type_annotation_function_parameter = false -ij_scala_type_annotation_implicit_modifier = true -ij_scala_type_annotation_local_definition = false -ij_scala_type_annotation_private_member = false -ij_scala_type_annotation_protected_member = true -ij_scala_type_annotation_public_member = true -ij_scala_type_annotation_structural_type = true -ij_scala_type_annotation_underscore_parameter = false -ij_scala_type_annotation_unit_type = true -ij_scala_use_alternate_continuation_indent_for_params = false -ij_scala_use_scaladoc2_formatting = false -ij_scala_variable_annotation_wrap = off -ij_scala_while_brace_force = never -ij_scala_while_on_new_line = false -ij_scala_wrap_before_with_keyword = false -ij_scala_wrap_first_method_in_call_chain = false -ij_scala_wrap_long_lines = false - -[*.tikz] -ij_latex_keep_first_column_comment = true -ij_latex_keep_indents_on_empty_lines = false -ij_latex_line_comment_add_space = false -ij_latex_line_comment_at_first_column = true -ij_latex_wrap_long_lines = false - -[*.ttl] -ij_turtle_keep_indents_on_empty_lines = false -ij_turtle_keep_line_breaks = true -ij_turtle_space_after_comma = true -ij_turtle_space_after_for_semicolon = true -ij_turtle_space_before_comma = false -ij_turtle_space_before_for_semicolon = false -ij_turtle_wrap_long_lines = false - -[.editorconfig] -ij_editorconfig_align_group_field_declarations = false -ij_editorconfig_space_after_colon = false -ij_editorconfig_space_after_comma = true -ij_editorconfig_space_before_colon = false -ij_editorconfig_space_before_comma = false -ij_editorconfig_spaces_around_assignment_operators = true - -[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] -ij_xml_align_attributes = true -ij_xml_align_text = false -ij_xml_attribute_wrap = normal -ij_xml_block_comment_at_first_column = true -ij_xml_keep_blank_lines = 2 -ij_xml_keep_indents_on_empty_lines = false -ij_xml_keep_line_breaks = true -ij_xml_keep_line_breaks_in_text = true -ij_xml_keep_whitespaces = false -ij_xml_keep_whitespaces_around_cdata = preserve -ij_xml_keep_whitespaces_inside_cdata = false -ij_xml_line_comment_at_first_column = true -ij_xml_space_after_tag_name = false -ij_xml_space_around_equals_in_attribute = false -ij_xml_space_inside_empty_tag = false -ij_xml_text_wrap = normal - -[{*.ats,*.ts}] -ij_continuation_indent_size = 4 -ij_typescript_align_imports = false -ij_typescript_align_multiline_array_initializer_expression = false -ij_typescript_align_multiline_binary_operation = false -ij_typescript_align_multiline_chained_methods = false -ij_typescript_align_multiline_extends_list = false -ij_typescript_align_multiline_for = true -ij_typescript_align_multiline_parameters = true -ij_typescript_align_multiline_parameters_in_calls = false -ij_typescript_align_multiline_ternary_operation = false -ij_typescript_align_object_properties = 0 -ij_typescript_align_union_types = false -ij_typescript_align_var_statements = 0 -ij_typescript_array_initializer_new_line_after_left_brace = false -ij_typescript_array_initializer_right_brace_on_new_line = false -ij_typescript_array_initializer_wrap = off -ij_typescript_assignment_wrap = off -ij_typescript_binary_operation_sign_on_next_line = false -ij_typescript_binary_operation_wrap = off -ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** -ij_typescript_blank_lines_after_imports = 1 -ij_typescript_blank_lines_around_class = 1 -ij_typescript_blank_lines_around_field = 0 -ij_typescript_blank_lines_around_field_in_interface = 0 -ij_typescript_blank_lines_around_function = 1 -ij_typescript_blank_lines_around_method = 1 -ij_typescript_blank_lines_around_method_in_interface = 1 -ij_typescript_block_brace_style = end_of_line -ij_typescript_call_parameters_new_line_after_left_paren = false -ij_typescript_call_parameters_right_paren_on_new_line = false -ij_typescript_call_parameters_wrap = off -ij_typescript_catch_on_new_line = false -ij_typescript_chained_call_dot_on_new_line = true -ij_typescript_class_brace_style = end_of_line -ij_typescript_comma_on_new_line = false -ij_typescript_do_while_brace_force = never -ij_typescript_else_on_new_line = false -ij_typescript_enforce_trailing_comma = keep -ij_typescript_extends_keyword_wrap = off -ij_typescript_extends_list_wrap = off -ij_typescript_field_prefix = _ -ij_typescript_file_name_style = relaxed -ij_typescript_finally_on_new_line = false -ij_typescript_for_brace_force = never -ij_typescript_for_statement_new_line_after_left_paren = false -ij_typescript_for_statement_right_paren_on_new_line = false -ij_typescript_for_statement_wrap = off -ij_typescript_force_quote_style = false -ij_typescript_force_semicolon_style = false -ij_typescript_function_expression_brace_style = end_of_line -ij_typescript_if_brace_force = never -ij_typescript_import_merge_members = global -ij_typescript_import_prefer_absolute_path = global -ij_typescript_import_sort_members = true -ij_typescript_import_sort_module_name = false -ij_typescript_import_use_node_resolution = true -ij_typescript_imports_wrap = on_every_item -ij_typescript_indent_case_from_switch = true -ij_typescript_indent_chained_calls = true -ij_typescript_indent_package_children = 0 -ij_typescript_jsdoc_include_types = false -ij_typescript_jsx_attribute_value = braces -ij_typescript_keep_blank_lines_in_code = 2 -ij_typescript_keep_first_column_comment = true -ij_typescript_keep_indents_on_empty_lines = false -ij_typescript_keep_line_breaks = true -ij_typescript_keep_simple_blocks_in_one_line = false -ij_typescript_keep_simple_methods_in_one_line = false -ij_typescript_line_comment_add_space = true -ij_typescript_line_comment_at_first_column = false -ij_typescript_method_brace_style = end_of_line -ij_typescript_method_call_chain_wrap = off -ij_typescript_method_parameters_new_line_after_left_paren = false -ij_typescript_method_parameters_right_paren_on_new_line = false -ij_typescript_method_parameters_wrap = off -ij_typescript_object_literal_wrap = on_every_item -ij_typescript_parentheses_expression_new_line_after_left_paren = false -ij_typescript_parentheses_expression_right_paren_on_new_line = false -ij_typescript_place_assignment_sign_on_next_line = false -ij_typescript_prefer_as_type_cast = false -ij_typescript_prefer_explicit_types_function_expression_returns = false -ij_typescript_prefer_explicit_types_function_returns = false -ij_typescript_prefer_explicit_types_vars_fields = false -ij_typescript_prefer_parameters_wrap = false -ij_typescript_reformat_c_style_comments = false -ij_typescript_space_after_colon = true -ij_typescript_space_after_comma = true -ij_typescript_space_after_dots_in_rest_parameter = false -ij_typescript_space_after_generator_mult = true -ij_typescript_space_after_property_colon = true -ij_typescript_space_after_quest = true -ij_typescript_space_after_type_colon = true -ij_typescript_space_after_unary_not = false -ij_typescript_space_before_async_arrow_lparen = true -ij_typescript_space_before_catch_keyword = true -ij_typescript_space_before_catch_left_brace = true -ij_typescript_space_before_catch_parentheses = true -ij_typescript_space_before_class_lbrace = true -ij_typescript_space_before_class_left_brace = true -ij_typescript_space_before_colon = true -ij_typescript_space_before_comma = false -ij_typescript_space_before_do_left_brace = true -ij_typescript_space_before_else_keyword = true -ij_typescript_space_before_else_left_brace = true -ij_typescript_space_before_finally_keyword = true -ij_typescript_space_before_finally_left_brace = true -ij_typescript_space_before_for_left_brace = true -ij_typescript_space_before_for_parentheses = true -ij_typescript_space_before_for_semicolon = false -ij_typescript_space_before_function_left_parenth = true -ij_typescript_space_before_generator_mult = false -ij_typescript_space_before_if_left_brace = true -ij_typescript_space_before_if_parentheses = true -ij_typescript_space_before_method_call_parentheses = false -ij_typescript_space_before_method_left_brace = true -ij_typescript_space_before_method_parentheses = false -ij_typescript_space_before_property_colon = false -ij_typescript_space_before_quest = true -ij_typescript_space_before_switch_left_brace = true -ij_typescript_space_before_switch_parentheses = true -ij_typescript_space_before_try_left_brace = true -ij_typescript_space_before_type_colon = false -ij_typescript_space_before_unary_not = false -ij_typescript_space_before_while_keyword = true -ij_typescript_space_before_while_left_brace = true -ij_typescript_space_before_while_parentheses = true -ij_typescript_spaces_around_additive_operators = true -ij_typescript_spaces_around_arrow_function_operator = true -ij_typescript_spaces_around_assignment_operators = true -ij_typescript_spaces_around_bitwise_operators = true -ij_typescript_spaces_around_equality_operators = true -ij_typescript_spaces_around_logical_operators = true -ij_typescript_spaces_around_multiplicative_operators = true -ij_typescript_spaces_around_relational_operators = true -ij_typescript_spaces_around_shift_operators = true -ij_typescript_spaces_around_unary_operator = false -ij_typescript_spaces_within_array_initializer_brackets = false -ij_typescript_spaces_within_brackets = false -ij_typescript_spaces_within_catch_parentheses = false -ij_typescript_spaces_within_for_parentheses = false -ij_typescript_spaces_within_if_parentheses = false -ij_typescript_spaces_within_imports = false -ij_typescript_spaces_within_interpolation_expressions = false -ij_typescript_spaces_within_method_call_parentheses = false -ij_typescript_spaces_within_method_parentheses = false -ij_typescript_spaces_within_object_literal_braces = false -ij_typescript_spaces_within_object_type_braces = true -ij_typescript_spaces_within_parentheses = false -ij_typescript_spaces_within_switch_parentheses = false -ij_typescript_spaces_within_type_assertion = false -ij_typescript_spaces_within_union_types = true -ij_typescript_spaces_within_while_parentheses = false -ij_typescript_special_else_if_treatment = true -ij_typescript_ternary_operation_signs_on_next_line = false -ij_typescript_ternary_operation_wrap = off -ij_typescript_union_types_wrap = on_every_item -ij_typescript_use_chained_calls_group_indents = false -ij_typescript_use_double_quotes = true -ij_typescript_use_explicit_js_extension = global -ij_typescript_use_path_mapping = always -ij_typescript_use_public_modifier = false -ij_typescript_use_semicolon_after_statement = true -ij_typescript_var_declaration_wrap = normal -ij_typescript_while_brace_force = never -ij_typescript_while_on_new_line = false -ij_typescript_wrap_comments = false - -[{*.bash,*.sh,*.zsh}] -indent_size = 2 -tab_width = 2 -ij_shell_binary_ops_start_line = false -ij_shell_keep_column_alignment_padding = false -ij_shell_minify_program = false -ij_shell_redirect_followed_by_space = false -ij_shell_switch_cases_indented = false - -[{*.cjs,*.js}] -ij_continuation_indent_size = 4 -ij_javascript_align_imports = false -ij_javascript_align_multiline_array_initializer_expression = false -ij_javascript_align_multiline_binary_operation = false -ij_javascript_align_multiline_chained_methods = false -ij_javascript_align_multiline_extends_list = false -ij_javascript_align_multiline_for = true -ij_javascript_align_multiline_parameters = true -ij_javascript_align_multiline_parameters_in_calls = false -ij_javascript_align_multiline_ternary_operation = false -ij_javascript_align_object_properties = 0 -ij_javascript_align_union_types = false -ij_javascript_align_var_statements = 0 -ij_javascript_array_initializer_new_line_after_left_brace = false -ij_javascript_array_initializer_right_brace_on_new_line = false -ij_javascript_array_initializer_wrap = off -ij_javascript_assignment_wrap = off -ij_javascript_binary_operation_sign_on_next_line = false -ij_javascript_binary_operation_wrap = off -ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** -ij_javascript_blank_lines_after_imports = 1 -ij_javascript_blank_lines_around_class = 1 -ij_javascript_blank_lines_around_field = 0 -ij_javascript_blank_lines_around_function = 1 -ij_javascript_blank_lines_around_method = 1 -ij_javascript_block_brace_style = end_of_line -ij_javascript_call_parameters_new_line_after_left_paren = false -ij_javascript_call_parameters_right_paren_on_new_line = false -ij_javascript_call_parameters_wrap = off -ij_javascript_catch_on_new_line = false -ij_javascript_chained_call_dot_on_new_line = true -ij_javascript_class_brace_style = end_of_line -ij_javascript_comma_on_new_line = false -ij_javascript_do_while_brace_force = never -ij_javascript_else_on_new_line = false -ij_javascript_enforce_trailing_comma = keep -ij_javascript_extends_keyword_wrap = off -ij_javascript_extends_list_wrap = off -ij_javascript_field_prefix = _ -ij_javascript_file_name_style = relaxed -ij_javascript_finally_on_new_line = false -ij_javascript_for_brace_force = never -ij_javascript_for_statement_new_line_after_left_paren = false -ij_javascript_for_statement_right_paren_on_new_line = false -ij_javascript_for_statement_wrap = off -ij_javascript_force_quote_style = false -ij_javascript_force_semicolon_style = false -ij_javascript_function_expression_brace_style = end_of_line -ij_javascript_if_brace_force = never -ij_javascript_import_merge_members = global -ij_javascript_import_prefer_absolute_path = global -ij_javascript_import_sort_members = true -ij_javascript_import_sort_module_name = false -ij_javascript_import_use_node_resolution = true -ij_javascript_imports_wrap = on_every_item -ij_javascript_indent_case_from_switch = true -ij_javascript_indent_chained_calls = true -ij_javascript_indent_package_children = 0 -ij_javascript_jsx_attribute_value = braces -ij_javascript_keep_blank_lines_in_code = 2 -ij_javascript_keep_first_column_comment = true -ij_javascript_keep_indents_on_empty_lines = false -ij_javascript_keep_line_breaks = true -ij_javascript_keep_simple_blocks_in_one_line = false -ij_javascript_keep_simple_methods_in_one_line = false -ij_javascript_line_comment_add_space = true -ij_javascript_line_comment_at_first_column = false -ij_javascript_method_brace_style = end_of_line -ij_javascript_method_call_chain_wrap = off -ij_javascript_method_parameters_new_line_after_left_paren = false -ij_javascript_method_parameters_right_paren_on_new_line = false -ij_javascript_method_parameters_wrap = off -ij_javascript_object_literal_wrap = on_every_item -ij_javascript_parentheses_expression_new_line_after_left_paren = false -ij_javascript_parentheses_expression_right_paren_on_new_line = false -ij_javascript_place_assignment_sign_on_next_line = false -ij_javascript_prefer_as_type_cast = false -ij_javascript_prefer_explicit_types_function_expression_returns = false -ij_javascript_prefer_explicit_types_function_returns = false -ij_javascript_prefer_explicit_types_vars_fields = false -ij_javascript_prefer_parameters_wrap = false -ij_javascript_reformat_c_style_comments = false -ij_javascript_space_after_colon = true -ij_javascript_space_after_comma = true -ij_javascript_space_after_dots_in_rest_parameter = false -ij_javascript_space_after_generator_mult = true -ij_javascript_space_after_property_colon = true -ij_javascript_space_after_quest = true -ij_javascript_space_after_type_colon = true -ij_javascript_space_after_unary_not = false -ij_javascript_space_before_async_arrow_lparen = true -ij_javascript_space_before_catch_keyword = true -ij_javascript_space_before_catch_left_brace = true -ij_javascript_space_before_catch_parentheses = true -ij_javascript_space_before_class_lbrace = true -ij_javascript_space_before_class_left_brace = true -ij_javascript_space_before_colon = true -ij_javascript_space_before_comma = false -ij_javascript_space_before_do_left_brace = true -ij_javascript_space_before_else_keyword = true -ij_javascript_space_before_else_left_brace = true -ij_javascript_space_before_finally_keyword = true -ij_javascript_space_before_finally_left_brace = true -ij_javascript_space_before_for_left_brace = true -ij_javascript_space_before_for_parentheses = true -ij_javascript_space_before_for_semicolon = false -ij_javascript_space_before_function_left_parenth = true -ij_javascript_space_before_generator_mult = false -ij_javascript_space_before_if_left_brace = true -ij_javascript_space_before_if_parentheses = true -ij_javascript_space_before_method_call_parentheses = false -ij_javascript_space_before_method_left_brace = true -ij_javascript_space_before_method_parentheses = false -ij_javascript_space_before_property_colon = false -ij_javascript_space_before_quest = true -ij_javascript_space_before_switch_left_brace = true -ij_javascript_space_before_switch_parentheses = true -ij_javascript_space_before_try_left_brace = true -ij_javascript_space_before_type_colon = false -ij_javascript_space_before_unary_not = false -ij_javascript_space_before_while_keyword = true -ij_javascript_space_before_while_left_brace = true -ij_javascript_space_before_while_parentheses = true -ij_javascript_spaces_around_additive_operators = true -ij_javascript_spaces_around_arrow_function_operator = true -ij_javascript_spaces_around_assignment_operators = true -ij_javascript_spaces_around_bitwise_operators = true -ij_javascript_spaces_around_equality_operators = true -ij_javascript_spaces_around_logical_operators = true -ij_javascript_spaces_around_multiplicative_operators = true -ij_javascript_spaces_around_relational_operators = true -ij_javascript_spaces_around_shift_operators = true -ij_javascript_spaces_around_unary_operator = false -ij_javascript_spaces_within_array_initializer_brackets = false -ij_javascript_spaces_within_brackets = false -ij_javascript_spaces_within_catch_parentheses = false -ij_javascript_spaces_within_for_parentheses = false -ij_javascript_spaces_within_if_parentheses = false -ij_javascript_spaces_within_imports = false -ij_javascript_spaces_within_interpolation_expressions = false -ij_javascript_spaces_within_method_call_parentheses = false -ij_javascript_spaces_within_method_parentheses = false -ij_javascript_spaces_within_object_literal_braces = false -ij_javascript_spaces_within_object_type_braces = true -ij_javascript_spaces_within_parentheses = false -ij_javascript_spaces_within_switch_parentheses = false -ij_javascript_spaces_within_type_assertion = false -ij_javascript_spaces_within_union_types = true -ij_javascript_spaces_within_while_parentheses = false -ij_javascript_special_else_if_treatment = true -ij_javascript_ternary_operation_signs_on_next_line = false -ij_javascript_ternary_operation_wrap = off -ij_javascript_union_types_wrap = on_every_item -ij_javascript_use_chained_calls_group_indents = false -ij_javascript_use_double_quotes = true -ij_javascript_use_explicit_js_extension = global -ij_javascript_use_path_mapping = always -ij_javascript_use_public_modifier = false -ij_javascript_use_semicolon_after_statement = true -ij_javascript_var_declaration_wrap = normal -ij_javascript_while_brace_force = never -ij_javascript_while_on_new_line = false -ij_javascript_wrap_comments = false - -[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] -ij_continuation_indent_size = 4 -ij_php_align_assignments = false -ij_php_align_class_constants = false -ij_php_align_group_field_declarations = false -ij_php_align_inline_comments = false -ij_php_align_key_value_pairs = false -ij_php_align_multiline_array_initializer_expression = false -ij_php_align_multiline_binary_operation = false -ij_php_align_multiline_chained_methods = false -ij_php_align_multiline_extends_list = false -ij_php_align_multiline_for = true -ij_php_align_multiline_parameters = true -ij_php_align_multiline_parameters_in_calls = false -ij_php_align_multiline_ternary_operation = false -ij_php_align_phpdoc_comments = false -ij_php_align_phpdoc_param_names = false -ij_php_anonymous_brace_style = end_of_line -ij_php_api_weight = 28 -ij_php_array_initializer_new_line_after_left_brace = false -ij_php_array_initializer_right_brace_on_new_line = false -ij_php_array_initializer_wrap = off -ij_php_assignment_wrap = off -ij_php_author_weight = 28 -ij_php_binary_operation_sign_on_next_line = false -ij_php_binary_operation_wrap = off -ij_php_blank_lines_after_class_header = 0 -ij_php_blank_lines_after_function = 1 -ij_php_blank_lines_after_imports = 1 -ij_php_blank_lines_after_opening_tag = 0 -ij_php_blank_lines_after_package = 0 -ij_php_blank_lines_around_class = 1 -ij_php_blank_lines_around_constants = 0 -ij_php_blank_lines_around_field = 0 -ij_php_blank_lines_around_method = 1 -ij_php_blank_lines_before_class_end = 0 -ij_php_blank_lines_before_imports = 1 -ij_php_blank_lines_before_method_body = 0 -ij_php_blank_lines_before_package = 1 -ij_php_blank_lines_before_return_statement = 0 -ij_php_blank_lines_between_imports = 0 -ij_php_block_brace_style = end_of_line -ij_php_call_parameters_new_line_after_left_paren = false -ij_php_call_parameters_right_paren_on_new_line = false -ij_php_call_parameters_wrap = off -ij_php_catch_on_new_line = false -ij_php_category_weight = 28 -ij_php_class_brace_style = next_line -ij_php_comma_after_last_array_element = false -ij_php_concat_spaces = true -ij_php_copyright_weight = 28 -ij_php_deprecated_weight = 28 -ij_php_do_while_brace_force = never -ij_php_else_if_style = as_is -ij_php_else_on_new_line = false -ij_php_example_weight = 28 -ij_php_extends_keyword_wrap = off -ij_php_extends_list_wrap = off -ij_php_fields_default_visibility = private -ij_php_filesource_weight = 28 -ij_php_finally_on_new_line = false -ij_php_for_brace_force = never -ij_php_for_statement_new_line_after_left_paren = false -ij_php_for_statement_right_paren_on_new_line = false -ij_php_for_statement_wrap = off -ij_php_force_short_declaration_array_style = false -ij_php_global_weight = 28 -ij_php_group_use_wrap = on_every_item -ij_php_if_brace_force = never -ij_php_if_lparen_on_next_line = false -ij_php_if_rparen_on_next_line = false -ij_php_ignore_weight = 28 -ij_php_import_sorting = alphabetic -ij_php_indent_break_from_case = true -ij_php_indent_case_from_switch = true -ij_php_indent_code_in_php_tags = false -ij_php_internal_weight = 28 -ij_php_keep_blank_lines_after_lbrace = 2 -ij_php_keep_blank_lines_before_right_brace = 2 -ij_php_keep_blank_lines_in_code = 2 -ij_php_keep_blank_lines_in_declarations = 2 -ij_php_keep_control_statement_in_one_line = true -ij_php_keep_first_column_comment = true -ij_php_keep_indents_on_empty_lines = false -ij_php_keep_line_breaks = true -ij_php_keep_rparen_and_lbrace_on_one_line = false -ij_php_keep_simple_methods_in_one_line = false -ij_php_lambda_brace_style = end_of_line -ij_php_license_weight = 28 -ij_php_line_comment_add_space = false -ij_php_line_comment_at_first_column = true -ij_php_link_weight = 28 -ij_php_lower_case_boolean_const = false -ij_php_lower_case_keywords = true -ij_php_lower_case_null_const = false -ij_php_method_brace_style = next_line -ij_php_method_call_chain_wrap = off -ij_php_method_parameters_new_line_after_left_paren = false -ij_php_method_parameters_right_paren_on_new_line = false -ij_php_method_parameters_wrap = off -ij_php_method_weight = 28 -ij_php_modifier_list_wrap = false -ij_php_multiline_chained_calls_semicolon_on_new_line = false -ij_php_namespace_brace_style = 1 -ij_php_new_line_after_php_opening_tag = false -ij_php_null_type_position = in_the_end -ij_php_package_weight = 28 -ij_php_param_weight = 0 -ij_php_parentheses_expression_new_line_after_left_paren = false -ij_php_parentheses_expression_right_paren_on_new_line = false -ij_php_phpdoc_blank_line_before_tags = false -ij_php_phpdoc_blank_lines_around_parameters = false -ij_php_phpdoc_keep_blank_lines = true -ij_php_phpdoc_param_spaces_between_name_and_description = 1 -ij_php_phpdoc_param_spaces_between_tag_and_type = 1 -ij_php_phpdoc_param_spaces_between_type_and_name = 1 -ij_php_phpdoc_use_fqcn = false -ij_php_phpdoc_wrap_long_lines = false -ij_php_place_assignment_sign_on_next_line = false -ij_php_place_parens_for_constructor = 0 -ij_php_property_read_weight = 28 -ij_php_property_weight = 28 -ij_php_property_write_weight = 28 -ij_php_return_type_on_new_line = false -ij_php_return_weight = 1 -ij_php_see_weight = 28 -ij_php_since_weight = 28 -ij_php_sort_phpdoc_elements = true -ij_php_space_after_colon = true -ij_php_space_after_colon_in_return_type = true -ij_php_space_after_comma = true -ij_php_space_after_for_semicolon = true -ij_php_space_after_quest = true -ij_php_space_after_type_cast = false -ij_php_space_after_unary_not = false -ij_php_space_before_array_initializer_left_brace = false -ij_php_space_before_catch_keyword = true -ij_php_space_before_catch_left_brace = true -ij_php_space_before_catch_parentheses = true -ij_php_space_before_class_left_brace = true -ij_php_space_before_closure_left_parenthesis = true -ij_php_space_before_colon = true -ij_php_space_before_colon_in_return_type = false -ij_php_space_before_comma = false -ij_php_space_before_do_left_brace = true -ij_php_space_before_else_keyword = true -ij_php_space_before_else_left_brace = true -ij_php_space_before_finally_keyword = true -ij_php_space_before_finally_left_brace = true -ij_php_space_before_for_left_brace = true -ij_php_space_before_for_parentheses = true -ij_php_space_before_for_semicolon = false -ij_php_space_before_if_left_brace = true -ij_php_space_before_if_parentheses = true -ij_php_space_before_method_call_parentheses = false -ij_php_space_before_method_left_brace = true -ij_php_space_before_method_parentheses = false -ij_php_space_before_quest = true -ij_php_space_before_short_closure_left_parenthesis = false -ij_php_space_before_switch_left_brace = true -ij_php_space_before_switch_parentheses = true -ij_php_space_before_try_left_brace = true -ij_php_space_before_unary_not = false -ij_php_space_before_while_keyword = true -ij_php_space_before_while_left_brace = true -ij_php_space_before_while_parentheses = true -ij_php_space_between_ternary_quest_and_colon = false -ij_php_spaces_around_additive_operators = true -ij_php_spaces_around_arrow = false -ij_php_spaces_around_assignment_in_declare = false -ij_php_spaces_around_assignment_operators = true -ij_php_spaces_around_bitwise_operators = true -ij_php_spaces_around_equality_operators = true -ij_php_spaces_around_logical_operators = true -ij_php_spaces_around_multiplicative_operators = true -ij_php_spaces_around_null_coalesce_operator = true -ij_php_spaces_around_relational_operators = true -ij_php_spaces_around_shift_operators = true -ij_php_spaces_around_unary_operator = false -ij_php_spaces_around_var_within_brackets = false -ij_php_spaces_within_array_initializer_braces = false -ij_php_spaces_within_brackets = false -ij_php_spaces_within_catch_parentheses = false -ij_php_spaces_within_for_parentheses = false -ij_php_spaces_within_if_parentheses = false -ij_php_spaces_within_method_call_parentheses = false -ij_php_spaces_within_method_parentheses = false -ij_php_spaces_within_parentheses = false -ij_php_spaces_within_short_echo_tags = true -ij_php_spaces_within_switch_parentheses = false -ij_php_spaces_within_while_parentheses = false -ij_php_special_else_if_treatment = false -ij_php_subpackage_weight = 28 -ij_php_ternary_operation_signs_on_next_line = false -ij_php_ternary_operation_wrap = off -ij_php_throws_weight = 2 -ij_php_todo_weight = 28 -ij_php_unknown_tag_weight = 28 -ij_php_upper_case_boolean_const = false -ij_php_upper_case_null_const = false -ij_php_uses_weight = 28 -ij_php_var_weight = 28 -ij_php_variable_naming_style = mixed -ij_php_version_weight = 28 -ij_php_while_brace_force = never -ij_php_while_on_new_line = false - -[{*.dockerapp,*.yaml,*.yml}] -indent_size = 2 -ij_yaml_keep_indents_on_empty_lines = false -ij_yaml_keep_line_breaks = true - -[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}] -indent_size = 2 -ij_json_keep_blank_lines_in_code = 0 -ij_json_keep_indents_on_empty_lines = false -ij_json_keep_line_breaks = true -ij_json_space_after_colon = true -ij_json_space_after_comma = true -ij_json_space_before_colon = true -ij_json_space_before_comma = false -ij_json_spaces_within_braces = false -ij_json_spaces_within_brackets = false -ij_json_wrap_long_lines = false - -[{*.htm,*.html,*.sht,*.shtm,*.shtml}] -ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 -ij_html_align_attributes = true -ij_html_align_text = false -ij_html_attribute_wrap = normal -ij_html_block_comment_at_first_column = true -ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p -ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot -ij_html_enforce_quotes = false -ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var -ij_html_keep_blank_lines = 2 -ij_html_keep_indents_on_empty_lines = false -ij_html_keep_line_breaks = true -ij_html_keep_line_breaks_in_text = true -ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span,pre,textarea -ij_html_line_comment_at_first_column = true -ij_html_new_line_after_last_attribute = never -ij_html_new_line_before_first_attribute = never -ij_html_quote_style = double -ij_html_remove_new_line_before_tags = br -ij_html_space_after_tag_name = false -ij_html_space_around_equality_in_attribute = false -ij_html_space_inside_empty_tag = false -ij_html_text_wrap = normal diff --git a/.scalafmt.conf b/.scalafmt.conf index e94dd85083..bf360eab5b 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,6 +1,6 @@ version = "2.7.5" maxColumn = 120 -align.preset = most +align.preset = some align.multiline = false continuationIndent.defnSite = 2 assumeStandardLibraryStripMargin = true diff --git a/webapi/src/main/scala/org/knora/webapi/LanguageCodes.scala b/webapi/src/main/scala/org/knora/webapi/LanguageCodes.scala index ba38134e6b..fdd58b9005 100644 --- a/webapi/src/main/scala/org/knora/webapi/LanguageCodes.scala +++ b/webapi/src/main/scala/org/knora/webapi/LanguageCodes.scala @@ -20,8 +20,8 @@ package org.knora.webapi /** - * Constants for language codes. - */ + * Constants for language codes. + */ object LanguageCodes { val DE: String = "de" val EN: String = "en" diff --git a/webapi/src/main/scala/org/knora/webapi/OntologySchema.scala b/webapi/src/main/scala/org/knora/webapi/OntologySchema.scala index 388d6d4b47..bda12465bd 100644 --- a/webapi/src/main/scala/org/knora/webapi/OntologySchema.scala +++ b/webapi/src/main/scala/org/knora/webapi/OntologySchema.scala @@ -20,131 +20,127 @@ package org.knora.webapi /** - * Indicates the schema that a Knora ontology or ontology entity conforms to. - */ + * Indicates the schema that a Knora ontology or ontology entity conforms to. + */ sealed trait OntologySchema /** - * The schema of Knora ontologies and entities that are used in the triplestore. - */ + * The schema of Knora ontologies and entities that are used in the triplestore. + */ case object InternalSchema extends OntologySchema /** - * The schema of Knora ontologies and entities that are used in API v2. - */ + * The schema of Knora ontologies and entities that are used in API v2. + */ sealed trait ApiV2Schema extends OntologySchema /** - * The simple schema for representing Knora ontologies and entities. This schema represents values as literals - * when possible. - */ + * The simple schema for representing Knora ontologies and entities. This schema represents values as literals + * when possible. + */ case object ApiV2Simple extends ApiV2Schema /** - * The default (or complex) schema for representing Knora ontologies and entities. This - * schema always represents values as objects. - */ + * The default (or complex) schema for representing Knora ontologies and entities. This + * schema always represents values as objects. + */ case object ApiV2Complex extends ApiV2Schema /** - * A trait representing options that can be submitted to configure an ontology schema. - */ + * A trait representing options that can be submitted to configure an ontology schema. + */ sealed trait SchemaOption /** - * A trait representing options that affect the rendering of markup when text values are returned. - */ + * A trait representing options that affect the rendering of markup when text values are returned. + */ sealed trait MarkupRendering extends SchemaOption /** - * Indicates that markup should be rendered as XML when text values are returned. - */ + * Indicates that markup should be rendered as XML when text values are returned. + */ case object MarkupAsXml extends MarkupRendering /** - * Indicates that markup should not be returned with text values, because it will be requested - * separately as standoff. - */ + * Indicates that markup should not be returned with text values, because it will be requested + * separately as standoff. + */ case object MarkupAsStandoff extends MarkupRendering /** - * Indicates that no markup should be returned with text values. Used only internally. - */ + * Indicates that no markup should be returned with text values. Used only internally. + */ case object NoMarkup extends MarkupRendering /** - * A trait representing options that affect the format of JSON-LD responses. - */ + * A trait representing options that affect the format of JSON-LD responses. + */ sealed trait JsonLDRendering extends SchemaOption /** - * Indicates that flat JSON-LD should be returned, i.e. objects with IRIs should be referenced by IRI - * rather than nested. Blank nodes will still be nested in any case. - */ + * Indicates that flat JSON-LD should be returned, i.e. objects with IRIs should be referenced by IRI + * rather than nested. Blank nodes will still be nested in any case. + */ case object FlatJsonLD extends JsonLDRendering /** - * Indicates that hierarchical JSON-LD should be returned, i.e. objects with IRIs should be nested when - * possible, rather than referenced by IRI. - */ + * Indicates that hierarchical JSON-LD should be returned, i.e. objects with IRIs should be nested when + * possible, rather than referenced by IRI. + */ case object HierarchicalJsonLD extends JsonLDRendering /** - * Utility functions for working with schema options. - */ + * Utility functions for working with schema options. + */ object SchemaOptions { /** - * A set of schema options for querying all standoff markup along with text values. - */ + * A set of schema options for querying all standoff markup along with text values. + */ val ForStandoffWithTextValues: Set[SchemaOption] = Set(MarkupAsXml) /** - * A set of schema options for querying standoff markup separately from text values. - */ + * A set of schema options for querying standoff markup separately from text values. + */ val ForStandoffSeparateFromTextValues: Set[SchemaOption] = Set(MarkupAsStandoff) /** - * Determines whether standoff should be queried when a text value is queried. - * - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @return `true` if standoff should be queried. - */ - def queryStandoffWithTextValues(targetSchema: ApiV2Schema, schemaOptions: Set[SchemaOption]): Boolean = { + * Determines whether standoff should be queried when a text value is queried. + * + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @return `true` if standoff should be queried. + */ + def queryStandoffWithTextValues(targetSchema: ApiV2Schema, schemaOptions: Set[SchemaOption]): Boolean = targetSchema == ApiV2Complex && !schemaOptions.contains(MarkupAsStandoff) - } /** - * Determines whether markup should be rendered as XML. - * - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @return `true` if markup should be rendered as XML. - */ - def renderMarkupAsXml(targetSchema: ApiV2Schema, schemaOptions: Set[SchemaOption]): Boolean = { + * Determines whether markup should be rendered as XML. + * + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @return `true` if markup should be rendered as XML. + */ + def renderMarkupAsXml(targetSchema: ApiV2Schema, schemaOptions: Set[SchemaOption]): Boolean = targetSchema == ApiV2Complex && !schemaOptions.contains(MarkupAsStandoff) - } /** - * Determines whether markup should be rendered as standoff, separately from text values. - * - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @return `true` if markup should be rendered as standoff. - */ - def renderMarkupAsStandoff(targetSchema: ApiV2Schema, schemaOptions: Set[SchemaOption]): Boolean = { + * Determines whether markup should be rendered as standoff, separately from text values. + * + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @return `true` if markup should be rendered as standoff. + */ + def renderMarkupAsStandoff(targetSchema: ApiV2Schema, schemaOptions: Set[SchemaOption]): Boolean = targetSchema == ApiV2Complex && schemaOptions.contains(MarkupAsStandoff) - } /** - * Determines whether flat JSON-LD should be returned, i.e. objects with IRIs should be referenced by IRI - * rather than nested. - * - * @param schemaOptions the schema options submitted with the request. - * @return `true` if flat JSON-LD should be returned. - */ - def returnFlatJsonLD(schemaOptions: Set[SchemaOption]): Boolean = { + * Determines whether flat JSON-LD should be returned, i.e. objects with IRIs should be referenced by IRI + * rather than nested. + * + * @param schemaOptions the schema options submitted with the request. + * @return `true` if flat JSON-LD should be returned. + */ + def returnFlatJsonLD(schemaOptions: Set[SchemaOption]): Boolean = schemaOptions.contains(FlatJsonLD) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/RdfMediaTypes.scala b/webapi/src/main/scala/org/knora/webapi/RdfMediaTypes.scala index 1aabb8e592..ca300265af 100644 --- a/webapi/src/main/scala/org/knora/webapi/RdfMediaTypes.scala +++ b/webapi/src/main/scala/org/knora/webapi/RdfMediaTypes.scala @@ -22,9 +22,9 @@ package org.knora.webapi import akka.http.scaladsl.model.{ContentType, HttpCharsets, MediaType, MediaTypes} /** - * Represents media types supported by the Knora API server for representing RDF data, and provides - * convenience methods for transforming media types. - */ + * Represents media types supported by the Knora API server for representing RDF data, and provides + * convenience methods for transforming media types. + */ object RdfMediaTypes { val `application/json`: MediaType.WithFixedCharset = MediaTypes.`application/json` @@ -63,8 +63,8 @@ object RdfMediaTypes { ) /** - * A map of MIME types (strings) to supported RDF media types. - */ + * A map of MIME types (strings) to supported RDF media types. + */ val registry: Map[String, MediaType.NonBinary] = Set( `application/json`, `application/ld+json`, @@ -77,29 +77,27 @@ object RdfMediaTypes { }.toMap /** - * Ensures that a media specifies the UTF-8 charset if necessary. - * - * @param mediaType a non-binary media type. - * @return the same media type, specifying the UTF-8 charset if necessary. - */ - def toUTF8ContentType(mediaType: MediaType.NonBinary): ContentType.NonBinary = { + * Ensures that a media specifies the UTF-8 charset if necessary. + * + * @param mediaType a non-binary media type. + * @return the same media type, specifying the UTF-8 charset if necessary. + */ + def toUTF8ContentType(mediaType: MediaType.NonBinary): ContentType.NonBinary = mediaType match { case withFixedCharset: MediaType.WithFixedCharset => withFixedCharset.toContentType case withOpenCharset: MediaType.WithOpenCharset => withOpenCharset.toContentType(HttpCharsets.`UTF-8`) } - } /** - * Converts less specific media types to more specific ones if necessary (e.g. specifying - * JSON-LD instead of JSON). - * - * @param mediaType a non-binary media type. - * @return the most specific similar media type that the Knora API server supports. - */ - def toMostSpecificMediaType(mediaType: MediaType.NonBinary): MediaType.NonBinary = { + * Converts less specific media types to more specific ones if necessary (e.g. specifying + * JSON-LD instead of JSON). + * + * @param mediaType a non-binary media type. + * @return the most specific similar media type that the Knora API server supports. + */ + def toMostSpecificMediaType(mediaType: MediaType.NonBinary): MediaType.NonBinary = mediaType match { case `application/json` => `application/ld+json` case other => other } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/annotation/ApiMayChange.scala b/webapi/src/main/scala/org/knora/webapi/annotation/ApiMayChange.scala index 180ca76a5a..180a3a58ba 100644 --- a/webapi/src/main/scala/org/knora/webapi/annotation/ApiMayChange.scala +++ b/webapi/src/main/scala/org/knora/webapi/annotation/ApiMayChange.scala @@ -20,24 +20,23 @@ package org.knora.webapi.annotation /** - * Creates the ApiMayChange annotation. - * - * Marks APIs that are meant to evolve towards becoming stable APIs, but are not stable APIs yet. - * - *

Evolving interfaces MAY change from one patch release to another (i.e. 2.4.10 to 2.4.11) - * without up-front notice. A best-effort approach is taken to not cause more breakage than really - * necessary, and usual deprecation techniques are utilised while evolving these APIs, however there - * is NO strong guarantee regarding the source or binary compatibility of APIs marked using this - * annotation. - * - *

It MAY also change when promoting the API to stable, for example such changes may include - * removal of deprecated methods that were introduced during the evolution and final refactoring - * that were deferred because they would have introduced to much breaking changes during the - * evolution phase. - * - *

Promoting the API to stable MAY happen in a patch release. - * - *

It is encouraged to document in ScalaDoc how exactly this API is expected to evolve. - * - */ + * Creates the ApiMayChange annotation. + * + * Marks APIs that are meant to evolve towards becoming stable APIs, but are not stable APIs yet. + * + *

Evolving interfaces MAY change from one patch release to another (i.e. 2.4.10 to 2.4.11) + * without up-front notice. A best-effort approach is taken to not cause more breakage than really + * necessary, and usual deprecation techniques are utilised while evolving these APIs, however there + * is NO strong guarantee regarding the source or binary compatibility of APIs marked using this + * annotation. + * + *

It MAY also change when promoting the API to stable, for example such changes may include + * removal of deprecated methods that were introduced during the evolution and final refactoring + * that were deferred because they would have introduced to much breaking changes during the + * evolution phase. + * + *

Promoting the API to stable MAY happen in a patch release. + * + *

It is encouraged to document in ScalaDoc how exactly this API is expected to evolve. + */ class ApiMayChange() extends scala.annotation.StaticAnnotation diff --git a/webapi/src/main/scala/org/knora/webapi/annotation/ProjectUnique.scala b/webapi/src/main/scala/org/knora/webapi/annotation/ProjectUnique.scala index cbda615a9d..c1ff2483b0 100644 --- a/webapi/src/main/scala/org/knora/webapi/annotation/ProjectUnique.scala +++ b/webapi/src/main/scala/org/knora/webapi/annotation/ProjectUnique.scala @@ -20,9 +20,8 @@ package org.knora.webapi.annotation /** - * Creates the ProjectUnique annotation. - * - * Marks values which need to be unique on the level of the PROJECT. - * - */ + * Creates the ProjectUnique annotation. + * + * Marks values which need to be unique on the level of the PROJECT. + */ class ProjectUnique() extends scala.annotation.StaticAnnotation diff --git a/webapi/src/main/scala/org/knora/webapi/annotation/ServerUnique.scala b/webapi/src/main/scala/org/knora/webapi/annotation/ServerUnique.scala index 6720350716..871bc5e97e 100644 --- a/webapi/src/main/scala/org/knora/webapi/annotation/ServerUnique.scala +++ b/webapi/src/main/scala/org/knora/webapi/annotation/ServerUnique.scala @@ -20,9 +20,8 @@ package org.knora.webapi.annotation /** - * Creates the ServerUnique annotation. - * - * Marks values which need to be unique on the level of the SERVER. - * - */ + * Creates the ServerUnique annotation. + * + * Marks values which need to be unique on the level of the SERVER. + */ class ServerUnique() extends scala.annotation.StaticAnnotation diff --git a/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala b/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala index 7840900dcc..91e01ca76e 100644 --- a/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala +++ b/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala @@ -80,8 +80,8 @@ trait LiveManagers extends Managers { this: Actor => /** - * The actor that forwards messages to actors that deal with persistent storage. - */ + * The actor that forwards messages to actors that deal with persistent storage. + */ lazy val storeManager: ActorRef = context.actorOf( Props(new StoreManager(appActor = self, cs = CacheServiceInMemImpl) with LiveActorMaker) .withDispatcher(KnoraDispatchers.KnoraActorDispatcher), @@ -89,30 +89,33 @@ trait LiveManagers extends Managers { ) /** - * The actor that forwards messages to responder actors to handle API requests. - */ + * The actor that forwards messages to responder actors to handle API requests. + */ lazy val responderManager: ActorRef = context.actorOf( Props( new ResponderManager( appActor = self, - responderData = ResponderData(system = context.system, - appActor = self, - knoraSettings = KnoraSettings(system), - cacheServiceSettings = new CacheServiceSettings(system.settings.config)) - ) with LiveActorMaker) + responderData = ResponderData( + system = context.system, + appActor = self, + knoraSettings = KnoraSettings(system), + cacheServiceSettings = new CacheServiceSettings(system.settings.config) + ) + ) with LiveActorMaker + ) .withDispatcher(KnoraDispatchers.KnoraActorDispatcher), name = RESPONDER_MANAGER_ACTOR_NAME ) } /** - * This is the first actor in the application. All other actors are children - * of this actor and thus it takes also the role of the supervisor actor. - * It accepts messages for starting and stopping the Knora-API, holds the - * current state of the application, and is responsible for coordination of - * the startup and shutdown sequence. Further, it forwards any messages meant - * for responders or the store to the respective actor. - */ + * This is the first actor in the application. All other actors are children + * of this actor and thus it takes also the role of the supervisor actor. + * It accepts messages for starting and stopping the Knora-API, holds the + * current state of the application, and is responsible for coordination of + * the startup and shutdown sequence. Further, it forwards any messages meant + * for responders or the store to the respective actor. + */ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDirectives with Timers { this: Managers => @@ -121,47 +124,47 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire implicit val system: ActorSystem = context.system /** - * The application's configuration. - */ + * The application's configuration. + */ implicit val knoraSettings: KnoraSettingsImpl = KnoraSettings(system) /** - * The Cache Service's configuration. - */ + * The Cache Service's configuration. + */ implicit val cacheServiceSettings: CacheServiceSettings = new CacheServiceSettings(system.settings.config) /** - * The default feature factory configuration, which is used during startup. - */ + * The default feature factory configuration, which is used during startup. + */ val defaultFeatureFactoryConfig: FeatureFactoryConfig = new KnoraSettingsFeatureFactoryConfig(knoraSettings) /** - * Provides the actor materializer (akka-http) - */ + * Provides the actor materializer (akka-http) + */ implicit val materializer: Materializer = Materializer.matFromSystem(system) /** - * Provides the default global execution context - */ + * Provides the default global execution context + */ implicit val executionContext: ExecutionContext = context.dispatcher /** - * Timeout definition - */ + * Timeout definition + */ implicit protected val timeout: Timeout = knoraSettings.defaultTimeout /** - * Route data. - */ + * Route data. + */ private val routeData = KnoraRouteData( system = system, appActor = self ) /** - * This actor acts as the supervisor for its child actors. - * Here we can override the default supervisor strategy. - */ + * This actor acts as the supervisor for its child actors. + * Here we can override the default supervisor strategy. + */ override val supervisorStrategy: OneForOneStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) { case _: ArithmeticException => Resume @@ -187,15 +190,14 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire private val withCacheService = cacheServiceSettings.cacheServiceEnabled /** - * Startup of the ApplicationActor is a two step process: - * 1. Step: Start the http server and bind to ip and port. This is done with - * the "initializing" behaviour - * - Success: After a successful bind, go to step 2. - * - Failure: If bind fails, then retry up to 5 times before exiting. - * - * 2. Step: - * - */ + * Startup of the ApplicationActor is a two step process: + * 1. Step: Start the http server and bind to ip and port. This is done with + * the "initializing" behaviour + * - Success: After a successful bind, go to step 2. + * - Failure: If bind fails, then retry up to 5 times before exiting. + * + * 2. Step: + */ def receive: Receive = initializing() def initializing(): Receive = { @@ -420,16 +422,17 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire case other => throw UnexpectedMessageException( - s"ApplicationActor received an unexpected message $other of type ${other.getClass.getCanonicalName}") + s"ApplicationActor received an unexpected message $other of type ${other.getClass.getCanonicalName}" + ) } /** - * All routes composed together and CORS activated based on the - * the configuration in application.conf (akka-http-cors). - * - * ALL requests go through each of the routes in ORDER. - * The FIRST matching route is used for handling a request. - */ + * All routes composed together and CORS activated based on the + * the configuration in application.conf (akka-http-cors). + * + * ALL requests go through each of the routes in ORDER. + * The FIRST matching route is used for handling a request. + */ private val apiRoutes: Route = logDuration { ServerVersion.addServerHeader { DSPApiDirectives.handleErrors(system) { @@ -472,12 +475,12 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire } /** - * Starts the Knora-API server. - * - * @param ignoreRepository if `true`, don't read anything from the repository on startup. - * @param requiresIIIFService if `true`, ensure that the IIIF service is started. - * @param retryCnt how many times was this command tried - */ + * Starts the Knora-API server. + * + * @param ignoreRepository if `true`, don't read anything from the repository on startup. + * @param requiresIIIFService if `true`, ensure that the IIIF service is started. + * @param retryCnt how many times was this command tried + */ def appStart(ignoreRepository: Boolean, requiresIIIFService: Boolean, retryCnt: Int): Unit = { val bindingFuture: Future[Http.ServerBinding] = Http() @@ -524,8 +527,8 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire } /** - * Stops Knora-API. - */ + * Stops Knora-API. + */ def appStop(): Unit = { logger.info("ApplicationActor - initiating shutdown ...") context.stop(self) @@ -546,17 +549,17 @@ class ApplicationActor extends Actor with Stash with LazyLogging with AroundDire } /** - * Prints the welcome message - */ + * Prints the welcome message + */ private def printBanner(): Unit = { var msg = """ - | ____ ____ ____ _ ____ ___ - | | _ \/ ___|| _ \ / \ | _ \_ _| - | | | | \___ \| |_) |____ / _ \ | |_) | | - | | |_| |___) | __/_____/ ___ \| __/| | - | |____/|____/|_| /_/ \_\_| |___| + | ____ ____ ____ _ ____ ___ + | | _ \/ ___|| _ \ / \ | _ \_ _| + | | | | \___ \| |_) |____ / _ \ | |_) | | + | | |_| |___) | __/_____/ ___ \| __/| | + | |____/|____/|_| /_/ \_\_| |___| """.stripMargin msg += "\n" diff --git a/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala b/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala index b95ff89f48..ea09a5450b 100644 --- a/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala +++ b/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala @@ -31,28 +31,28 @@ import scala.language.postfixOps import scala.languageFeature.postfixOps /** - * The applications actor system. - */ + * The applications actor system. + */ trait LiveCore extends Core { /** - * The application's actor system. - */ + * The application's actor system. + */ implicit lazy val system: ActorSystem = ActorSystem("webapi") /** - * The application's configuration. - */ + * The application's configuration. + */ implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) /** - * Provides the actor materializer (akka-http) - */ + * Provides the actor materializer (akka-http) + */ implicit val materializer: Materializer = Materializer.matFromSystem(system) /** - * Provides the default global execution context - */ + * Provides the default global execution context + */ implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // Initialise StringFormatter and RdfFeatureFactory with the system settings. @@ -61,11 +61,11 @@ trait LiveCore extends Core { RdfFeatureFactory.init(settings) /** - * The main application supervisor actor which is at the top of the actor - * hierarchy. All other actors are instantiated as child actors. Further, - * this actor is responsible for the execution of the startup and shutdown - * sequences. - */ + * The main application supervisor actor which is at the top of the actor + * hierarchy. All other actors are instantiated as child actors. Further, + * this actor is responsible for the execution of the startup and shutdown + * sequences. + */ lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with LiveManagers) .withDispatcher(KnoraDispatchers.KnoraActorDispatcher), diff --git a/webapi/src/main/scala/org/knora/webapi/app/Main.scala b/webapi/src/main/scala/org/knora/webapi/app/Main.scala index 206072f9e5..14d259aea1 100644 --- a/webapi/src/main/scala/org/knora/webapi/app/Main.scala +++ b/webapi/src/main/scala/org/knora/webapi/app/Main.scala @@ -26,10 +26,10 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} /** - * Starts Knora by bringing everything into scope by using the cake pattern. - * The [[LiveCore]] trait provides an actor system and the main application - * actor. - */ + * Starts Knora by bringing everything into scope by using the cake pattern. + * The [[LiveCore]] trait provides an actor system and the main application + * actor. + */ object Main extends App with LiveCore { val arglist = args.toList @@ -64,16 +64,14 @@ object Main extends App with LiveCore { appActor ! AppStart(ignoreRepository = false, requiresIIIFService = true) /** - * Adds shutting down of our actor system to the shutdown hook. - * Because we are blocking, we will run this on a separate thread. - */ + * Adds shutting down of our actor system to the shutdown hook. + * Because we are blocking, we will run this on a separate thread. + */ scala.sys.addShutdownHook( - new Thread( - () => { - val terminate: Future[Terminated] = system.terminate() - Await.result(terminate, 30.seconds) - } - ) + new Thread(() => { + val terminate: Future[Terminated] = system.terminate() + Await.result(terminate, 30.seconds) + }) ) system.registerOnTermination { diff --git a/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala b/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala index f4a3d452fb..3d63c693ca 100644 --- a/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala +++ b/webapi/src/main/scala/org/knora/webapi/contributors/GenerateContributorsFile.scala @@ -37,13 +37,13 @@ object GenerateContributorsFile extends App { // Configuration - val contributorsUrl = "https://api.github.com/repos/dasch-swiss/knora-api/contributors" + val contributorsUrl = "https://api.github.com/repos/dasch-swiss/knora-api/contributors" val defaultOutputFile = "Contributors.md" // Command-line args - private val conf = new GenerateContributorsFileConf(args.toIndexedSeq) - private val token = conf.token.toOption + private val conf = new GenerateContributorsFileConf(args.toIndexedSeq) + private val token = conf.token.toOption private val outputFile: Path = Paths.get(conf.output()) // Get the list of contributors. diff --git a/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala b/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala index a794c6642d..ea1a049ec5 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala @@ -22,9 +22,9 @@ package org.knora.webapi.core import akka.actor.{Actor, ActorRef, Props} /** - * This trait is part of the cake pattern used in the creation of actors. Here we only define the method, and with - * the forward declaration we make sure that it can only be attached to an actor. - */ + * This trait is part of the cake pattern used in the creation of actors. Here we only define the method, and with + * the forward declaration we make sure that it can only be attached to an actor. + */ trait ActorMaker { this: Actor => diff --git a/webapi/src/main/scala/org/knora/webapi/core/Core.scala b/webapi/src/main/scala/org/knora/webapi/core/Core.scala index c67fb22ee3..ee913ed656 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/Core.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/Core.scala @@ -26,8 +26,8 @@ import org.knora.webapi.settings.KnoraSettingsImpl import scala.concurrent.ExecutionContext /** - * Knora Core abstraction. - */ + * Knora Core abstraction. + */ trait Core { implicit val system: ActorSystem diff --git a/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala b/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala index 66a814cf18..60c748f04f 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala @@ -22,9 +22,9 @@ package org.knora.webapi.core import akka.actor.{Actor, Props} /** - * This trait is part of the cake pattern used in the creation of actors. This trait provides an implementation of the - * makeActor method that creates actors as a child actor. - */ + * This trait is part of the cake pattern used in the creation of actors. This trait provides an implementation of the + * makeActor method that creates actors as a child actor. + */ trait LiveActorMaker extends ActorMaker { this: Actor => diff --git a/webapi/src/main/scala/org/knora/webapi/exceptions/Exceptions.scala b/webapi/src/main/scala/org/knora/webapi/exceptions/Exceptions.scala index bca702d4d7..c35ce2b5bb 100644 --- a/webapi/src/main/scala/org/knora/webapi/exceptions/Exceptions.scala +++ b/webapi/src/main/scala/org/knora/webapi/exceptions/Exceptions.scala @@ -69,20 +69,20 @@ import org.apache.commons.lang3.{SerializationException, SerializationUtils} */ /** - * A trait implemented by all Knora exceptions, which must be serializable and extend [[java.lang.Exception]]. - */ + * A trait implemented by all Knora exceptions, which must be serializable and extend [[java.lang.Exception]]. + */ trait KnoraException extends Serializable { this: Exception => {} } /** - * An abstract base class for exceptions indicating that something about a request made it impossible to fulfil (e.g. - * it was malformed or referred to nonexistent data). - * - * @param msg a description of the error. - * @param cause the cause of the error. - */ + * An abstract base class for exceptions indicating that something about a request made it impossible to fulfil (e.g. + * it was malformed or referred to nonexistent data). + * + * @param msg a description of the error. + * @param cause the cause of the error. + */ abstract class RequestRejectedException(msg: String, cause: Throwable = null) extends Exception(msg, cause) with KnoraException @@ -93,100 +93,100 @@ object RequestRejectedException { } /** - * An exception indicating that the request parameters did not make sense. - * - * @param message a description of the error. - */ + * An exception indicating that the request parameters did not make sense. + * + * @param message a description of the error. + */ case class BadRequestException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that a user has provided bad credentials. - * - * @param message a description of the error. - */ + * An exception indicating that a user has provided bad credentials. + * + * @param message a description of the error. + */ case class BadCredentialsException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that a user has made a request for which the user lacks the necessary permission. - * - * @param message a description of the error. - */ + * An exception indicating that a user has made a request for which the user lacks the necessary permission. + * + * @param message a description of the error. + */ case class ForbiddenException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that the requested data was not found. - * - * @param message a description of the error. - */ + * An exception indicating that the requested data was not found. + * + * @param message a description of the error. + */ case class NotFoundException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that a requested update is not allowed because it would create a duplicate value. - * - * @param message a description of the error. - */ + * An exception indicating that a requested update is not allowed because it would create a duplicate value. + * + * @param message a description of the error. + */ case class DuplicateValueException(message: String = "Duplicate values are not permitted") extends RequestRejectedException(message) /** - * An exception indicating that a requested update is not allowed because it would violate an ontology constraint, - * e.g. an `knora-base:objectClassConstraint` or an OWL cardinality restriction. - * - * @param message a description of the error. - */ + * An exception indicating that a requested update is not allowed because it would violate an ontology constraint, + * e.g. an `knora-base:objectClassConstraint` or an OWL cardinality restriction. + * + * @param message a description of the error. + */ case class OntologyConstraintException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that a requested update is not allowed because another user has edited the - * data that was to be updated. - * - * @param message a description of the error. - */ + * An exception indicating that a requested update is not allowed because another user has edited the + * data that was to be updated. + * + * @param message a description of the error. + */ case class EditConflictException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that the submitted standoff is not valid. - * - * @param message a description of the error. - */ + * An exception indicating that the submitted standoff is not valid. + * + * @param message a description of the error. + */ case class InvalidStandoffException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that an error occurred when converting standoff markup to or from another format. - * - * @param message a description of the error. - */ + * An exception indicating that an error occurred when converting standoff markup to or from another format. + * + * @param message a description of the error. + */ case class StandoffConversionException(message: String) extends RequestRejectedException(message) /** - * An exception indicating that the Gravsearch query submitted to the API v2 search route was invalid. - * - * @param message a description of the error. - */ + * An exception indicating that the Gravsearch query submitted to the API v2 search route was invalid. + * + * @param message a description of the error. + */ case class GravsearchException(message: String) extends RequestRejectedException(message) /** - * An exception indication that the JSON-LD submitted to the API v2 was invalid. - * - * @param msg a description of the error. - * @param cause the cause for the error - */ + * An exception indication that the JSON-LD submitted to the API v2 was invalid. + * + * @param msg a description of the error. + * @param cause the cause for the error + */ case class InvalidJsonLDException(msg: String, cause: Throwable = null) extends RequestRejectedException(msg, cause) /** - * An exception indication that the RDF submitted to the API v2 was invalid. - * - * @param msg a description of the error. - * @param cause the cause for the error - */ + * An exception indication that the RDF submitted to the API v2 was invalid. + * + * @param msg a description of the error. + * @param cause the cause for the error + */ case class InvalidRdfException(msg: String, cause: Throwable = null) extends RequestRejectedException(msg, cause) /** - * An abstract class for exceptions indicating that something went wrong and it's not the client's fault. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * An abstract class for exceptions indicating that something went wrong and it's not the client's fault. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ abstract class InternalServerException(message: String, cause: Option[Throwable] = None) extends Exception(message, cause.orNull) with KnoraException @@ -197,14 +197,14 @@ object InternalServerException { } /** - * An exception indicating that during authentication something unexpected happened. - * - * @param message a description of the error. - */ -case class AuthenticationException(message: String = - "Error during authentication. Please report this as a possible bug.", - cause: Option[Throwable] = None) - extends InternalServerException(message) + * An exception indicating that during authentication something unexpected happened. + * + * @param message a description of the error. + */ +case class AuthenticationException( + message: String = "Error during authentication. Please report this as a possible bug.", + cause: Option[Throwable] = None +) extends InternalServerException(message) object AuthenticationException { def apply(message: String, e: Throwable, log: LoggingAdapter): AuthenticationException = @@ -212,48 +212,48 @@ object AuthenticationException { } /** - * Indicates that data could not be converted from one format to another. This exception should not be thrown when - * validating user input, but rather when processing input that has already been validated, or data that has been - * loaded from the triplestore. - * - * @param message a description of the error. - */ + * Indicates that data could not be converted from one format to another. This exception should not be thrown when + * validating user input, but rather when processing input that has already been validated, or data that has been + * loaded from the triplestore. + * + * @param message a description of the error. + */ case class DataConversionException(message: String) extends InternalServerException(message) /** - * An exception indicating that during file upload there was an error. - * - * @param message a description of the error. - */ + * An exception indicating that during file upload there was an error. + * + * @param message a description of the error. + */ case class FileUploadException(message: String = "Error during file upload. Please report this as a possible bug.") extends InternalServerException(message) /** - * An exception indicating that a requested update was not performed, although it was expected to succeed. - * This probably indicates a bug. - * - * @param message a description of the error. - */ + * An exception indicating that a requested update was not performed, although it was expected to succeed. + * This probably indicates a bug. + * + * @param message a description of the error. + */ case class UpdateNotPerformedException( - message: String = "A requested update was not performed. Please report this as a possible bug.") - extends InternalServerException(message) + message: String = "A requested update was not performed. Please report this as a possible bug." +) extends InternalServerException(message) /** - * An exception indicating that an unsupported value was passed. - * This probably indicates a bug. - * - * @param message a description of the error. - */ + * An exception indicating that an unsupported value was passed. + * This probably indicates a bug. + * + * @param message a description of the error. + */ case class UnsupportedValueException( - message: String = "An unsupported value was given. Please report this as a possible bug.") - extends InternalServerException(message) + message: String = "An unsupported value was given. Please report this as a possible bug." +) extends InternalServerException(message) /** - * Indicates an internal server error in standoff-related processing. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates an internal server error in standoff-related processing. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class StandoffInternalException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message, cause) @@ -263,11 +263,11 @@ object StandoffInternalException { } /** - * Indicates that something happened that should be impossible. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that something happened that should be impossible. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class AssertionException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message, cause) @@ -277,20 +277,20 @@ object AssertionException { } /** - * An abstract class for exceptions indicating that something went wrong with the triplestore. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * An abstract class for exceptions indicating that something went wrong with the triplestore. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ abstract class TriplestoreException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message, cause) /** - * Indicates that the network connection to the triplestore failed. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that the network connection to the triplestore failed. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class TriplestoreConnectionException(message: String, cause: Option[Throwable] = None) extends TriplestoreException(message, cause) @@ -300,11 +300,11 @@ object TriplestoreConnectionException { } /** - * Indicates that a read timeout occurred while waiting for data from the triplestore. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that a read timeout occurred while waiting for data from the triplestore. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class TriplestoreTimeoutException(message: String, cause: Option[Throwable] = None) extends TriplestoreException(message, cause) @@ -314,11 +314,11 @@ object TriplestoreTimeoutException { } /** - * Indicates that we tried using a feature which is unsuported by the selected triplestore. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that we tried using a feature which is unsuported by the selected triplestore. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class TriplestoreUnsupportedFeatureException(message: String, cause: Option[Throwable] = None) extends TriplestoreException(message, cause) @@ -328,11 +328,11 @@ object TriplestoreUnsupportedFeatureException { } /** - * Indicates that something inside the Triplestore package went wrong. More details can be given in the message parameter. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that something inside the Triplestore package went wrong. More details can be given in the message parameter. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class TriplestoreInternalException(message: String, cause: Option[Throwable] = None) extends TriplestoreException(message, cause) @@ -342,11 +342,11 @@ object TriplestoreInternalException { } /** - * Indicates that the triplestore returned an error message, or a response that could not be parsed. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that the triplestore returned an error message, or a response that could not be parsed. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class TriplestoreResponseException(message: String, cause: Option[Throwable] = None) extends TriplestoreException(message, cause) @@ -356,10 +356,10 @@ object TriplestoreResponseException { } /** - * Indicates an inconsistency in repository data. - * - * @param message a description of the error. - */ + * Indicates an inconsistency in repository data. + * + * @param message a description of the error. + */ case class InconsistentRepositoryDataException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message, cause) @@ -369,11 +369,11 @@ object InconsistentRepositoryDataException { } /** - * Indicates that the API server generated invalid JSON in an API response. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that the API server generated invalid JSON in an API response. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class InvalidApiJsonException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message, cause) @@ -383,76 +383,76 @@ object InvalidApiJsonException { } /** - * Indicates that the during caching with the [[org.knora.webapi.store.cacheservice.CacheService]] something went wrong. - * - * @param message a description of the error. - */ + * Indicates that the during caching with the [[org.knora.webapi.store.cacheservice.CacheService]] something went wrong. + * + * @param message a description of the error. + */ abstract class CacheServiceException(message: String) extends InternalServerException(message) /** - * Indicates that an application lock could not be acquired. - * - * @param message a description of the error. - */ + * Indicates that an application lock could not be acquired. + * + * @param message a description of the error. + */ case class ApplicationLockException(message: String) extends InternalServerException(message) /** - * Indicates that an error occurred in transaction management. - */ + * Indicates that an error occurred in transaction management. + */ case class TransactionManagementException(message: String) extends InternalServerException(message) /** - * Indicates that an Akka actor received an unexpected message. - * - * @param message a description of the error. - */ + * Indicates that an Akka actor received an unexpected message. + * + * @param message a description of the error. + */ case class UnexpectedMessageException(message: String) extends InternalServerException(message) /** - * Indicates that an error occurred in the application's cache. - * - * @param message a description of the error. - */ + * Indicates that an error occurred in the application's cache. + * + * @param message a description of the error. + */ case class ApplicationCacheException(message: String) extends InternalServerException(message) /** - * Indicates that an error occurred during the generation of SPARQL query code. - * - * @param message a description of the error. - */ + * Indicates that an error occurred during the generation of SPARQL query code. + * + * @param message a description of the error. + */ case class SparqlGenerationException(message: String) extends InternalServerException(message) /** - * Indicates that an error occurred during the generation of client API code. - * - * @param message a description of the error. - */ + * Indicates that an error occurred during the generation of client API code. + * + * @param message a description of the error. + */ case class ClientApiGenerationException(message: String) extends InternalServerException(message) /** - * A generic [[InternalServerException]] for wrapping any non-serializable exception in a serializable form. - */ + * A generic [[InternalServerException]] for wrapping any non-serializable exception in a serializable form. + */ case class WrapperException(e: Throwable) extends InternalServerException(e.toString) /** - * Indicates that an error occurred when trying to write a file to the disk. - * - * @param message a description of the error. - */ + * Indicates that an error occurred when trying to write a file to the disk. + * + * @param message a description of the error. + */ case class FileWriteException(message: String) extends InternalServerException(message) /** - * Indicates that a request attempted to use a feature that has not yet been implemented. - * - * @param message a description of the error. - */ + * Indicates that a request attempted to use a feature that has not yet been implemented. + * + * @param message a description of the error. + */ case class NotImplementedException(message: String) extends InternalServerException(message) /** - * Indicates that an error occurred with Sipi not relating to the user's request (it is not the user's fault). - * - * @param message a description of the error. - */ + * Indicates that an error occurred with Sipi not relating to the user's request (it is not the user's fault). + * + * @param message a description of the error. + */ case class SipiException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message, cause) @@ -462,10 +462,10 @@ object SipiException { } /** - * An abstract base class for exceptions indicating that something about a configuration made it impossible to start. - * - * @param message a description of the error. - */ + * An abstract base class for exceptions indicating that something about a configuration made it impossible to start. + * + * @param message a description of the error. + */ abstract class ApplicationConfigurationException(message: String, cause: Option[Throwable] = None) extends Exception(message, cause.orNull) with KnoraException @@ -476,77 +476,75 @@ object ApplicationConfigurationException { } /** - * Indicates that an unsupported triplestore was selected in the configuration. - * - * @param message a description of the error. - */ + * Indicates that an unsupported triplestore was selected in the configuration. + * + * @param message a description of the error. + */ case class UnsupportedTriplestoreException(message: String) extends ApplicationConfigurationException(message) /** - * Indicates that the HTTP configuration is incorrect. - * - * @param message a description of the error. - */ + * Indicates that the HTTP configuration is incorrect. + * + * @param message a description of the error. + */ case class HttpConfigurationException(message: String) extends ApplicationConfigurationException(message) /** - * Indicates that a test configuration is incorrect. - * - * @param message a description of the error. - */ + * Indicates that a test configuration is incorrect. + * + * @param message a description of the error. + */ case class TestConfigurationException(message: String) extends ApplicationConfigurationException(message) /** - * Indicates that a feature toggle configuration is incorrect. - * - * @param message a description of the error. - */ + * Indicates that a feature toggle configuration is incorrect. + * + * @param message a description of the error. + */ case class FeatureToggleException(message: String, cause: Option[Throwable] = None) extends ApplicationConfigurationException(message) /** - * Indicates that RDF processing failed. - * - * @param message a description of the error. - * @param cause the original exception representing the cause of the error, if any. - */ + * Indicates that RDF processing failed. + * + * @param message a description of the error. + * @param cause the original exception representing the cause of the error, if any. + */ case class RdfProcessingException(message: String, cause: Option[Throwable] = None) extends InternalServerException(message) /** - * Helper functions for error handling. - */ + * Helper functions for error handling. + */ object ExceptionUtil { /** - * Checks whether an exception is serializable. - * - * @param e the exception to be checked. - * @return `true` if the exception is serializable, otherwise `false`. - */ - def isSerializable(e: Throwable): Boolean = { + * Checks whether an exception is serializable. + * + * @param e the exception to be checked. + * @return `true` if the exception is serializable, otherwise `false`. + */ + def isSerializable(e: Throwable): Boolean = try { SerializationUtils.serialize(e) true } catch { case _: SerializationException => false } - } /** - * Checks whether an exception is serializable. If it is serializable, it is returned as-is. If not, - * the exception is logged with its stack trace, and a string representation of it is returned in a - * [[WrapperException]]. - * - * @param e the exception to be checked. - * @return the same exception, or a [[WrapperException]]. - */ - def logAndWrapIfNotSerializable(e: Throwable, log: LoggingAdapter): Throwable = { + * Checks whether an exception is serializable. If it is serializable, it is returned as-is. If not, + * the exception is logged with its stack trace, and a string representation of it is returned in a + * [[WrapperException]]. + * + * @param e the exception to be checked. + * @return the same exception, or a [[WrapperException]]. + */ + def logAndWrapIfNotSerializable(e: Throwable, log: LoggingAdapter): Throwable = if (isSerializable(e)) { e } else { log.error(e, e.toString) WrapperException(e) } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/feature/FeatureFactory.scala b/webapi/src/main/scala/org/knora/webapi/feature/FeatureFactory.scala index 71b1aebb9e..dd56e8df93 100644 --- a/webapi/src/main/scala/org/knora/webapi/feature/FeatureFactory.scala +++ b/webapi/src/main/scala/org/knora/webapi/feature/FeatureFactory.scala @@ -31,82 +31,81 @@ import scala.util.control.Exception._ import scala.util.{Failure, Success, Try} /** - * A tagging trait for module-specific factories that produce implementations of features. - */ + * A tagging trait for module-specific factories that produce implementations of features. + */ trait FeatureFactory /** - * A tagging trait for classes that implement features returned by feature factories. - */ + * A tagging trait for classes that implement features returned by feature factories. + */ trait Feature /** - * A tagging trait for case objects representing feature versions. - */ + * A tagging trait for case objects representing feature versions. + */ trait Version /** - * A trait representing the state of a feature toggle. - */ + * A trait representing the state of a feature toggle. + */ sealed trait FeatureToggleState /** - * Indicates that a feature toggle is off. - */ + * Indicates that a feature toggle is off. + */ case object ToggleStateOff extends FeatureToggleState /** - * Indicates that a feature toggle is on. - * - * @param version the configured version of the toggle. - */ + * Indicates that a feature toggle is on. + * + * @param version the configured version of the toggle. + */ case class ToggleStateOn(version: Int) extends FeatureToggleState /** - * Represents a feature toggle state, for use in match-case expressions. - */ + * Represents a feature toggle state, for use in match-case expressions. + */ sealed trait MatchableState[+T] /** - * A matchable object indicating that a feature toggle is off. - */ + * A matchable object indicating that a feature toggle is off. + */ case object Off extends MatchableState[Nothing] /** - * A matchable object indicating that a feature toggle is on. - * - * @param versionObj a case object representing the enabled version of the toggle. - * @tparam T the type of the case object. - */ + * A matchable object indicating that a feature toggle is on. + * + * @param versionObj a case object representing the enabled version of the toggle. + * @tparam T the type of the case object. + */ case class On[T <: Version](versionObj: T) extends MatchableState[T] /** - * Represents a feature toggle. - * - * @param featureName the name of the feature toggle. - * @param state the state of the feature toggle. - */ + * Represents a feature toggle. + * + * @param featureName the name of the feature toggle. + * @param state the state of the feature toggle. + */ case class FeatureToggle(featureName: String, state: FeatureToggleState) { /** - * Returns `true` if this toggle is enabled. - */ - def isEnabled: Boolean = { + * Returns `true` if this toggle is enabled. + */ + def isEnabled: Boolean = state match { case ToggleStateOn(_) => true case ToggleStateOff => false } - } /** - * Returns a [[MatchableState]] indicating the state of this toggle, for use in match-case expressions. - * - * @param versionObjects case objects representing the supported versions of the feature, in ascending - * order by version number. - * @tparam T a sealed trait implemented by the version objects. - * @return one of the objects in `versionObjects`, or [[Off]]. - */ - def getMatchableState[T <: Version](versionObjects: T*): MatchableState[T] = { + * Returns a [[MatchableState]] indicating the state of this toggle, for use in match-case expressions. + * + * @param versionObjects case objects representing the supported versions of the feature, in ascending + * order by version number. + * @tparam T a sealed trait implemented by the version objects. + * @return one of the objects in `versionObjects`, or [[Off]]. + */ + def getMatchableState[T <: Version](versionObjects: T*): MatchableState[T] = state match { case ToggleStateOn(version) => if (version < 1) { @@ -126,30 +125,29 @@ case class FeatureToggle(featureName: String, state: FeatureToggleState) { case ToggleStateOff => Off } - } } object FeatureToggle { /** - * The name of the HTTP request header containing feature toggles. - */ + * The name of the HTTP request header containing feature toggles. + */ val REQUEST_HEADER: String = "X-Knora-Feature-Toggles" val REQUEST_HEADER_LOWERCASE: String = REQUEST_HEADER.toLowerCase /** - * The name of the HTTP response header that lists configured feature toggles. - */ + * The name of the HTTP response header that lists configured feature toggles. + */ val RESPONSE_HEADER: String = REQUEST_HEADER val RESPONSE_HEADER_LOWERCASE: String = REQUEST_HEADER_LOWERCASE /** - * Constructs a default [[FeatureToggle]] from a [[FeatureToggleBaseConfig]]. - * - * @param baseConfig a feature toggle's base configuration. - * @return a [[FeatureToggle]] representing the feature's default setting. - */ - def fromBaseConfig(baseConfig: FeatureToggleBaseConfig): FeatureToggle = { + * Constructs a default [[FeatureToggle]] from a [[FeatureToggleBaseConfig]]. + * + * @param baseConfig a feature toggle's base configuration. + * @return a [[FeatureToggle]] representing the feature's default setting. + */ + def fromBaseConfig(baseConfig: FeatureToggleBaseConfig): FeatureToggle = FeatureToggle( featureName = baseConfig.featureName, state = if (baseConfig.enabledByDefault) { @@ -158,21 +156,22 @@ object FeatureToggle { ToggleStateOff } ) - } /** - * Constructs a feature toggle from non-base configuration. - * - * @param featureName the name of the feature. - * @param isEnabled `true` if the feature should be enabled. - * @param maybeVersion the version of the feature that should be used. - * @param baseConfig the base configuration of the toggle. - * @return a [[FeatureToggle]] for the toggle. - */ - def apply(featureName: String, - isEnabled: Boolean, - maybeVersion: Option[Int], - baseConfig: FeatureToggleBaseConfig): FeatureToggle = { + * Constructs a feature toggle from non-base configuration. + * + * @param featureName the name of the feature. + * @param isEnabled `true` if the feature should be enabled. + * @param maybeVersion the version of the feature that should be used. + * @param baseConfig the base configuration of the toggle. + * @return a [[FeatureToggle]] for the toggle. + */ + def apply( + featureName: String, + isEnabled: Boolean, + maybeVersion: Option[Int], + baseConfig: FeatureToggleBaseConfig + ): FeatureToggle = { if (!baseConfig.overrideAllowed) { throw BadRequestException(s"Feature toggle $featureName cannot be overridden") } @@ -200,39 +199,39 @@ object FeatureToggle { } /** - * An abstract class representing configuration for a [[FeatureFactory]] from a particular - * configuration source. - * - * @param maybeParent if this [[FeatureFactoryConfig]] has no setting for a particular - * feature toggle, it delegates to its parent. - */ + * An abstract class representing configuration for a [[FeatureFactory]] from a particular + * configuration source. + * + * @param maybeParent if this [[FeatureFactoryConfig]] has no setting for a particular + * feature toggle, it delegates to its parent. + */ abstract class FeatureFactoryConfig(protected val maybeParent: Option[FeatureFactoryConfig]) { /** - * Gets the base configuration for a feature toggle. - * - * @param featureName the name of the feature. - * @return the toggle's base configuration. - */ + * Gets the base configuration for a feature toggle. + * + * @param featureName the name of the feature. + * @return the toggle's base configuration. + */ protected[feature] def getBaseConfig(featureName: String): FeatureToggleBaseConfig /** - * Gets the base configurations of all feature toggles. - */ + * Gets the base configurations of all feature toggles. + */ protected[feature] def getAllBaseConfigs: Set[FeatureToggleBaseConfig] /** - * Returns a feature toggle in the configuration source of this [[FeatureFactoryConfig]]. - * - * @param featureName the name of a feature. - * @return the configuration of the feature toggle in this [[FeatureFactoryConfig]]'s configuration - * source, or `None` if the source contains no configuration for that feature toggle. - */ + * Returns a feature toggle in the configuration source of this [[FeatureFactoryConfig]]. + * + * @param featureName the name of a feature. + * @return the configuration of the feature toggle in this [[FeatureFactoryConfig]]'s configuration + * source, or `None` if the source contains no configuration for that feature toggle. + */ protected[feature] def getLocalConfig(featureName: String): Option[FeatureToggle] /** - * Returns a string giving the state of all feature toggles. - */ + * Returns a string giving the state of all feature toggles. + */ def makeToggleSettingsString: Option[String] = { // Convert each toggle to its string representation. val enabledToggles: Set[String] = getAllBaseConfigs.map { baseConfig: FeatureToggleBaseConfig => @@ -257,31 +256,29 @@ abstract class FeatureFactoryConfig(protected val maybeParent: Option[FeatureFac } /** - * Returns an [[HttpHeader]] giving the state of all feature toggles. - */ - def makeHttpResponseHeader: Option[HttpHeader] = { + * Returns an [[HttpHeader]] giving the state of all feature toggles. + */ + def makeHttpResponseHeader: Option[HttpHeader] = makeToggleSettingsString.map { settingsStr: String => RawHeader(FeatureToggle.RESPONSE_HEADER, settingsStr) } - } /** - * Adds an [[HttpHeader]] to an [[HttpResponse]] indicating which feature toggles are enabled. - */ - def addHeaderToHttpResponse(httpResponse: HttpResponse): HttpResponse = { + * Adds an [[HttpHeader]] to an [[HttpResponse]] indicating which feature toggles are enabled. + */ + def addHeaderToHttpResponse(httpResponse: HttpResponse): HttpResponse = makeHttpResponseHeader match { case Some(header) => httpResponse.withHeaders(header) case None => httpResponse } - } /** - * Returns a feature toggle, taking into account the base configuration - * and the parent configuration. - * - * @param featureName the name of the feature. - * @return the feature toggle. - */ + * Returns a feature toggle, taking into account the base configuration + * and the parent configuration. + * + * @param featureName the name of the feature. + * @return the feature toggle. + */ @tailrec final def getToggle(featureName: String): FeatureToggle = { // Get the base configuration for the feature. @@ -315,48 +312,42 @@ abstract class FeatureFactoryConfig(protected val maybeParent: Option[FeatureFac } /** - * A [[FeatureFactoryConfig]] that reads configuration from the application's configuration file. - * - * @param knoraSettings a [[KnoraSettingsImpl]] representing the configuration in the application's - * configuration file. - */ + * A [[FeatureFactoryConfig]] that reads configuration from the application's configuration file. + * + * @param knoraSettings a [[KnoraSettingsImpl]] representing the configuration in the application's + * configuration file. + */ class KnoraSettingsFeatureFactoryConfig(knoraSettings: KnoraSettingsImpl) extends FeatureFactoryConfig(None) { private val baseConfigs: Map[String, FeatureToggleBaseConfig] = knoraSettings.featureToggles.map { baseConfig => baseConfig.featureName -> baseConfig }.toMap - override protected[feature] def getBaseConfig(featureName: String): FeatureToggleBaseConfig = { + override protected[feature] def getBaseConfig(featureName: String): FeatureToggleBaseConfig = baseConfigs.getOrElse(featureName, throw BadRequestException(s"No such feature: $featureName")) - } - override protected[feature] def getAllBaseConfigs: Set[FeatureToggleBaseConfig] = { + override protected[feature] def getAllBaseConfigs: Set[FeatureToggleBaseConfig] = baseConfigs.values.toSet - } - override protected[feature] def getLocalConfig(featureName: String): Option[FeatureToggle] = { + override protected[feature] def getLocalConfig(featureName: String): Option[FeatureToggle] = Some(FeatureToggle.fromBaseConfig(getBaseConfig(featureName))) - } } /** - * An abstract class for feature factory configs that don't represent the base configuration. - * - * @param parent the parent config. - */ + * An abstract class for feature factory configs that don't represent the base configuration. + * + * @param parent the parent config. + */ abstract class OverridingFeatureFactoryConfig(parent: FeatureFactoryConfig) extends FeatureFactoryConfig(Some(parent)) { protected val featureToggles: Map[String, FeatureToggle] - override protected[feature] def getBaseConfig(featureName: String): FeatureToggleBaseConfig = { + override protected[feature] def getBaseConfig(featureName: String): FeatureToggleBaseConfig = parent.getBaseConfig(featureName) - } - override protected[feature] def getAllBaseConfigs: Set[FeatureToggleBaseConfig] = { + override protected[feature] def getAllBaseConfigs: Set[FeatureToggleBaseConfig] = parent.getAllBaseConfigs - } - override protected[feature] def getLocalConfig(featureName: String): Option[FeatureToggle] = { + override protected[feature] def getLocalConfig(featureName: String): Option[FeatureToggle] = featureToggles.get(featureName) - } } object RequestContextFeatureFactoryConfig { @@ -368,11 +359,11 @@ object RequestContextFeatureFactoryConfig { } /** - * A [[FeatureFactoryConfig]] that reads configuration from a header in an HTTP request. - * - * @param requestContext the HTTP request context. - * @param parent the parent [[FeatureFactoryConfig]]. - */ + * A [[FeatureFactoryConfig]] that reads configuration from a header in an HTTP request. + * + * @param requestContext the HTTP request context. + * @param parent the parent [[FeatureFactoryConfig]]. + */ class RequestContextFeatureFactoryConfig(requestContext: RequestContext, parent: FeatureFactoryConfig) extends OverridingFeatureFactoryConfig(parent) { import FeatureToggle._ @@ -407,7 +398,8 @@ class RequestContextFeatureFactoryConfig(requestContext: RequestContext, parent: allCatch .opt(versionStr.toInt) .getOrElse( - throw BadRequestException(s"Invalid version number '$versionStr' in feature toggle $featureName")) + throw BadRequestException(s"Invalid version number '$versionStr' in feature toggle $featureName") + ) } featureName -> FeatureToggle( @@ -444,10 +436,10 @@ class RequestContextFeatureFactoryConfig(requestContext: RequestContext, parent: } /** - * A [[FeatureFactoryConfig]] with a fixed configuration, to be used in tests. - * - * @param testToggles the toggles to be used. - */ + * A [[FeatureFactoryConfig]] with a fixed configuration, to be used in tests. + * + * @param testToggles the toggles to be used. + */ class TestFeatureFactoryConfig(testToggles: Set[FeatureToggle], parent: FeatureFactoryConfig) extends OverridingFeatureFactoryConfig(parent) { protected override val featureToggles: Map[String, FeatureToggle] = testToggles.map { setting => diff --git a/webapi/src/main/scala/org/knora/webapi/http/directives/DSPApiDirectives.scala b/webapi/src/main/scala/org/knora/webapi/http/directives/DSPApiDirectives.scala index e670d3deaf..32a33d6cdc 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/directives/DSPApiDirectives.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/directives/DSPApiDirectives.scala @@ -28,9 +28,9 @@ import org.knora.webapi.http.handler.KnoraExceptionHandler import org.knora.webapi.settings.KnoraSettings /** - * DSP-API HTTP directives, used by wrapping around a routes, to influence - * rejections and exception handling - */ + * DSP-API HTTP directives, used by wrapping around a routes, to influence + * rejections and exception handling + */ object DSPApiDirectives { // Our rejection handler. Here we are using the default one from the CORS lib diff --git a/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala b/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala index 64db3455b1..55a91b57f2 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala @@ -31,9 +31,9 @@ import org.knora.webapi.settings.KnoraSettingsImpl import spray.json.{JsNumber, JsObject, JsString, JsValue} /** - * The Knora exception handler is used by akka-http to convert any exceptions thrown during route processing - * into HttpResponses. It is brought implicitly into scope by the application actor. - */ + * The Knora exception handler is used by akka-http to convert any exceptions thrown during route processing + * into HttpResponses. It is brought implicitly into scope by the application actor. + */ object KnoraExceptionHandler extends LazyLogging { // A generic error message that we return to clients when an internal server error occurs. @@ -101,11 +101,11 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Converts an exception to an HTTP response in JSON format specific to `V1`. - * - * @param ex the exception to be converted. - * @return an [[HttpResponse]] in JSON format. - */ + * Converts an exception to an HTTP response in JSON format specific to `V1`. + * + * @param ex the exception to be converted. + * @return an [[HttpResponse]] in JSON format. + */ private def exceptionToJsonHttpResponseV1(ex: Throwable, settings: KnoraSettingsImpl): HttpResponse = { // Get the API status code that corresponds to the exception. val apiStatus: ApiStatusCodesV1.Value = ApiStatusCodesV1.fromException(ex) @@ -134,11 +134,11 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Converts an exception to an HTTP response in JSON format specific to `V2`. - * - * @param ex the exception to be converted. - * @return an [[HttpResponse]] in JSON format. - */ + * Converts an exception to an HTTP response in JSON format specific to `V2`. + * + * @param ex the exception to be converted. + * @return an [[HttpResponse]] in JSON format. + */ private def exceptionToJsonHttpResponseV2(ex: Throwable, settings: KnoraSettingsImpl): HttpResponse = { // Get the HTTP status code that corresponds to the exception. val httpStatus: StatusCode = ApiStatusCodesV2.fromException(ex) @@ -152,7 +152,9 @@ object KnoraExceptionHandler extends LazyLogging { context = JsonLDObject( Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> JsonLDString( - OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion)) + OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion + ) + ) ) ) @@ -164,11 +166,11 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Converts an exception to an HTTP response in JSON format specific to `ADM`. - * - * @param ex the exception to be converted. - * @return an [[HttpResponse]] in JSON format. - */ + * Converts an exception to an HTTP response in JSON format specific to `ADM`. + * + * @param ex the exception to be converted. + * @return an [[HttpResponse]] in JSON format. + */ private def exceptionToJsonHttpResponseADM(ex: Throwable, settings: KnoraSettingsImpl): HttpResponse = { // Get the HTTP status code that corresponds to the exception. @@ -187,11 +189,11 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Converts an exception to an HTTP response in HTML format specific to `V1`. - * - * @param ex the exception to be converted. - * @return an [[HttpResponse]] in HTML format. - */ + * Converts an exception to an HTTP response in HTML format specific to `V1`. + * + * @param ex the exception to be converted. + * @return an [[HttpResponse]] in HTML format. + */ private def exceptionToHtmlHttpResponseV1(ex: Throwable, settings: KnoraSettingsImpl): HttpResponse = { // Get the API status code that corresponds to the exception. val apiStatus: ApiStatusCodesV1.Value = ApiStatusCodesV1.fromException(ex) @@ -228,11 +230,11 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Converts an exception to an HTTP response in HTML format specific to `V2`. - * - * @param ex the exception to be converted. - * @return an [[HttpResponse]] in HTML format. - */ + * Converts an exception to an HTTP response in HTML format specific to `V2`. + * + * @param ex the exception to be converted. + * @return an [[HttpResponse]] in HTML format. + */ private def exceptionToHtmlHttpResponseV2(ex: Throwable, settings: KnoraSettingsImpl): HttpResponse = { // Get the HTTP status code that corresponds to the exception. @@ -261,11 +263,11 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Converts an exception to an HTTP response in HTML format specific to `ADM`. - * - * @param ex the exception to be converted. - * @return an [[HttpResponse]] in HTML format. - */ + * Converts an exception to an HTTP response in HTML format specific to `ADM`. + * + * @param ex the exception to be converted. + * @return an [[HttpResponse]] in HTML format. + */ private def exceptionToHtmlHttpResponseADM(ex: Throwable, settings: KnoraSettingsImpl): HttpResponse = { // Get the HTTP status code that corresponds to the exception. @@ -294,13 +296,13 @@ object KnoraExceptionHandler extends LazyLogging { } /** - * Given an exception, returns an error message suitable for clients. - * - * @param ex the exception. - * @param settings the application settings. - * @return an error message suitable for clients. - */ - private def makeClientErrorMessage(ex: Throwable, settings: KnoraSettingsImpl): String = { + * Given an exception, returns an error message suitable for clients. + * + * @param ex the exception. + * @param settings the application settings. + * @return an error message suitable for clients. + */ + private def makeClientErrorMessage(ex: Throwable, settings: KnoraSettingsImpl): String = ex match { case rre: RequestRejectedException => rre.toString @@ -311,6 +313,5 @@ object KnoraExceptionHandler extends LazyLogging { GENERIC_INTERNAL_SERVER_ERROR_MESSAGE } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesADM.scala b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesADM.scala index 500a83c9cf..31f0afd3fb 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesADM.scala @@ -23,17 +23,17 @@ import akka.http.scaladsl.model.{StatusCode, StatusCodes} import org.knora.webapi.exceptions._ /** - * The possible values for the HTTP status code that is returned as part of each Knora ADM response. - */ + * The possible values for the HTTP status code that is returned as part of each Knora ADM response. + */ object ApiStatusCodesADM { /** - * Converts an exception to a similar HTTP status code. - * - * @param ex an exception. - * @return an HTTP status code. - */ - def fromException(ex: Throwable): StatusCode = { + * Converts an exception to a similar HTTP status code. + * + * @param ex an exception. + * @return an HTTP status code. + */ + def fromException(ex: Throwable): StatusCode = ex match { // Subclasses of RequestRejectedException (which must be last in this group) case NotFoundException(_) => StatusCodes.NotFound @@ -49,6 +49,5 @@ object ApiStatusCodesADM { case InternalServerException(_) => StatusCodes.InternalServerError case _ => StatusCodes.InternalServerError } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV1.scala b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV1.scala index 53f3164a3b..b78fc46b5d 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV1.scala @@ -23,9 +23,9 @@ import akka.http.scaladsl.model.{StatusCode, StatusCodes} import org.knora.webapi.exceptions._ /** - * The possible values for the status code that is returned as part of each Knora API v1 response. - * Based on `salsah/api/ApiErrors.php`. - */ + * The possible values for the status code that is returned as part of each Knora API v1 response. + * Based on `salsah/api/ApiErrors.php`. + */ object ApiStatusCodesV1 extends Enumeration { val OK = Value(0) val INVALID_REQUEST_METHOD = Value(1) @@ -38,8 +38,8 @@ object ApiStatusCodesV1 extends Enumeration { val UNKNOWN_VOCABULARY = Value(8) /** - * The requested item was not found. This was called NO_NODES_FOUND in SALSAH; its meaning has been broadened here. - */ + * The requested item was not found. This was called NO_NODES_FOUND in SALSAH; its meaning has been broadened here. + */ val NOT_FOUND = Value(9) val API_ENDPOINT_NOT_FOUND = Value(10) @@ -64,29 +64,29 @@ object ApiStatusCodesV1 extends Enumeration { val GEONAMES_GEONAME_ID_EXISTING = Value(26) /** - * The requested update was not performed, perhaps because it was based on outdated information (e.g. because of an edit conflict). (New in Knora.) - */ + * The requested update was not performed, perhaps because it was based on outdated information (e.g. because of an edit conflict). (New in Knora.) + */ val UPDATE_NOT_PERFORMED = Value(27) /** - * The requested update was not performed, because it would have created a duplicate value. (New in Knora.) - */ + * The requested update was not performed, because it would have created a duplicate value. (New in Knora.) + */ val DUPLICATE_VALUE = Value(28) /** - * The requested update was not performed, because it would have violated an ontology constraint. (New in Knora.) - */ + * The requested update was not performed, because it would have violated an ontology constraint. (New in Knora.) + */ val ONTOLOGY_CONSTRAINT = Value(29) val UNSPECIFIED_ERROR = Value(999) /** - * Converts an exception to a similar API status code. - * - * @param ex an exception. - * @return an API status code. - */ - def fromException(ex: Throwable): Value = { + * Converts an exception to a similar API status code. + * + * @param ex an exception. + * @return an API status code. + */ + def fromException(ex: Throwable): Value = ex match { // Subclasses of RequestRejectedException (which must be last in this group) case NotFoundException(_) => ApiStatusCodesV1.NOT_FOUND @@ -102,15 +102,14 @@ object ApiStatusCodesV1 extends Enumeration { case InternalServerException(_) => ApiStatusCodesV1.INTERNAL_SALSAH_ERROR case _ => ApiStatusCodesV1.INTERNAL_SALSAH_ERROR } - } /** - * Converts an API status code to a similar HTTP status code. - * - * @param apiStatus an API status code. - * @return an HTTP status code. - */ - def toHttpStatus(apiStatus: Value): StatusCode = { + * Converts an API status code to a similar HTTP status code. + * + * @param apiStatus an API status code. + * @return an HTTP status code. + */ + def toHttpStatus(apiStatus: Value): StatusCode = apiStatus match { case OK => StatusCodes.OK case INVALID_REQUEST_METHOD => StatusCodes.MethodNotAllowed @@ -144,5 +143,4 @@ object ApiStatusCodesV1 extends Enumeration { case UPDATE_NOT_PERFORMED => StatusCodes.Conflict case UNSPECIFIED_ERROR => StatusCodes.InternalServerError } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala index 2f1b47f0e5..32b22f71bf 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala @@ -23,17 +23,17 @@ import akka.http.scaladsl.model.{StatusCode, StatusCodes} import org.knora.webapi.exceptions._ /** - * The possible values for the HTTP status code that is returned as part of each Knora API v2 response. - */ + * The possible values for the HTTP status code that is returned as part of each Knora API v2 response. + */ object ApiStatusCodesV2 { /** - * Converts an exception to a similar HTTP status code. - * - * @param ex an exception. - * @return an HTTP status code. - */ - def fromException(ex: Throwable): StatusCode = { + * Converts an exception to a similar HTTP status code. + * + * @param ex an exception. + * @return an HTTP status code. + */ + def fromException(ex: Throwable): StatusCode = ex match { // Subclasses of RequestRejectedException (which must be last in this group) case NotFoundException(_) => StatusCodes.NotFound @@ -49,6 +49,5 @@ object ApiStatusCodesV2 { case InternalServerException(_) => StatusCodes.InternalServerError case _ => StatusCodes.InternalServerError } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala b/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala index c09c3d2e73..4cdb2c0861 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala @@ -25,9 +25,9 @@ import akka.http.scaladsl.server.Route import org.knora.webapi.http.version.versioninfo.VersionInfo /** - * This object provides methods that can be used to add the [[Server]] header - * to an [[akka.http.scaladsl.model.HttpResponse]]. - */ + * This object provides methods that can be used to add the [[Server]] header + * to an [[akka.http.scaladsl.model.HttpResponse]]. + */ object ServerVersion { private val ApiNameAndVersion = s"${VersionInfo.name}/${VersionInfo.webapiVersion}" diff --git a/webapi/src/main/scala/org/knora/webapi/instrumentation/InstrumentationSupport.scala b/webapi/src/main/scala/org/knora/webapi/instrumentation/InstrumentationSupport.scala index f5a95c152e..82d6c94cd9 100644 --- a/webapi/src/main/scala/org/knora/webapi/instrumentation/InstrumentationSupport.scala +++ b/webapi/src/main/scala/org/knora/webapi/instrumentation/InstrumentationSupport.scala @@ -27,42 +27,41 @@ import scala.concurrent.{ExecutionContext, Future} import scala.util.Success /** - * A set of methods used for measuring stuff that is happening. - */ + * A set of methods used for measuring stuff that is happening. + */ trait InstrumentationSupport { /** - * For convenience. Returns the metrics logger based on the current - * class name. - */ + * For convenience. Returns the metrics logger based on the current + * class name. + */ protected lazy val metricsLogger: Logger = getMetricsLoggerForClass /** - * Measures the time the future needs to complete. - * - * Example: - * - * val f = tracedFuture { - * Future { - * work inside the future - * } - * } - * - * @param name the name identifying the span. - * @param future the future we want to instrument. - */ + * Measures the time the future needs to complete. + * + * Example: + * + * val f = tracedFuture { + * Future { + * work inside the future + * } + * } + * + * @param name the name identifying the span. + * @param future the future we want to instrument. + */ def tracedFuture[A](name: String)(future: => Future[A])(implicit ec: ExecutionContext): Future[A] = { /** - * NOTE: The elapsed time of the span is saved somewhere by kamon, but - * I have no idea how to get to it and this is why I'm calculating - * it in the metricsLogger.info line. This is a quick and dirty hack to - * have at least something. - */ + * NOTE: The elapsed time of the span is saved somewhere by kamon, but + * I have no idea how to get to it and this is why I'm calculating + * it in the metricsLogger.info line. This is a quick and dirty hack to + * have at least something. + */ val start = System.currentTimeMillis() - trace(name)(future.andThen { - case Success(_) => - metricsLogger.info(s"$name: {} ms", System.currentTimeMillis() - start) + trace(name)(future.andThen { case Success(_) => + metricsLogger.info(s"$name: {} ms", System.currentTimeMillis() - start) }) //.andThen(case completed => logger.info(s"$name: " + (System.currentTimeMillis() - start) + "ms")) } @@ -76,11 +75,11 @@ trait InstrumentationSupport { // } /** - * Based on the current class name, create a logger with the name in the - * form 'M-ClassName', e.g., 'M-RedisManager'. - * All loggers returned by this method can be configured in 'logback.xml', - * i.e., turned on or off. - */ + * Based on the current class name, create a logger with the name in the + * form 'M-ClassName', e.g., 'M-RedisManager'. + * All loggers returned by this method can be configured in 'logback.xml', + * i.e., turned on or off. + */ def getMetricsLoggerForClass: Logger = { val simpleClassName = this.getClass.getSimpleName Logger(LoggerFactory.getLogger(s"M-$simpleClassName")) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala index b179eeacad..9c6ea26146 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala @@ -23,8 +23,8 @@ package messages import exceptions.InconsistentRepositoryDataException /** - * Contains string constants for IRIs from ontologies used by the application. - */ + * Contains string constants for IRIs from ontologies used by the application. + */ object OntologyConstants { object Rdf { @@ -72,9 +72,9 @@ object OntologyConstants { val OnDatatype: IRI = OwlPrefixExpansion + "onDatatype" /** - * Cardinality IRIs expressed as OWL restrictions, which specify the properties that resources of - * a particular type can have. - */ + * Cardinality IRIs expressed as OWL restrictions, which specify the properties that resources of + * a particular type can have. + */ val cardinalityOWLRestrictions: Set[IRI] = Set( Cardinality, MinCardinality, @@ -84,8 +84,8 @@ object OntologyConstants { val NamedIndividual: IRI = OwlPrefixExpansion + "NamedIndividual" /** - * Classes defined by OWL that can be used as knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - */ + * Classes defined by OWL that can be used as knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + */ val ClassesThatCanBeKnoraClassConstraints: Set[IRI] = Set( Ontology, Class, @@ -138,8 +138,8 @@ object OntologyConstants { } /** - * http://schema.org - */ + * http://schema.org + */ object SchemaOrg { val SchemaOrgPrefixExpansion: IRI = "http://schema.org/" val Name: IRI = SchemaOrgPrefixExpansion + "name" @@ -152,23 +152,23 @@ object OntologyConstants { } /** - * The object types of resource metadata properties. - */ + * The object types of resource metadata properties. + */ val ResourceMetadataPropertyAxioms: Map[IRI, IRI] = Map( OntologyConstants.Rdfs.Label -> OntologyConstants.Xsd.String ) /** - * Ontology labels that are used only in the internal schema. - */ + * Ontology labels that are used only in the internal schema. + */ val InternalOntologyLabels: Set[String] = Set( KnoraBase.KnoraBaseOntologyLabel, KnoraAdmin.KnoraAdminOntologyLabel ) /** - * Ontology labels that are reserved for built-in ontologies. - */ + * Ontology labels that are reserved for built-in ontologies. + */ val BuiltInOntologyLabels: Set[String] = Set( KnoraBase.KnoraBaseOntologyLabel, KnoraAdmin.KnoraAdminOntologyLabel, @@ -398,8 +398,8 @@ object OntologyConstants { val StandoffTagHasEndParent: IRI = KnoraBasePrefixExpansion + "standoffTagHasEndParent" val StandoffTagHasUUID: IRI = KnoraBasePrefixExpansion + "standoffTagHasUUID" val StandoffTagHasOriginalXMLID: IRI = KnoraBasePrefixExpansion + "standoffTagHasOriginalXMLID" - val TargetHasOriginalXMLID - : IRI = KnoraBasePrefixExpansion + "targetHasOriginalXMLID" // virtual property, used only in CONSTRUCT + val TargetHasOriginalXMLID: IRI = + KnoraBasePrefixExpansion + "targetHasOriginalXMLID" // virtual property, used only in CONSTRUCT val StandoffTagHasInternalReference: IRI = KnoraBasePrefixExpansion + "standoffTagHasInternalReference" val StandoffTagHasStartAncestor: IRI = KnoraBasePrefixExpansion + "standoffTagHasStartAncestor" @@ -560,14 +560,14 @@ object OntologyConstants { val DefaultSharedOntologiesProject: IRI = KnoraAdminPrefixExpansion + "DefaultSharedOntologiesProject" /** - * The system user is the owner of objects that are created by the system, rather than directly by the user, - * such as link values for standoff resource references. - */ + * The system user is the owner of objects that are created by the system, rather than directly by the user, + * such as link values for standoff resource references. + */ val SystemUser: IRI = KnoraAdminPrefixExpansion + "SystemUser" /** - * Every user not logged-in is per default an anonymous user. - */ + * Every user not logged-in is per default an anonymous user. + */ val AnonymousUser: IRI = KnoraAdminPrefixExpansion + "AnonymousUser" } @@ -636,12 +636,11 @@ object OntologyConstants { val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap - def lookup(name: String): Value = { + def lookup(name: String): Value = valueMap.get(name) match { case Some(value) => value case None => throw InconsistentRepositoryDataException(s"salsah-gui attribute type not found: $name") } - } } } @@ -681,48 +680,46 @@ object OntologyConstants { val KnoraApiPrefix: String = KnoraApiOntologyLabel + ":" /** - * The IRIs representing `knora-api:Resource` in Knora API v2, in the simple and complex schemas. - */ + * The IRIs representing `knora-api:Resource` in Knora API v2, in the simple and complex schemas. + */ lazy val KnoraApiV2ResourceIris: Set[IRI] = Set( OntologyConstants.KnoraApiV2Simple.Resource, OntologyConstants.KnoraApiV2Complex.Resource ) /** - * The IRIs representing `knora-api:GravsearchOptions` in Knora API v2, in the simple and complex schemas. - */ + * The IRIs representing `knora-api:GravsearchOptions` in Knora API v2, in the simple and complex schemas. + */ lazy val GravsearchOptionsIris: Set[IRI] = Set( OntologyConstants.KnoraApiV2Simple.GravsearchOptions, OntologyConstants.KnoraApiV2Complex.GravsearchOptions ) /** - * The IRIs representing `knora-api:useInference` in Knora API v2, in the simple and complex schemas. - */ + * The IRIs representing `knora-api:useInference` in Knora API v2, in the simple and complex schemas. + */ lazy val UseInferenceIris: Set[IRI] = Set( OntologyConstants.KnoraApiV2Simple.UseInference, OntologyConstants.KnoraApiV2Complex.UseInference ) /** - * Returns the IRI of `knora-api:subjectType` in the specified schema. - */ - def getSubjectTypePredicate(apiV2Schema: ApiV2Schema): IRI = { + * Returns the IRI of `knora-api:subjectType` in the specified schema. + */ + def getSubjectTypePredicate(apiV2Schema: ApiV2Schema): IRI = apiV2Schema match { case ApiV2Simple => KnoraApiV2Simple.SubjectType case ApiV2Complex => KnoraApiV2Complex.SubjectType } - } /** - * Returns the IRI of `knora-api:objectType` in the specified schema. - */ - def getObjectTypePredicate(apiV2Schema: ApiV2Schema): IRI = { + * Returns the IRI of `knora-api:objectType` in the specified schema. + */ + def getObjectTypePredicate(apiV2Schema: ApiV2Schema): IRI = apiV2Schema match { case ApiV2Simple => KnoraApiV2Simple.ObjectType case ApiV2Complex => KnoraApiV2Complex.ObjectType } - } } object KnoraApiV2Complex { @@ -978,8 +975,8 @@ object OntologyConstants { } object SalsahGuiApiV2WithValueObjects { - val SalsahGuiOntologyIri - : IRI = KnoraApi.ApiOntologyStart + SalsahGui.SalsahGuiOntologyLabel + KnoraApiV2Complex.VersionSegment + val SalsahGuiOntologyIri: IRI = + KnoraApi.ApiOntologyStart + SalsahGui.SalsahGuiOntologyLabel + KnoraApiV2Complex.VersionSegment val SalsahGuiPrefixExpansion: IRI = SalsahGuiOntologyIri + "#" val GuiAttribute: IRI = SalsahGuiPrefixExpansion + "guiAttribute" @@ -1083,9 +1080,9 @@ object OntologyConstants { } /** - * A map of IRIs in each possible source schema to the corresponding IRIs in each possible target schema, for the - * cases where this can't be done formally by [[SmartIri]]. - */ + * A map of IRIs in each possible source schema to the corresponding IRIs in each possible target schema, for the + * cases where this can't be done formally by [[SmartIri]]. + */ val CorrespondingIris: Map[(OntologySchema, OntologySchema), Map[IRI, IRI]] = Map( (InternalSchema, ApiV2Simple) -> Map( // All the values of this map must be either properties or datatypes. PropertyInfoContentV2.toOntologySchema diff --git a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala index f36ec52cd9..1764be65cd 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala @@ -59,8 +59,8 @@ import scala.util.matching.Regex import scala.util.{Failure, Success, Try} /** - * Provides instances of [[StringFormatter]], as well as string formatting constants. - */ + * Provides instances of [[StringFormatter]], as well as string formatting constants. + */ object StringFormatter { // A non-printing delimiter character, Unicode INFORMATION SEPARATOR ONE, that should never occur in data. @@ -86,187 +86,189 @@ object StringFormatter { val ANSI_RESET = "\u001B[0m" /** - * Separates the calendar name from the rest of a Knora date. - */ + * Separates the calendar name from the rest of a Knora date. + */ val CalendarSeparator: String = ":" /** - * Separates year, month, and day in a Knora date. - */ + * Separates year, month, and day in a Knora date. + */ val PrecisionSeparator: String = "-" /** - * Separates a date (year, month, day) from the era in a Knora date. - */ + * Separates a date (year, month, day) from the era in a Knora date. + */ val EraSeparator: String = " " /** - * Before Christ (equivalent to BCE) - */ + * Before Christ (equivalent to BCE) + */ val Era_BC: String = "BC" /** - * Before Common Era (equivalent to BC) - */ + * Before Common Era (equivalent to BC) + */ val Era_BCE: String = "BCE" /** - * Anno Domini (equivalent to CE) - */ + * Anno Domini (equivalent to CE) + */ val Era_AD: String = "AD" /** - * Common Era (equivalent to AD) - */ + * Common Era (equivalent to AD) + */ val Era_CE: String = "CE" /** - * String representation of the name of the Gregorian calendar. - */ + * String representation of the name of the Gregorian calendar. + */ val CalendarGregorian: String = "GREGORIAN" /** - * String representation of the name of the Julian calendar. - */ + * String representation of the name of the Julian calendar. + */ val CalendarJulian: String = "JULIAN" /** - * String representation of the name of the Islamic calendar. - */ + * String representation of the name of the Islamic calendar. + */ val CalendarIslamic: String = "ISLAMIC" /** - * String representation of day precision in a date. - */ + * String representation of day precision in a date. + */ val PrecisionDay: String = "DAY" /** - * String representation of month precision in a date. - */ + * String representation of month precision in a date. + */ val PrecisionMonth: String = "MONTH" /** - * String representation of year precision in a date. - */ + * String representation of year precision in a date. + */ val PrecisionYear: String = "YEAR" /** - * The version number of the current version of Knora's ARK URL format. - */ + * The version number of the current version of Knora's ARK URL format. + */ val ArkVersion: String = "1" /** - * The length of the canonical representation of a UUID. - */ + * The length of the canonical representation of a UUID. + */ val CanonicalUuidLength = 36 /** - * The length of a Base64-encoded UUID. - */ + * The length of a Base64-encoded UUID. + */ val Base64UuidLength = 22 /** - * The maximum number of times that `makeUnusedIri` will try to make a new, unused IRI. - */ + * The maximum number of times that `makeUnusedIri` will try to make a new, unused IRI. + */ val MAX_IRI_ATTEMPTS: Int = 5 /** - * The domain name used to construct Knora IRIs. - */ + * The domain name used to construct Knora IRIs. + */ val IriDomain: String = "rdfh.ch" /** - * A keyword used in IRI entity names to introduce a collection type annotation for client code generation. - */ + * A keyword used in IRI entity names to introduce a collection type annotation for client code generation. + */ val ClientCollectionTypeKeyword: String = "collection:" /** - * A string found in IRIs representing collection type annotations for client code generation. - */ + * A string found in IRIs representing collection type annotations for client code generation. + */ val ClientCollectionEntityNameStart: String = "#" + ClientCollectionTypeKeyword /** - * A container for an XML import namespace and its prefix label. - * - * @param namespace the namespace. - * @param prefixLabel the prefix label. - */ + * A container for an XML import namespace and its prefix label. + * + * @param namespace the namespace. + * @param prefixLabel the prefix label. + */ case class XmlImportNamespaceInfoV1(namespace: IRI, prefixLabel: String) /** - * Represents a parsed object of the property `salsah-gui:guiAttributeDefinition`. - * - * @param attributeName the name of the attribute. - * @param isRequired `true` if the attribute is required. - * @param allowedType the type of the attribute's value. - * @param enumeratedValues the allowed values, if this is an enumerated string attribute. - */ - case class SalsahGuiAttributeDefinition(attributeName: String, - isRequired: Boolean, - allowedType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value, - enumeratedValues: Set[String] = Set.empty[String], - unparsedString: String) - - /** - * Represents a parsed object of the property `salsah-gui:guiAttribute`. - * - * @param attributeName the name of the attribute. - * @param attributeValue the value of the attribute. - */ + * Represents a parsed object of the property `salsah-gui:guiAttributeDefinition`. + * + * @param attributeName the name of the attribute. + * @param isRequired `true` if the attribute is required. + * @param allowedType the type of the attribute's value. + * @param enumeratedValues the allowed values, if this is an enumerated string attribute. + */ + case class SalsahGuiAttributeDefinition( + attributeName: String, + isRequired: Boolean, + allowedType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value, + enumeratedValues: Set[String] = Set.empty[String], + unparsedString: String + ) + + /** + * Represents a parsed object of the property `salsah-gui:guiAttribute`. + * + * @param attributeName the name of the attribute. + * @param attributeValue the value of the attribute. + */ case class SalsahGuiAttribute(attributeName: String, attributeValue: SalsahGuiAttributeValue) /** - * Represents a parsed value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - */ + * Represents a parsed value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + */ sealed trait SalsahGuiAttributeValue { def attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value } /** - * Represents a parsed integer value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the integer value. - */ + * Represents a parsed integer value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the integer value. + */ case class SalsahGuiIntegerAttributeValue(value: Int) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Integer } /** - * Represents a parsed percent value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the percent value. - */ + * Represents a parsed percent value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the percent value. + */ case class SalsahGuiPercentAttributeValue(value: Int) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Percent } /** - * Represents a parsed decimal value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the decimal value. - */ + * Represents a parsed decimal value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the decimal value. + */ case class SalsahGuiDecimalAttributeValue(value: BigDecimal) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Decimal } /** - * Represents a parsed string value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the string value. - */ + * Represents a parsed string value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the string value. + */ case class SalsahGuiStringAttributeValue(value: String) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Str } /** - * Represents a parsed IRI value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the IRI value. - */ + * Represents a parsed IRI value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the IRI value. + */ case class SalsahGuiIriAttributeValue(value: IRI) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Iri @@ -284,137 +286,135 @@ object StringFormatter { */ /** - * The instance of [[StringFormatter]] that is initialised after the ActorSystem starts, - * and can parse project-specific API v2 ontology IRIs. This instance is used almost - * everywhere in the API server. - */ + * The instance of [[StringFormatter]] that is initialised after the ActorSystem starts, + * and can parse project-specific API v2 ontology IRIs. This instance is used almost + * everywhere in the API server. + */ private var generalInstance: Option[StringFormatter] = None /** - * The instance of [[StringFormatter]] that can be used as soon as the JVM starts, but - * can't parse project-specific API v2 ontology IRIs. This instance is used - * only to initialise the hard-coded API v2 ontologies [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2SimpleTransformationRules]] - * and [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2ComplexTransformationRules]]. - */ + * The instance of [[StringFormatter]] that can be used as soon as the JVM starts, but + * can't parse project-specific API v2 ontology IRIs. This instance is used + * only to initialise the hard-coded API v2 ontologies [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2SimpleTransformationRules]] + * and [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2ComplexTransformationRules]]. + */ private val instanceForConstantOntologies = new StringFormatter(None) /** - * Gets the singleton instance of [[StringFormatter]] that handles IRIs from data. - */ - def getGeneralInstance: StringFormatter = { + * Gets the singleton instance of [[StringFormatter]] that handles IRIs from data. + */ + def getGeneralInstance: StringFormatter = generalInstance match { case Some(instance) => instance case None => throw AssertionException("StringFormatter not yet initialised") } - } /** - * Gets the singleton instance of [[StringFormatter]] that can only handle the IRIs in built-in - * ontologies. - */ + * Gets the singleton instance of [[StringFormatter]] that can only handle the IRIs in built-in + * ontologies. + */ def getInstanceForConstantOntologies: StringFormatter = instanceForConstantOntologies /** - * Initialises the general instance of [[StringFormatter]]. - * - * @param settings the application settings. - */ - def init(settings: KnoraSettingsImpl): Unit = { + * Initialises the general instance of [[StringFormatter]]. + * + * @param settings the application settings. + */ + def init(settings: KnoraSettingsImpl): Unit = this.synchronized { generalInstance match { case Some(_) => () case None => generalInstance = Some(new StringFormatter(Some(settings))) } } - } /** - * Initialises the singleton instance of [[StringFormatter]] for a test. - */ - def initForTest(): Unit = { + * Initialises the singleton instance of [[StringFormatter]] for a test. + */ + def initForTest(): Unit = this.synchronized { generalInstance match { case Some(_) => () case None => generalInstance = Some(new StringFormatter(maybeSettings = None, initForTest = true)) } } - } /** - * Indicates whether the IRI is a data IRI, a definition IRI, or an IRI of an unknown type. - */ + * Indicates whether the IRI is a data IRI, a definition IRI, or an IRI of an unknown type. + */ private sealed trait IriType /** - * Indicates that the IRI is a data IRI. - */ + * Indicates that the IRI is a data IRI. + */ private case object KnoraDataIri extends IriType /** - * Indicates that the IRI is an ontology or ontology entity IRI. - */ + * Indicates that the IRI is an ontology or ontology entity IRI. + */ private case object KnoraDefinitionIri extends IriType /** - * Indicates that the type of the IRI is unknown. - */ + * Indicates that the type of the IRI is unknown. + */ private case object UnknownIriType extends IriType /** - * Holds information extracted from the IRI. - * - * @param iriType the type of the IRI. - * @param projectCode the IRI's project code, if any. - * @param ontologyName the IRI's ontology name, if any. - * @param entityName the IRI's entity name, if any. - * @param resourceID if this is a resource IRI or value IRI, its resource ID. - * @param valueID if this is a value IRI, its value ID. - * @param standoffStartIndex if this is a standoff IRI, its start index. - * @param ontologySchema the IRI's ontology schema, or `None` if it is not a Knora definition IRI. - * @param isBuiltInDef `true` if the IRI refers to a built-in Knora ontology or ontology entity. - */ - private case class SmartIriInfo(iriType: IriType, - projectCode: Option[String] = None, - ontologyName: Option[String] = None, - entityName: Option[String] = None, - resourceID: Option[String] = None, - valueID: Option[String] = None, - standoffStartIndex: Option[Int] = None, - ontologySchema: Option[OntologySchema], - isBuiltInDef: Boolean = false, - sharedOntology: Boolean = false) - - /** - * A cache that maps IRI strings to [[SmartIri]] instances. To keep the cache from getting too large, - * only IRIs from known ontologies are cached. - */ + * Holds information extracted from the IRI. + * + * @param iriType the type of the IRI. + * @param projectCode the IRI's project code, if any. + * @param ontologyName the IRI's ontology name, if any. + * @param entityName the IRI's entity name, if any. + * @param resourceID if this is a resource IRI or value IRI, its resource ID. + * @param valueID if this is a value IRI, its value ID. + * @param standoffStartIndex if this is a standoff IRI, its start index. + * @param ontologySchema the IRI's ontology schema, or `None` if it is not a Knora definition IRI. + * @param isBuiltInDef `true` if the IRI refers to a built-in Knora ontology or ontology entity. + */ + private case class SmartIriInfo( + iriType: IriType, + projectCode: Option[String] = None, + ontologyName: Option[String] = None, + entityName: Option[String] = None, + resourceID: Option[String] = None, + valueID: Option[String] = None, + standoffStartIndex: Option[Int] = None, + ontologySchema: Option[OntologySchema], + isBuiltInDef: Boolean = false, + sharedOntology: Boolean = false + ) + + /** + * A cache that maps IRI strings to [[SmartIri]] instances. To keep the cache from getting too large, + * only IRIs from known ontologies are cached. + */ private lazy val smartIriCache = new ConcurrentHashMap[IRI, SmartIri](2048) /** - * Gets a cached smart IRI, or constructs and caches one. - * - * @param iriStr the IRI in string form. - * @param creationFun a function that creates the smart IRI to be cached. - * @return the smart IRI. - */ - private def getOrCacheSmartIri(iriStr: IRI, creationFun: () => SmartIri): SmartIri = { + * Gets a cached smart IRI, or constructs and caches one. + * + * @param iriStr the IRI in string form. + * @param creationFun a function that creates the smart IRI to be cached. + * @return the smart IRI. + */ + private def getOrCacheSmartIri(iriStr: IRI, creationFun: () => SmartIri): SmartIri = smartIriCache.computeIfAbsent( iriStr, JavaUtil.function({ _ => creationFun() }) ) - } } /** - * Represents a parsed IRI with Knora-specific functionality. To construct a `SmartIri`, - * `import org.knora.webapi.messages.StringFormatter.IriConversions.ConvertibleIri`, then call one of the methods that - * it implicitly defines on `String`, e.g.: - * - * - "http://knora.example.org/ontology/0000/example#Something".toSmartIri - * - "http://knora.example.org/ontology/0000/example#Something".toSmartIriWithErr(throw BadRequestException("Invalid IRI")) - */ + * Represents a parsed IRI with Knora-specific functionality. To construct a `SmartIri`, + * `import org.knora.webapi.messages.StringFormatter.IriConversions.ConvertibleIri`, then call one of the methods that + * it implicitly defines on `String`, e.g.: + * + * - "http://knora.example.org/ontology/0000/example#Something".toSmartIri + * - "http://knora.example.org/ontology/0000/example#Something".toSmartIriWithErr(throw BadRequestException("Invalid IRI")) + */ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { /* @@ -435,198 +435,197 @@ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { */ /** - * Returns this IRI as a string in angle brackets. - */ + * Returns this IRI as a string in angle brackets. + */ def toSparql: String /** - * Returns `true` if this is a Knora data or definition IRI. - */ + * Returns `true` if this is a Knora data or definition IRI. + */ def isKnoraIri: Boolean /** - * Returns `true` if this is a Knora data IRI. - */ + * Returns `true` if this is a Knora data IRI. + */ def isKnoraDataIri: Boolean /** - * Returns `true` if this is a Knora resource IRI. - */ + * Returns `true` if this is a Knora resource IRI. + */ def isKnoraResourceIri: Boolean /** - * Returns `true` if this is a Knora value IRI. - */ + * Returns `true` if this is a Knora value IRI. + */ def isKnoraValueIri: Boolean /** - * Returns `true` if this is a Knora standoff IRI. - */ + * Returns `true` if this is a Knora standoff IRI. + */ def isKnoraStandoffIri: Boolean /** - * Returns `true` if this is a Knora ontology or entity IRI. - */ + * Returns `true` if this is a Knora ontology or entity IRI. + */ def isKnoraDefinitionIri: Boolean /** - * Returns `true` if this is a built-in Knora ontology or entity IRI. - * - * @return - */ + * Returns `true` if this is a built-in Knora ontology or entity IRI. + * + * @return + */ def isKnoraBuiltInDefinitionIri: Boolean /** - * Returns `true` if this IRI belongs to a shared ontology. - */ + * Returns `true` if this IRI belongs to a shared ontology. + */ def isKnoraSharedDefinitionIri: Boolean /** - * Returns `true` if this is an internal Knora ontology or entity IRI. - * - * @return - */ + * Returns `true` if this is an internal Knora ontology or entity IRI. + * + * @return + */ def isKnoraInternalDefinitionIri: Boolean /** - * Returns `true` if this is an internal Knora ontology entity IRI. - */ + * Returns `true` if this is an internal Knora ontology entity IRI. + */ def isKnoraInternalEntityIri: Boolean /** - * Returns `true` if this is a Knora ontology IRI. - */ + * Returns `true` if this is a Knora ontology IRI. + */ def isKnoraOntologyIri: Boolean /** - * Returns `true` if this is a Knora entity IRI. - */ + * Returns `true` if this is a Knora entity IRI. + */ def isKnoraEntityIri: Boolean /** - * Returns `true` if this is a Knora API v2 ontology or entity IRI. - */ + * Returns `true` if this is a Knora API v2 ontology or entity IRI. + */ def isKnoraApiV2DefinitionIri: Boolean /** - * Returns `true` if this is a Knora API v2 ontology entity IRI. - */ + * Returns `true` if this is a Knora API v2 ontology entity IRI. + */ def isKnoraApiV2EntityIri: Boolean /** - * Returns the IRI's project code, if any. - */ + * Returns the IRI's project code, if any. + */ def getProjectCode: Option[String] /** - * Returns the IRI's resource ID, if any. - */ + * Returns the IRI's resource ID, if any. + */ def getResourceID: Option[String] /** - * Returns the IRI's value ID, if any. - */ + * Returns the IRI's value ID, if any. + */ def getValueID: Option[String] /** - * Returns the IRI's standoff start index, if any. - */ + * Returns the IRI's standoff start index, if any. + */ def getStandoffStartIndex: Option[Int] /** - * If this is an ontology entity IRI, returns its ontology IRI. - */ + * If this is an ontology entity IRI, returns its ontology IRI. + */ def getOntologyFromEntity: SmartIri /** - * If this is a Knora ontology or entity IRI, returns the name of the ontology. Otherwise, throws [[DataConversionException]]. - */ + * If this is a Knora ontology or entity IRI, returns the name of the ontology. Otherwise, throws [[DataConversionException]]. + */ def getOntologyName: String /** - * If this is a Knora entity IRI, returns the name of the entity. Otherwise, throws [[DataConversionException]]. - */ + * If this is a Knora entity IRI, returns the name of the entity. Otherwise, throws [[DataConversionException]]. + */ def getEntityName: String /** - * If this is a Knora ontology IRI, constructs a Knora entity IRI based on it. Otherwise, throws [[DataConversionException]]. - * - * @param entityName the name of the entity. - */ + * If this is a Knora ontology IRI, constructs a Knora entity IRI based on it. Otherwise, throws [[DataConversionException]]. + * + * @param entityName the name of the entity. + */ def makeEntityIri(entityName: String): SmartIri /** - * Returns the IRI's [[OntologySchema]], or `None` if this is not a Knora definition IRI. - */ + * Returns the IRI's [[OntologySchema]], or `None` if this is not a Knora definition IRI. + */ def getOntologySchema: Option[OntologySchema] /** - * Checks that the IRI's ontology schema, if present, corresponds to the specified schema. If the IRI - * has no schema, does nothing. If the IRI has a schema that's different to the specified schema, calls - * `errorFun`. - * - * @param allowedSchema the schema to be allowed. - * @param errorFun a function that throws an exception. It will be called if the IRI has a different schema - * to the one specified. - * @return the same IRI - */ + * Checks that the IRI's ontology schema, if present, corresponds to the specified schema. If the IRI + * has no schema, does nothing. If the IRI has a schema that's different to the specified schema, calls + * `errorFun`. + * + * @param allowedSchema the schema to be allowed. + * @param errorFun a function that throws an exception. It will be called if the IRI has a different schema + * to the one specified. + * @return the same IRI + */ def checkApiV2Schema(allowedSchema: ApiV2Schema, errorFun: => Nothing): SmartIri /** - * Converts this IRI to another ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this IRI to another ontology schema. + * + * @param targetSchema the target schema. + */ override def toOntologySchema(targetSchema: OntologySchema): SmartIri /** - * Constructs a short prefix label for the ontology that the IRI belongs to. - */ + * Constructs a short prefix label for the ontology that the IRI belongs to. + */ def getShortPrefixLabel: String /** - * Constructs a longer prefix label than the one returned by `getShortPrefixLabel`, which may be needed - * if there are ontology name collisions. - */ + * Constructs a longer prefix label than the one returned by `getShortPrefixLabel`, which may be needed + * if there are ontology name collisions. + */ def getLongPrefixLabel: String /** - * If this is the IRI of a link value property, returns the IRI of the corresponding link property. Throws - * [[DataConversionException]] if this IRI is not a Knora entity IRI. - */ + * If this is the IRI of a link value property, returns the IRI of the corresponding link property. Throws + * [[DataConversionException]] if this IRI is not a Knora entity IRI. + */ def fromLinkValuePropToLinkProp: SmartIri /** - * If this is the IRI of a link property, returns the IRI of the corresponding link value property. Throws - * [[DataConversionException]] if this IRI is not a Knora entity IRI. - */ + * If this is the IRI of a link property, returns the IRI of the corresponding link value property. Throws + * [[DataConversionException]] if this IRI is not a Knora entity IRI. + */ def fromLinkPropToLinkValueProp: SmartIri /** - * If this is a Knora data IRI representing a resource, returns an ARK URL for the resource. Throws - * [[DataConversionException]] if this IRI is not a Knora resource IRI. - * - * @param maybeTimestamp an optional timestamp indicating the point in the resource's version history that the ARK URL should - * cite. - */ + * If this is a Knora data IRI representing a resource, returns an ARK URL for the resource. Throws + * [[DataConversionException]] if this IRI is not a Knora resource IRI. + * + * @param maybeTimestamp an optional timestamp indicating the point in the resource's version history that the ARK URL should + * cite. + */ def fromResourceIriToArkUrl(maybeTimestamp: Option[Instant] = None): String /** - * If this is a Knora data IRI representing a value, returns an ARK URL for the value. Throws - * [[DataConversionException]] if this IRI is not a Knora value IRI. - * - * @param maybeTimestamp an optional timestamp indicating the point in the value's version history that the ARK URL should - * cite. - */ + * If this is a Knora data IRI representing a value, returns an ARK URL for the value. Throws + * [[DataConversionException]] if this IRI is not a Knora value IRI. + * + * @param maybeTimestamp an optional timestamp indicating the point in the value's version history that the ARK URL should + * cite. + */ def fromValueIriToArkUrl(valueUUID: UUID, maybeTimestamp: Option[Instant] = None): String - override def equals(obj: scala.Any): Boolean = { + override def equals(obj: scala.Any): Boolean = // See the comment at the top of the SmartIri trait. obj match { case that: SmartIri => this.toString == that.toString case _ => false } - } override def hashCode: Int = toString.hashCode @@ -634,8 +633,8 @@ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { } /** - * Provides `apply` and `unapply` methods to for `SmartIri`. - */ + * Provides `apply` and `unapply` methods to for `SmartIri`. + */ object SmartIri { def apply(iriStr: IRI)(implicit stringFormatter: StringFormatter): SmartIri = stringFormatter.toSmartIri(iriStr) @@ -643,24 +642,24 @@ object SmartIri { } /** - * Provides automatic conversion of IRI strings to [[SmartIri]] objects. See [[https://www.scala-lang.org/api/current/scala/AnyVal.html]] - * for details. - */ + * Provides automatic conversion of IRI strings to [[SmartIri]] objects. See [[https://www.scala-lang.org/api/current/scala/AnyVal.html]] + * for details. + */ object IriConversions { implicit class ConvertibleIri(val self: IRI) extends AnyVal { /** - * Converts an IRI string to a [[SmartIri]]. - */ + * Converts an IRI string to a [[SmartIri]]. + */ def toSmartIri(implicit stringFormatter: StringFormatter): SmartIri = stringFormatter.toSmartIri(self) /** - * Converts an IRI string to a [[SmartIri]]. If the string cannot be converted, a function is called to report - * the error. Use this function to parse IRIs from client input. - * - * @param errorFun A function that throws an exception. It will be called if the string cannot be converted. - */ + * Converts an IRI string to a [[SmartIri]]. If the string cannot be converted, a function is called to report + * the error. Use this function to parse IRIs from client input. + * + * @param errorFun A function that throws an exception. It will be called if the string cannot be converted. + */ def toSmartIriWithErr(errorFun: => Nothing)(implicit stringFormatter: StringFormatter): SmartIri = stringFormatter.toSmartIriWithErr(self, errorFun) } @@ -668,11 +667,13 @@ object IriConversions { } /** - * Handles string parsing, formatting, conversion, and validation. - */ -class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = None, - maybeKnoraHostAndPort: Option[String] = None, - initForTest: Boolean = false) { + * Handles string parsing, formatting, conversion, and validation. + */ +class StringFormatter private ( + val maybeSettings: Option[KnoraSettingsImpl] = None, + maybeKnoraHostAndPort: Option[String] = None, + initForTest: Boolean = false +) { import StringFormatter._ @@ -709,7 +710,11 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No private val schemes = Array("http", "https") // A validator for URLs. - private val urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS) // local urls are URL-encoded Knora IRIs as part of the whole URL + private val urlValidator = + new UrlValidator( + schemes, + UrlValidator.ALLOW_LOCAL_URLS + ) // local urls are URL-encoded Knora IRIs as part of the whole URL // The hostname used in internal Knora IRIs. private val InternalIriHostname = "www.knora.org" @@ -743,7 +748,8 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No private val versionSegmentWords = Set("simple", "v2") // Reserved words that cannot be used in project-specific ontology names. - private val reservedIriWords = Set("knora", "ontology", "rdf", "rdfs", "owl", "xsd", "schema", "shared") ++ versionSegmentWords + private val reservedIriWords = + Set("knora", "ontology", "rdf", "rdfs", "owl", "xsd", "schema", "shared") ++ versionSegmentWords // The expected format of a Knora date. // Calendar:YYYY[-MM[-DD]][ EE][:YYYY[-MM[-DD]][ EE]] @@ -878,18 +884,18 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No """0+$""".r /** - * A regex that matches a valid username - * - 4 - 50 characters long - * - Only contains alphanumeric characters, underscore and dot. - * - Underscore and dot can't be at the end or start of a username - * - Underscore or dot can't be used multiple times in a row - */ + * A regex that matches a valid username + * - 4 - 50 characters long + * - Only contains alphanumeric characters, underscore and dot. + * - Underscore and dot can't be at the end or start of a username + * - Underscore or dot can't be used multiple times in a row + */ private val UsernameRegex: Regex = """^(?=.{4,50}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(? Nothing) extends SmartIri { def this(iriStr: IRI) = this(iriStr, None, throw DataConversionException(s"Couldn't parse IRI: $iriStr")) @@ -916,11 +922,11 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No private val iri: IRI = validateAndEscapeIri(iriStr, errorFun) /** - * Determines the API v2 schema of an external IRI. - * - * @param segments the segments of the namespace. - * @return the IRI's API schema. - */ + * Determines the API v2 schema of an external IRI. + * + * @param segments the segments of the namespace. + * @return the IRI's API schema. + */ private def parseApiV2VersionSegments(segments: Vector[String]): ApiV2Schema = { if (segments.length < 2) { errorFun @@ -969,10 +975,12 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No valueID = Some(valueID) ) - case StandoffIriRegex(projectCode: String, - resourceID: String, - valueID: String, - standoffStartIndex: String) => + case StandoffIriRegex( + projectCode: String, + resourceID: String, + valueID: String, + standoffStartIndex: String + ) => // It's a standoff IRI. SmartIriInfo( iriType = KnoraDataIri, @@ -990,8 +998,10 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No ontologySchema = None ) } - } else if (iri.startsWith(OntologyConstants.NamedGraphs.DataNamedGraphStart) || - iri == OntologyConstants.NamedGraphs.KnoraExplicitNamedGraph) { + } else if ( + iri.startsWith(OntologyConstants.NamedGraphs.DataNamedGraphStart) || + iri == OntologyConstants.NamedGraphs.KnoraExplicitNamedGraph + ) { // Nothing else to do. SmartIriInfo( iriType = KnoraDataIri, @@ -1085,7 +1095,10 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No errorFun } } else if (ontologyPath.length == 2) { - (false, Some(validateProjectShortcode(ontologyPath.head, errorFun))) // non-shared ontology with project code + ( + false, + Some(validateProjectShortcode(ontologyPath.head, errorFun)) + ) // non-shared ontology with project code } else { (false, None) // built-in ontology } @@ -1192,32 +1205,29 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No override def getOntologyFromEntity: SmartIri = ontologyFromEntity - override def makeEntityIri(entityName: String): SmartIri = { + override def makeEntityIri(entityName: String): SmartIri = if (isKnoraOntologyIri) { val entityIriStr = iri + "#" + entityName getOrCacheSmartIri(entityIriStr, () => new SmartIriImpl(entityIriStr)) } else { throw DataConversionException(s"$iri is not a Knora ontology IRI") } - } - override def getOntologyName: String = { + override def getOntologyName: String = iriInfo.ontologyName match { case Some(name) => name case None => throw DataConversionException(s"Expected a Knora ontology IRI: $iri") } - } - override def getEntityName: String = { + override def getEntityName: String = iriInfo.entityName match { case Some(name) => name case None => throw DataConversionException(s"Expected a Knora entity IRI: $iri") } - } override def getOntologySchema: Option[OntologySchema] = iriInfo.ontologySchema - override def checkApiV2Schema(allowedSchema: ApiV2Schema, errorFun: => Nothing): SmartIri = { + override def checkApiV2Schema(allowedSchema: ApiV2Schema, errorFun: => Nothing): SmartIri = iriInfo.ontologySchema match { case Some(schema) => if (schema == allowedSchema) { @@ -1228,7 +1238,6 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case None => this } - } override def getShortPrefixLabel: String = getOntologyName @@ -1245,7 +1254,7 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No prefix.append(ontologyName).toString } - override def toOntologySchema(targetSchema: OntologySchema): SmartIri = { + override def toOntologySchema(targetSchema: OntologySchema): SmartIri = if (!isKnoraDefinitionIri || iriInfo.ontologySchema.contains(targetSchema)) { this } else { @@ -1267,9 +1276,12 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No predicateMap.get(iri) match { case Some(convertedIri) => // Yes. Return the corresponding IRI in the target schema. - getOrCacheSmartIri(iriStr = convertedIri, creationFun = { () => - new SmartIriImpl(convertedIri) - }) + getOrCacheSmartIri( + iriStr = convertedIri, + creationFun = { () => + new SmartIriImpl(convertedIri) + } + ) case None => // No. Convert the IRI using a formal procedure. @@ -1291,14 +1303,12 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No throw AssertionException(s"IRI $iri is a Knora IRI, but is neither an ontology IRI nor an entity IRI") } } - } - private def getVersionSegment(targetSchema: ApiV2Schema): String = { + private def getVersionSegment(targetSchema: ApiV2Schema): String = targetSchema match { case ApiV2Simple => OntologyConstants.KnoraApiV2Simple.VersionSegment case ApiV2Complex => OntologyConstants.KnoraApiV2Complex.VersionSegment } - } private def externalToInternalEntityIri: SmartIri = { // Construct the string representation of this IRI in the target schema. @@ -1486,7 +1496,7 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No override def fromLinkPropToLinkValueProp: SmartIri = asLinkValueProp - override def isKnoraResourceIri: Boolean = { + override def isKnoraResourceIri: Boolean = if (!isKnoraDataIri) { false } else { @@ -1495,9 +1505,8 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => false } } - } - override def isKnoraValueIri: Boolean = { + override def isKnoraValueIri: Boolean = if (!isKnoraDataIri) { false } else { @@ -1506,9 +1515,8 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => false } } - } - override def isKnoraStandoffIri: Boolean = { + override def isKnoraStandoffIri: Boolean = if (!isKnoraDataIri) { false } else { @@ -1517,7 +1525,6 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => false } } - } override def fromResourceIriToArkUrl(maybeTimestamp: Option[Instant] = None): String = { if (!isKnoraResourceIri) { @@ -1563,11 +1570,11 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. Throws - * [[DataConversionException]] if the IRI is invalid. - * - * @param iri the IRI string to be parsed. - */ + * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. Throws + * [[DataConversionException]] if the IRI is invalid. + * + * @param iri the IRI string to be parsed. + */ def toSmartIri(iri: IRI, requireInternal: Boolean = false): SmartIri = { // Is this a Knora definition IRI? val smartIri: SmartIri = if (CacheableIriStarts.exists(start => iri.startsWith(start))) { @@ -1586,12 +1593,12 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. - * - * @param iri the IRI string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the IRI is invalid. - */ - def toSmartIriWithErr(iri: IRI, errorFun: => Nothing): SmartIri = { + * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. + * + * @param iri the IRI string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the IRI is invalid. + */ + def toSmartIriWithErr(iri: IRI, errorFun: => Nothing): SmartIri = // Is this a Knora definition IRI? if (CacheableIriStarts.exists(start => iri.startsWith(start))) { // Yes. Return it from the cache, or cache it if it's not already cached. @@ -1600,58 +1607,54 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No // No. Convert it to a SmartIri without caching it. new SmartIriImpl(iri, None, errorFun) } - } /** - * Checks that a string represents a valid integer. - * - * @param s the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a - * valid integer. - * @return the integer value of the string. - */ - def validateInt(s: String, errorFun: => Nothing): Int = { + * Checks that a string represents a valid integer. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a + * valid integer. + * @return the integer value of the string. + */ + def validateInt(s: String, errorFun: => Nothing): Int = try { s.toInt } catch { case _: Exception => errorFun // value could not be converted to an Integer } - } /** - * Checks that a string represents a valid decimal number. - * - * @param s the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a - * valid decimal number. - * @return the decimal value of the string. - */ - def validateBigDecimal(s: String, errorFun: => Nothing): BigDecimal = { + * Checks that a string represents a valid decimal number. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a + * valid decimal number. + * @return the decimal value of the string. + */ + def validateBigDecimal(s: String, errorFun: => Nothing): BigDecimal = try { BigDecimal(s) } catch { case _: Exception => errorFun // value could not be converted to a decimal } - } /** - * Returns `true` if a string is an IRI. - * - * @param s the string to be checked. - * @return `true` if the string is an IRI. - */ - def isIri(s: String): Boolean = { + * Returns `true` if a string is an IRI. + * + * @param s the string to be checked. + * @return `true` if the string is an IRI. + */ + def isIri(s: String): Boolean = urlValidator.isValid(s) - } /** - * Checks that a string represents a valid IRI. Also encodes the IRI, preserving existing %-escapes. - * - * @param s the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * IRI. - * @return the same string. - */ + * Checks that a string represents a valid IRI. Also encodes the IRI, preserving existing %-escapes. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * IRI. + * @return the same string. + */ def validateAndEscapeIri(s: String, errorFun: => Nothing): IRI = { val urlEncodedStr = encodeAllowEscapes(s) @@ -1663,36 +1666,34 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Check that an optional string represents a valid IRI. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * IRI. - * @return the same optional string. - */ - def toOptionalIri(maybeString: Option[String], errorFun: => Nothing): Option[IRI] = { + * Check that an optional string represents a valid IRI. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * IRI. + * @return the same optional string. + */ + def toOptionalIri(maybeString: Option[String], errorFun: => Nothing): Option[IRI] = maybeString match { case Some(s) => Some(validateAndEscapeIri(s, errorFun)) case None => None } - } /** - * Returns `true` if an IRI string looks like a Knora project IRI - * - * @param iri the IRI to be checked. - */ - def isKnoraProjectIriStr(iri: IRI): Boolean = { + * Returns `true` if an IRI string looks like a Knora project IRI + * + * @param iri the IRI to be checked. + */ + def isKnoraProjectIriStr(iri: IRI): Boolean = isIri(iri) && (iri.startsWith("http://" + IriDomain + "/projects/") || isKnoraBuiltInProjectIriStr(iri)) - } /** - * Returns `true` if an IRI string looks like a Knora built-in IRI: - * - http://www.knora.org/ontology/knora-admin#SystemProject - * - http://www.knora.org/ontology/knora-admin#SharedOntologiesProject - * - * @param iri the IRI to be checked. - */ + * Returns `true` if an IRI string looks like a Knora built-in IRI: + * - http://www.knora.org/ontology/knora-admin#SystemProject + * - http://www.knora.org/ontology/knora-admin#SharedOntologiesProject + * + * @param iri the IRI to be checked. + */ def isKnoraBuiltInProjectIriStr(iri: IRI): Boolean = { val builtInProjects = Seq( @@ -1704,52 +1705,48 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Returns `true` if an IRI string looks like a Knora list IRI. - * - * @param iri the IRI to be checked. - */ - def isKnoraListIriStr(iri: IRI): Boolean = { + * Returns `true` if an IRI string looks like a Knora list IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraListIriStr(iri: IRI): Boolean = isIri(iri) && iri.startsWith("http://" + IriDomain + "/lists/") - } /** - * Returns `true` if an IRI string looks like a Knora user IRI. - * - * @param iri the IRI to be checked. - */ - def isKnoraUserIriStr(iri: IRI): Boolean = { + * Returns `true` if an IRI string looks like a Knora user IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraUserIriStr(iri: IRI): Boolean = isIri(iri) && iri.startsWith("http://" + IriDomain + "/users/") - } /** - * Returns `true` if an IRI string looks like a Knora group IRI. - * - * @param iri the IRI to be checked. - */ - def isKnoraGroupIriStr(iri: IRI): Boolean = { + * Returns `true` if an IRI string looks like a Knora group IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraGroupIriStr(iri: IRI): Boolean = isIri(iri) && iri.startsWith("http://" + IriDomain + "/groups/") - } /** - * Returns `true` if an IRI string looks like a Knora permission IRI. - * - * @param iri the IRI to be checked. - */ - def isKnoraPermissionIriStr(iri: IRI): Boolean = { + * Returns `true` if an IRI string looks like a Knora permission IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraPermissionIriStr(iri: IRI): Boolean = isIri(iri) && iri.startsWith("http://" + IriDomain + "/permissions/") - } /** - * Checks that a string represents a valid resource identifier in a standoff link. - * - * @param s the string to be checked. - * @param acceptClientIDs if `true`, the function accepts either an IRI or an XML NCName prefixed by `ref:`. - * The latter is used to refer to a client's ID for a resource that is described in an XML bulk import. - * If `false`, only an IRI is accepted. - * @param errorFun a function that throws an exception. It will be called if the form of the string is invalid. - * @return the same string. - */ - def validateStandoffLinkResourceReference(s: String, acceptClientIDs: Boolean, errorFun: => Nothing): IRI = { + * Checks that a string represents a valid resource identifier in a standoff link. + * + * @param s the string to be checked. + * @param acceptClientIDs if `true`, the function accepts either an IRI or an XML NCName prefixed by `ref:`. + * The latter is used to refer to a client's ID for a resource that is described in an XML bulk import. + * If `false`, only an IRI is accepted. + * @param errorFun a function that throws an exception. It will be called if the form of the string is invalid. + * @return the same string. + */ + def validateStandoffLinkResourceReference(s: String, acceptClientIDs: Boolean, errorFun: => Nothing): IRI = if (acceptClientIDs) { s match { case StandoffLinkReferenceToClientIDForResourceRegex(_) => s @@ -1758,48 +1755,45 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } else { validateAndEscapeIri(s, errorFun) } - } /** - * Checks whether a string is a reference to a client's ID for a resource described in an XML bulk import. - * - * @param s the string to be checked. - * @return `true` if the string is an XML NCName prefixed by `ref:`. - */ - def isStandoffLinkReferenceToClientIDForResource(s: String): Boolean = { + * Checks whether a string is a reference to a client's ID for a resource described in an XML bulk import. + * + * @param s the string to be checked. + * @return `true` if the string is an XML NCName prefixed by `ref:`. + */ + def isStandoffLinkReferenceToClientIDForResource(s: String): Boolean = s match { case StandoffLinkReferenceToClientIDForResourceRegex(_) => true case _ => false } - } /** - * Accepts a reference from a standoff link to a resource. The reference may be either a real resource IRI - * (referring to a resource that already exists) or a client's ID for a resource that doesn't yet exist and is - * described in an XML bulk import. Returns the real IRI of the target resource. - * - * @param iri an IRI from a standoff link, either in the form of a real resource IRI or in the form of - * a reference to a client's ID for a resource. - * @param clientResourceIDsToResourceIris a map of client resource IDs to real resource IRIs. - */ - def toRealStandoffLinkTargetResourceIri(iri: IRI, clientResourceIDsToResourceIris: Map[String, IRI]): IRI = { + * Accepts a reference from a standoff link to a resource. The reference may be either a real resource IRI + * (referring to a resource that already exists) or a client's ID for a resource that doesn't yet exist and is + * described in an XML bulk import. Returns the real IRI of the target resource. + * + * @param iri an IRI from a standoff link, either in the form of a real resource IRI or in the form of + * a reference to a client's ID for a resource. + * @param clientResourceIDsToResourceIris a map of client resource IDs to real resource IRIs. + */ + def toRealStandoffLinkTargetResourceIri(iri: IRI, clientResourceIDsToResourceIris: Map[String, IRI]): IRI = iri match { case StandoffLinkReferenceToClientIDForResourceRegex(clientResourceID) => clientResourceIDsToResourceIris(clientResourceID) case _ => iri } - } /** - * Makes a string safe to be entered in the triplestore by escaping special chars. - * - * If the param `revert` is set to `true`, the string is unescaped. - * - * @param s a string. - * @param errorFun a function that throws an exception. It will be called if the string is empty or contains - * a carriage return (`\r`). - * @return the same string, escaped or unescaped as requested. - */ + * Makes a string safe to be entered in the triplestore by escaping special chars. + * + * If the param `revert` is set to `true`, the string is unescaped. + * + * @param s a string. + * @param errorFun a function that throws an exception. It will be called if the string is empty or contains + * a carriage return (`\r`). + * @return the same string, escaped or unescaped as requested. + */ def toSparqlEncodedString(s: String, errorFun: => Nothing): String = { if (s.isEmpty || s.contains("\r")) errorFun @@ -1813,37 +1807,35 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Unescapes a string that has been escaped for SPARQL. - * - * @param s the string to be unescaped. - * @return the unescaped string. - */ - def fromSparqlEncodedString(s: String): String = { + * Unescapes a string that has been escaped for SPARQL. + * + * @param s the string to be unescaped. + * @return the unescaped string. + */ + def fromSparqlEncodedString(s: String): String = StringUtils.replaceEach( s, SparqlEscapeOutput, SparqlEscapeInput ) - } /** - * Encodes a string for use in JSON, and encloses it in quotation marks. - * - * @param s the string to be encoded. - * @return the encoded string. - */ - def toJsonEncodedString(s: String): String = { + * Encodes a string for use in JSON, and encloses it in quotation marks. + * + * @param s the string to be encoded. + * @return the encoded string. + */ + def toJsonEncodedString(s: String): String = JsString(s).compactPrint - } /** - * Parses an object of `salsah-gui:guiAttributeDefinition`. - * - * @param s the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string is invalid. - * @return a [[SalsahGuiAttributeDefinition]]. - */ - def toSalsahGuiAttributeDefinition(s: String, errorFun: => Nothing): SalsahGuiAttributeDefinition = { + * Parses an object of `salsah-gui:guiAttributeDefinition`. + * + * @param s the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string is invalid. + * @return a [[SalsahGuiAttributeDefinition]]. + */ + def toSalsahGuiAttributeDefinition(s: String, errorFun: => Nothing): SalsahGuiAttributeDefinition = s match { case SalsahGuiAttributeDefinitionRegex(attributeName, required, allowedTypeStr, _, enumeratedValuesStr) => val allowedType: SalsahGui.SalsahGuiAttributeType.Value = @@ -1871,19 +1863,20 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => errorFun } - } /** - * Parses an object of `salsah-gui:guiAttribute`. - * - * @param s the string to be parsed. - * @param attributeDefs the values of `salsah-gui:guiAttributeDefinition` for the property. - * @param errorFun a function that throws an exception. It will be called if the string is invalid. - * @return a [[SalsahGuiAttribute]]. - */ - def toSalsahGuiAttribute(s: String, - attributeDefs: Set[SalsahGuiAttributeDefinition], - errorFun: => Nothing): SalsahGuiAttribute = { + * Parses an object of `salsah-gui:guiAttribute`. + * + * @param s the string to be parsed. + * @param attributeDefs the values of `salsah-gui:guiAttributeDefinition` for the property. + * @param errorFun a function that throws an exception. It will be called if the string is invalid. + * @return a [[SalsahGuiAttribute]]. + */ + def toSalsahGuiAttribute( + s: String, + attributeDefs: Set[SalsahGuiAttributeDefinition], + errorFun: => Nothing + ): SalsahGuiAttribute = // Try to parse the expression using a regex. s match { case SalsahGuiAttributeRegex(attributeName: String, attributeValue: String) => @@ -1937,32 +1930,30 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No // The expression couldn't be parsed. errorFun } - } /** - * Parses an `xsd:dateTimeStamp`. - * - * @param s the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return an [[Instant]]. - */ - def xsdDateTimeStampToInstant(s: String, errorFun: => Nothing): Instant = { + * Parses an `xsd:dateTimeStamp`. + * + * @param s the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return an [[Instant]]. + */ + def xsdDateTimeStampToInstant(s: String, errorFun: => Nothing): Instant = try { val accessor: TemporalAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(s) Instant.from(accessor) } catch { case _: Exception => errorFun } - } /** - * Parses a Knora ARK timestamp. - * - * @param timestampStr the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return an [[Instant]]. - */ - def arkTimestampToInstant(timestampStr: String, errorFun: => Nothing): Instant = { + * Parses a Knora ARK timestamp. + * + * @param timestampStr the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return an [[Instant]]. + */ + def arkTimestampToInstant(timestampStr: String, errorFun: => Nothing): Instant = timestampStr match { case ArkTimestampRegex(year, month, day, hour, minute, second, fraction) => val nanoOfSecond: Int = Option(fraction) match { @@ -1993,14 +1984,13 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => errorFun } - } /** - * Formats a Knora ARK timestamp. - * - * @param timestamp the timestamp to be formatted. - * @return a string representation of the timestamp. - */ + * Formats a Knora ARK timestamp. + * + * @param timestamp the timestamp to be formatted. + * @return a string representation of the timestamp. + */ def formatArkTimestamp(timestamp: Instant): String = { val offsetDateTime: OffsetDateTime = timestamp.atOffset(ZoneOffset.UTC) @@ -2024,47 +2014,44 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Checks that a geometry string contains valid JSON. - * - * @param s a geometry string. - * @param errorFun a function that throws an exception. It will be called if the string does not contain valid - * JSON. - * @return the same string. - */ - def validateGeometryString(s: String, errorFun: => Nothing): String = { + * Checks that a geometry string contains valid JSON. + * + * @param s a geometry string. + * @param errorFun a function that throws an exception. It will be called if the string does not contain valid + * JSON. + * @return the same string. + */ + def validateGeometryString(s: String, errorFun: => Nothing): String = // TODO: For now, we just make sure that the string is valid JSON. We should stop storing JSON in the triplestore, and represent geometry in RDF instead (issue 169). - try { JsonParser(s) s } catch { case _: Exception => errorFun } - } /** - * Checks that a hexadecimal color code string is valid. - * - * @param s a string containing a hexadecimal color code. - * @param errorFun a function that throws an exception. It will be called if the string does not contain a valid - * hexadecimal color code. - * @return the same string. - */ - def validateColor(s: String, errorFun: => Nothing): String = { + * Checks that a hexadecimal color code string is valid. + * + * @param s a string containing a hexadecimal color code. + * @param errorFun a function that throws an exception. It will be called if the string does not contain a valid + * hexadecimal color code. + * @return the same string. + */ + def validateColor(s: String, errorFun: => Nothing): String = ColorRegex.findFirstIn(s) match { case Some(dateStr) => dateStr case None => errorFun // not a valid color hex value string } - } /** - * Checks that the format of a Knora date string is valid. - * - * @param s a Knora date string. - * @param errorFun a function that throws an exception. It will be called if the date's format is invalid. - * @return the same string. - */ - def validateDate(s: String, errorFun: => Nothing): String = { + * Checks that the format of a Knora date string is valid. + * + * @param s a Knora date string. + * @param errorFun a function that throws an exception. It will be called if the date's format is invalid. + * @return the same string. + */ + def validateDate(s: String, errorFun: => Nothing): String = // if the pattern doesn't match (=> None), the date string is formally invalid // Please note that this is a mere formal validation, // the actual validity check is done in `DateUtilV1.dateString2DateRange` @@ -2072,111 +2059,103 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case Some(value) => value case None => errorFun // calling this function throws an error } - } /** - * Checks that a string contains a valid boolean value. - * - * @param s a string containing a boolean value. - * @param errorFun a function that throws an exception. It will be called if the string does not contain - * a boolean value. - * @return the boolean value of the string. - */ - def validateBoolean(s: String, errorFun: => Nothing): Boolean = { + * Checks that a string contains a valid boolean value. + * + * @param s a string containing a boolean value. + * @param errorFun a function that throws an exception. It will be called if the string does not contain + * a boolean value. + * @return the boolean value of the string. + */ + def validateBoolean(s: String, errorFun: => Nothing): Boolean = try { s.toBoolean } catch { case _: Exception => errorFun // value could not be converted to Boolean } - } /** - * Map over all standoff tags to collect IRIs that are referred to by linking standoff tags. - * - * @param standoffTags The list of [[StandoffTagV2]]. - * @return a set of Iris referred to in the [[StandoffTagV2]]. - */ - def getResourceIrisFromStandoffTags(standoffTags: Seq[StandoffTagV2]): Set[IRI] = { - standoffTags.foldLeft(Set.empty[IRI]) { - case (acc: Set[IRI], standoffNode: StandoffTagV2) => - if (standoffNode.dataType.contains(StandoffDataTypeClasses.StandoffLinkTag)) { - val maybeTargetIri: Option[IRI] = standoffNode.attributes.collectFirst { - case iriTagAttr: StandoffTagIriAttributeV2 - if iriTagAttr.standoffPropertyIri.toString == OntologyConstants.KnoraBase.StandoffTagHasLink => - iriTagAttr.value - } - - acc + maybeTargetIri.getOrElse(throw NotFoundException(s"No link found in $standoffNode")) - } else { - acc + * Map over all standoff tags to collect IRIs that are referred to by linking standoff tags. + * + * @param standoffTags The list of [[StandoffTagV2]]. + * @return a set of Iris referred to in the [[StandoffTagV2]]. + */ + def getResourceIrisFromStandoffTags(standoffTags: Seq[StandoffTagV2]): Set[IRI] = + standoffTags.foldLeft(Set.empty[IRI]) { case (acc: Set[IRI], standoffNode: StandoffTagV2) => + if (standoffNode.dataType.contains(StandoffDataTypeClasses.StandoffLinkTag)) { + val maybeTargetIri: Option[IRI] = standoffNode.attributes.collectFirst { + case iriTagAttr: StandoffTagIriAttributeV2 + if iriTagAttr.standoffPropertyIri.toString == OntologyConstants.KnoraBase.StandoffTagHasLink => + iriTagAttr.value } + + acc + maybeTargetIri.getOrElse(throw NotFoundException(s"No link found in $standoffNode")) + } else { + acc + } } - } /** - * Turn a possibly empty string value into a boolean value. - * Returns false if the value is empty or if the given string is cannot be converted to a Boolean `true`. - * - * @param maybe an optional string representation of a boolean value. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed - * as a boolean value. - * @return a Boolean. - */ - def optionStringToBoolean(maybe: Option[String], errorFun: => Nothing): Boolean = { + * Turn a possibly empty string value into a boolean value. + * Returns false if the value is empty or if the given string is cannot be converted to a Boolean `true`. + * + * @param maybe an optional string representation of a boolean value. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed + * as a boolean value. + * @return a Boolean. + */ + def optionStringToBoolean(maybe: Option[String], errorFun: => Nothing): Boolean = try { maybe.exists(_.toBoolean) } catch { case _: IllegalArgumentException => errorFun } - } /** - * Converts a string to a boolean. - * - * @param s the string to be converted. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed - * as a boolean value. - * @return a Boolean. - */ - def toBoolean(s: String, errorFun: => Nothing): Boolean = { + * Converts a string to a boolean. + * + * @param s the string to be converted. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed + * as a boolean value. + * @return a Boolean. + */ + def toBoolean(s: String, errorFun: => Nothing): Boolean = try { s.toBoolean } catch { case _: IllegalArgumentException => errorFun } - } /** - * Checks that a string is a valid XML [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. - * - * @param ncName the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string is invalid. - * @return the same string. - */ - def validateNCName(ncName: String, errorFun: => Nothing): String = { + * Checks that a string is a valid XML [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. + * + * @param ncName the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string is invalid. + * @return the same string. + */ + def validateNCName(ncName: String, errorFun: => Nothing): String = NCNameRegex.findFirstIn(ncName) match { case Some(value) => value case None => errorFun } - } /** - * Returns `true` if an ontology name is reserved for a built-in ontology. - * - * @param ontologyName the ontology name to be checked. - * @return `true` if the ontology name is reserved for a built-in ontology. - */ - def isBuiltInOntologyName(ontologyName: String): Boolean = { + * Returns `true` if an ontology name is reserved for a built-in ontology. + * + * @param ontologyName the ontology name to be checked. + * @return `true` if the ontology name is reserved for a built-in ontology. + */ + def isBuiltInOntologyName(ontologyName: String): Boolean = OntologyConstants.BuiltInOntologyLabels.contains(ontologyName) - } /** - * Checks that a name is valid as a project-specific ontology name. - * - * @param ontologyName the ontology name to be checked. - * @param errorFun a function that throws an exception. It will be called if the name is invalid. - * @return the same ontology name. - */ + * Checks that a name is valid as a project-specific ontology name. + * + * @param ontologyName the ontology name to be checked. + * @param errorFun a function that throws an exception. It will be called if the name is invalid. + * @return the same ontology name. + */ def validateProjectSpecificOntologyName(ontologyName: String, errorFun: => Nothing): String = { // Check that ontology name matched NCName regex pattern ontologyName match { @@ -2211,16 +2190,18 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Given a valid internal (built-in or project-specific) ontology name and an optional project code, constructs the - * corresponding internal ontology IRI. - * - * @param internalOntologyName the ontology name. - * @param projectCode the project code. - * @return the ontology IRI. - */ - private def makeInternalOntologyIriStr(internalOntologyName: String, - isShared: Boolean, - projectCode: Option[String]): IRI = { + * Given a valid internal (built-in or project-specific) ontology name and an optional project code, constructs the + * corresponding internal ontology IRI. + * + * @param internalOntologyName the ontology name. + * @param projectCode the project code. + * @return the ontology IRI. + */ + private def makeInternalOntologyIriStr( + internalOntologyName: String, + isShared: Boolean, + projectCode: Option[String] + ): IRI = { val internalOntologyIri = new StringBuilder(OntologyConstants.KnoraInternal.InternalOntologyStart) if (isShared) { @@ -2240,60 +2221,60 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Given a valid internal ontology name and an optional project code, constructs the corresponding internal - * ontology IRI. - * - * @param internalOntologyName the ontology name. - * @param projectCode the project code. - * @return the ontology IRI. - */ - def makeProjectSpecificInternalOntologyIri(internalOntologyName: String, - isShared: Boolean, - projectCode: String): SmartIri = { + * Given a valid internal ontology name and an optional project code, constructs the corresponding internal + * ontology IRI. + * + * @param internalOntologyName the ontology name. + * @param projectCode the project code. + * @return the ontology IRI. + */ + def makeProjectSpecificInternalOntologyIri( + internalOntologyName: String, + isShared: Boolean, + projectCode: String + ): SmartIri = toSmartIri(makeInternalOntologyIriStr(internalOntologyName, isShared, Some(projectCode))) - } /** - * Converts an internal ontology name to an external ontology name. This only affects `knora-base`, whose - * external equivalent is `knora-api.` - * - * @param ontologyName an internal ontology name. - * @return the corresponding external ontology name. - */ - private def internalToExternalOntologyName(ontologyName: String): String = { + * Converts an internal ontology name to an external ontology name. This only affects `knora-base`, whose + * external equivalent is `knora-api.` + * + * @param ontologyName an internal ontology name. + * @return the corresponding external ontology name. + */ + private def internalToExternalOntologyName(ontologyName: String): String = if (ontologyName == OntologyConstants.KnoraBase.KnoraBaseOntologyLabel) { OntologyConstants.KnoraApi.KnoraApiOntologyLabel } else { ontologyName } - } /** - * Converts an external ontology name to an internal ontology name. This only affects `knora-api`, whose - * internal equivalent is `knora-base.` - * - * @param ontologyName an external ontology name. - * @return the corresponding internal ontology name. - */ - private def externalToInternalOntologyName(ontologyName: String): String = { + * Converts an external ontology name to an internal ontology name. This only affects `knora-api`, whose + * internal equivalent is `knora-base.` + * + * @param ontologyName an external ontology name. + * @return the corresponding internal ontology name. + */ + private def externalToInternalOntologyName(ontologyName: String): String = if (ontologyName == OntologyConstants.KnoraApi.KnoraApiOntologyLabel) { OntologyConstants.KnoraBase.KnoraBaseOntologyLabel } else { ontologyName } - } /** - * Converts the IRI of a project-specific internal ontology (used in the triplestore) to an XML prefix label and - * namespace for use in data import. - * - * @param internalOntologyIri the IRI of the project-specific internal ontology. Any trailing # character will be - * stripped before the conversion. - * @return the corresponding XML prefix label and import namespace. - */ + * Converts the IRI of a project-specific internal ontology (used in the triplestore) to an XML prefix label and + * namespace for use in data import. + * + * @param internalOntologyIri the IRI of the project-specific internal ontology. Any trailing # character will be + * stripped before the conversion. + * @return the corresponding XML prefix label and import namespace. + */ def internalOntologyIriToXmlNamespaceInfoV1(internalOntologyIri: SmartIri): XmlImportNamespaceInfoV1 = { val namespace = new StringBuilder( - OntologyConstants.KnoraXmlImportV1.ProjectSpecificXmlImportNamespace.XmlImportNamespaceStart) + OntologyConstants.KnoraXmlImportV1.ProjectSpecificXmlImportNamespace.XmlImportNamespaceStart + ) if (internalOntologyIri.isKnoraSharedDefinitionIri) { namespace.append("shared/") @@ -2315,15 +2296,15 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Converts an XML namespace (used in XML data import) to the IRI of a project-specific internal ontology (used - * in the triplestore). The resulting IRI will not end in a # character. - * - * @param namespace the XML namespace. - * @param errorFun a function that throws an exception. It will be called if the form of the string is not - * valid for a Knora XML import namespace. - * @return the corresponding project-specific internal ontology IRI. - */ - def xmlImportNamespaceToInternalOntologyIriV1(namespace: String, errorFun: => Nothing): SmartIri = { + * Converts an XML namespace (used in XML data import) to the IRI of a project-specific internal ontology (used + * in the triplestore). The resulting IRI will not end in a # character. + * + * @param namespace the XML namespace. + * @param errorFun a function that throws an exception. It will be called if the form of the string is not + * valid for a Knora XML import namespace. + * @return the corresponding project-specific internal ontology IRI. + */ + def xmlImportNamespaceToInternalOntologyIriV1(namespace: String, errorFun: => Nothing): SmartIri = namespace match { case ProjectSpecificXmlImportNamespaceRegex(shared, _, projectCode, ontologyName) if !isBuiltInOntologyName(ontologyName) => @@ -2342,34 +2323,35 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => errorFun } - } /** - * Converts a XML element name in a particular namespace (used in XML data import) to the IRI of a - * project-specific internal ontology entity (used in the triplestore). - * - * @param namespace the XML namespace. - * @param elementLabel the XML element label. - * @param errorFun a function that throws an exception. It will be called if the form of the namespace is not - * valid for a Knora XML import namespace. - * @return the corresponding project-specific internal ontology entity IRI. - */ - def xmlImportElementNameToInternalOntologyIriV1(namespace: String, - elementLabel: String, - errorFun: => Nothing): IRI = { + * Converts a XML element name in a particular namespace (used in XML data import) to the IRI of a + * project-specific internal ontology entity (used in the triplestore). + * + * @param namespace the XML namespace. + * @param elementLabel the XML element label. + * @param errorFun a function that throws an exception. It will be called if the form of the namespace is not + * valid for a Knora XML import namespace. + * @return the corresponding project-specific internal ontology entity IRI. + */ + def xmlImportElementNameToInternalOntologyIriV1( + namespace: String, + elementLabel: String, + errorFun: => Nothing + ): IRI = { val ontologyIri = xmlImportNamespaceToInternalOntologyIriV1(namespace, errorFun) ontologyIri + "#" + elementLabel } /** - * In XML import data, a property from another ontology is referred to as `prefixLabel__localName`. The prefix label - * may start with a project ID (prefixed with 'p') and a hyphen. This function attempts to parse a property name in - * that format. - * - * @param prefixLabelAndLocalName a string that may refer to a property in the format `prefixLabel__localName`. - * @return if successful, a `Some` containing the entity's internal IRI, otherwise `None`. - */ - def toPropertyIriFromOtherOntologyInXmlImport(prefixLabelAndLocalName: String): Option[IRI] = { + * In XML import data, a property from another ontology is referred to as `prefixLabel__localName`. The prefix label + * may start with a project ID (prefixed with 'p') and a hyphen. This function attempts to parse a property name in + * that format. + * + * @param prefixLabelAndLocalName a string that may refer to a property in the format `prefixLabel__localName`. + * @return if successful, a `Some` containing the entity's internal IRI, otherwise `None`. + */ + def toPropertyIriFromOtherOntologyInXmlImport(prefixLabelAndLocalName: String): Option[IRI] = prefixLabelAndLocalName match { case PropertyFromOtherOntologyInXmlImportRegex(_, projectID, prefixLabel, localName) => Option(projectID) match { @@ -2380,7 +2362,8 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No Some(s"${OntologyConstants.KnoraInternal.InternalOntologyStart}/shared/$prefixLabel#$localName") } else { Some( - s"${OntologyConstants.KnoraInternal.InternalOntologyStart}/$definedProjectID/$prefixLabel#$localName") + s"${OntologyConstants.KnoraInternal.InternalOntologyStart}/$definedProjectID/$prefixLabel#$localName" + ) } case None => @@ -2393,103 +2376,95 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No case _ => None } - } /** - * Determines whether a URL path refers to a built-in API v2 ontology (simple or complex). - * - * @param urlPath the URL path. - * @return true if the path refers to a built-in API v2 ontology. - */ - def isBuiltInApiV2OntologyUrlPath(urlPath: String): Boolean = { + * Determines whether a URL path refers to a built-in API v2 ontology (simple or complex). + * + * @param urlPath the URL path. + * @return true if the path refers to a built-in API v2 ontology. + */ + def isBuiltInApiV2OntologyUrlPath(urlPath: String): Boolean = urlPath match { case ApiV2OntologyUrlPathRegex(_, _, ontologyName, _) if isBuiltInOntologyName(ontologyName) => true case _ => false } - } /** - * Determines whether a URL path refers to a project-specific API v2 ontology (simple or complex). - * - * @param urlPath the URL path. - * @return true if the path refers to a project-specific API v2 ontology. - */ - def isProjectSpecificApiV2OntologyUrlPath(urlPath: String): Boolean = { + * Determines whether a URL path refers to a project-specific API v2 ontology (simple or complex). + * + * @param urlPath the URL path. + * @return true if the path refers to a project-specific API v2 ontology. + */ + def isProjectSpecificApiV2OntologyUrlPath(urlPath: String): Boolean = urlPath match { case ApiV2OntologyUrlPathRegex(_, _, ontologyName, _) if !isBuiltInOntologyName(ontologyName) => true case _ => false } - } /** - * Given the projectInfo calculates the project's data named graph. - * - * @param projectInfo the project's [[ProjectInfoV1]]. - * @return the IRI of the project's data named graph. - */ - def projectDataNamedGraphV1(projectInfo: ProjectInfoV1): IRI = { + * Given the projectInfo calculates the project's data named graph. + * + * @param projectInfo the project's [[ProjectInfoV1]]. + * @return the IRI of the project's data named graph. + */ + def projectDataNamedGraphV1(projectInfo: ProjectInfoV1): IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/" + projectInfo.shortcode + "/" + projectInfo.shortname - } /** - * Given the [[ProjectADM]] calculates the project's data named graph. - * - * @param project the project's [[ProjectADM]]. - * @return the IRI of the project's data named graph. - */ - def projectDataNamedGraphV2(project: ProjectADM): IRI = { + * Given the [[ProjectADM]] calculates the project's data named graph. + * + * @param project the project's [[ProjectADM]]. + * @return the IRI of the project's data named graph. + */ + def projectDataNamedGraphV2(project: ProjectADM): IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/" + project.shortcode + "/" + project.shortname - } /** - * Given the [[ProjectADM]] calculates the project's metadata named graph. - * - * @param project the project's [[ProjectADM]]. - * @return the IRI of the project's metadata named graph. - */ - def projectMetadataNamedGraphV2(project: ProjectADM): IRI = { + * Given the [[ProjectADM]] calculates the project's metadata named graph. + * + * @param project the project's [[ProjectADM]]. + * @return the IRI of the project's metadata named graph. + */ + def projectMetadataNamedGraphV2(project: ProjectADM): IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/" + project.shortcode + "/" + project.shortname + "/metadata" - } /** - * Check that the supplied IRI represents a valid project IRI. - * - * @param iri the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project IRI. - * @return the same string but escaped. - */ - def validateAndEscapeProjectIri(iri: IRI, errorFun: => Nothing): IRI = { + * Check that the supplied IRI represents a valid project IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project IRI. + * @return the same string but escaped. + */ + def validateAndEscapeProjectIri(iri: IRI, errorFun: => Nothing): IRI = if (isKnoraProjectIriStr(iri)) { toSparqlEncodedString(iri, errorFun) } else { errorFun } - } /** - * Check that an optional string represents a valid project IRI. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project IRI. - * @return the same optional string but escaped. - */ - def validateAndEscapeOptionalProjectIri(maybeString: Option[String], errorFun: => Nothing): Option[IRI] = { + * Check that an optional string represents a valid project IRI. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project IRI. + * @return the same optional string but escaped. + */ + def validateAndEscapeOptionalProjectIri(maybeString: Option[String], errorFun: => Nothing): Option[IRI] = maybeString match { case Some(s) => Some(validateAndEscapeProjectIri(s, errorFun)) case None => None } - } /** - * Check that the string represents a valid project shortname. - * - * @param value the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortname. - * @return the same string. - */ + * Check that the string represents a valid project shortname. + * + * @param value the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortname. + * @return the same string. + */ def validateAndEscapeProjectShortname(shortname: String, errorFun: => Nothing): String = { // Check that shortname matches NCName pattern val ncNameMatch = NCNameRegex.findFirstIn(shortname) match { @@ -2504,329 +2479,309 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Check that an optional string represents a valid project shortname. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortname. - * @return the same optional string. - */ - def validateAndEscapeOptionalProjectShortname(maybeString: Option[String], errorFun: => Nothing): Option[String] = { + * Check that an optional string represents a valid project shortname. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortname. + * @return the same optional string. + */ + def validateAndEscapeOptionalProjectShortname(maybeString: Option[String], errorFun: => Nothing): Option[String] = maybeString match { case Some(s) => Some(validateAndEscapeProjectShortname(s, errorFun)) case None => None } - } /** - * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. - * - * @param shortcode the project's shortcode. - * @return the shortcode in upper case. - */ - def validateProjectShortcode(shortcode: String, errorFun: => Nothing): String = { + * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. + * + * @param shortcode the project's shortcode. + * @return the shortcode in upper case. + */ + def validateProjectShortcode(shortcode: String, errorFun: => Nothing): String = ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { case Some(value) => value case None => errorFun } - } /** - * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. - * - * @param shortcode the project's shortcode. - * @return the shortcode in upper case. - */ - def validateProjectShortcodeOption(shortcode: String): Option[String] = { + * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. + * + * @param shortcode the project's shortcode. + * @return the shortcode in upper case. + */ + def validateProjectShortcodeOption(shortcode: String): Option[String] = ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { case Some(value) => Some(value) case None => None } - } /** - * Check that a string represents a valid project shortcode. - * - * @param shortcode the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortcode. - * @return the same string. - */ - def validateAndEscapeProjectShortcode(shortcode: String, errorFun: => Nothing): String = { + * Check that a string represents a valid project shortcode. + * + * @param shortcode the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortcode. + * @return the same string. + */ + def validateAndEscapeProjectShortcode(shortcode: String, errorFun: => Nothing): String = ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { case Some(definedShortcode) => toSparqlEncodedString(definedShortcode, errorFun) case None => errorFun } - } /** - * Check that an optional string represents a valid project shortcode. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortcode. - * @return the same optional string. - */ - def validateAndEscapeOptionalProjectShortcode(maybeString: Option[String], errorFun: => Nothing): Option[String] = { + * Check that an optional string represents a valid project shortcode. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortcode. + * @return the same optional string. + */ + def validateAndEscapeOptionalProjectShortcode(maybeString: Option[String], errorFun: => Nothing): Option[String] = maybeString match { case Some(s) => Some(validateAndEscapeProjectShortcode(s, errorFun)) case None => None } - } - def escapeOptionalString(maybeString: Option[String], errorFun: => Nothing): Option[String] = { + def escapeOptionalString(maybeString: Option[String], errorFun: => Nothing): Option[String] = maybeString match { case Some(s) => Some(toSparqlEncodedString(s, errorFun)) case None => None } - } /** - * Given the list IRI, checks if it is in a valid format. - * - * @param iri the list's IRI. - * @return the IRI of the list. - */ - def validateListIri(iri: IRI, errorFun: => Nothing): IRI = { + * Given the list IRI, checks if it is in a valid format. + * + * @param iri the list's IRI. + * @return the IRI of the list. + */ + def validateListIri(iri: IRI, errorFun: => Nothing): IRI = if (isKnoraListIriStr(iri)) { iri } else { errorFun } - } /** - * Given the optional list IRI, checks if it is in a valid format. - * - * @param maybeIri the optional list's IRI to be checked. - * @return the same optional IRI. - */ - def validateOptionalListIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = { + * Given the optional list IRI, checks if it is in a valid format. + * + * @param maybeIri the optional list's IRI to be checked. + * @return the same optional IRI. + */ + def validateOptionalListIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = maybeIri match { case Some(iri) => Some(validateListIri(iri, errorFun)) case None => None } - } /** - * Given the group IRI, checks if it is in a valid format. - * - * @param iri the group's IRI. - * @return the IRI of the list. - */ - def validateGroupIri(iri: IRI, errorFun: => Nothing): IRI = { + * Given the group IRI, checks if it is in a valid format. + * + * @param iri the group's IRI. + * @return the IRI of the list. + */ + def validateGroupIri(iri: IRI, errorFun: => Nothing): IRI = if (isKnoraGroupIriStr(iri)) { iri } else { errorFun } - } /** - * Given the optional group IRI, checks if it is in a valid format. - * - * @param maybeIri the optional group's IRI to be checked. - * @return the same optional IRI. - */ - def validateOptionalGroupIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = { + * Given the optional group IRI, checks if it is in a valid format. + * + * @param maybeIri the optional group's IRI to be checked. + * @return the same optional IRI. + */ + def validateOptionalGroupIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = maybeIri match { case Some(iri) => Some(validateGroupIri(iri, errorFun)) case None => None } - } /** - * Given the permission IRI, checks if it is in a valid format. - * - * @param iri the permission's IRI. - * @return the IRI of the list. - */ - def validatePermissionIri(iri: IRI, errorFun: => Nothing): IRI = { + * Given the permission IRI, checks if it is in a valid format. + * + * @param iri the permission's IRI. + * @return the IRI of the list. + */ + def validatePermissionIri(iri: IRI, errorFun: => Nothing): IRI = if (isKnoraPermissionIriStr(iri)) { iri } else { errorFun } - } /** - * Given the optional permission IRI, checks if it is in a valid format. - * - * @param maybeIri the optional permission's IRI to be checked. - * @return the same optional IRI. - */ - def validateOptionalPermissionIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = { + * Given the optional permission IRI, checks if it is in a valid format. + * + * @param maybeIri the optional permission's IRI to be checked. + * @return the same optional IRI. + */ + def validateOptionalPermissionIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = maybeIri match { case Some(iri) => Some(validatePermissionIri(iri, errorFun)) case None => None } - } /** - * Check that the supplied IRI represents a valid user IRI. - * - * @param iri the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * user IRI. - * @return the same string. - */ - def validateUserIri(iri: IRI, errorFun: => Nothing): IRI = { + * Check that the supplied IRI represents a valid user IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same string. + */ + def validateUserIri(iri: IRI, errorFun: => Nothing): IRI = if (isKnoraUserIriStr(iri)) { iri } else { errorFun } - } /** - * Given the optional user IRI, checks if it is in a valid format. - * - * @param maybeIri the optional user's IRI to be checked. - * @return the same optional IRI. - */ - def validateOptionalUserIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = { + * Given the optional user IRI, checks if it is in a valid format. + * + * @param maybeIri the optional user's IRI to be checked. + * @return the same optional IRI. + */ + def validateOptionalUserIri(maybeIri: Option[IRI], errorFun: => Nothing): Option[IRI] = maybeIri match { case Some(iri) => Some(validateUserIri(iri, errorFun)) case None => None } - } /** - * Check that the supplied IRI represents a valid user IRI. - * - * @param iri the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * user IRI. - * @return the same string but escaped. - */ - def validateAndEscapeUserIri(iri: IRI, errorFun: => Nothing): String = { + * Check that the supplied IRI represents a valid user IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same string but escaped. + */ + def validateAndEscapeUserIri(iri: IRI, errorFun: => Nothing): String = if (isKnoraUserIriStr(iri)) { toSparqlEncodedString(iri, errorFun) } else { errorFun } - } /** - * Check that an optional string represents a valid user IRI. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * user IRI. - * @return the same optional string. - */ - def validateAndEscapeOptionalUserIri(maybeString: Option[String], errorFun: => Nothing): Option[String] = { + * Check that an optional string represents a valid user IRI. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same optional string. + */ + def validateAndEscapeOptionalUserIri(maybeString: Option[String], errorFun: => Nothing): Option[String] = maybeString match { case Some(s) => Some(validateAndEscapeUserIri(s, errorFun)) case None => None } - } /** - * Given an email address, checks if it is in a valid format. - * - * @param email the email. - * @return the email - */ - def validateEmail(email: String): Option[String] = { + * Given an email address, checks if it is in a valid format. + * + * @param email the email. + * @return the email + */ + def validateEmail(email: String): Option[String] = EmailAddressRegex.findFirstIn(email) - } /** - * Given an email address, checks if it is in a valid format. - * - * @param email the email. - * @return the email - */ - def validateEmailAndThrow(email: String, errorFun: => Nothing): String = { + * Given an email address, checks if it is in a valid format. + * + * @param email the email. + * @return the email + */ + def validateEmailAndThrow(email: String, errorFun: => Nothing): String = EmailAddressRegex.findFirstIn(email) match { case Some(value) => value case None => errorFun } - } /** - * Check that an optional string represents a valid email address. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * email address. - * @return the same optional string. - */ - def validateAndEscapeOptionalEmail(maybeString: Option[String], errorFun: => Nothing): Option[String] = { + * Check that an optional string represents a valid email address. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * email address. + * @return the same optional string. + */ + def validateAndEscapeOptionalEmail(maybeString: Option[String], errorFun: => Nothing): Option[String] = maybeString match { case Some(s) => Some(toSparqlEncodedString(validateEmailAndThrow(s, errorFun), errorFun)) case None => None } - } /** - * Check that the string represents a valid username. - * - * @param value the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * username. - * @return the same string. - */ - def validateUsername(value: String, errorFun: => Nothing): String = { + * Check that the string represents a valid username. + * + * @param value the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * username. + * @return the same string. + */ + def validateUsername(value: String, errorFun: => Nothing): String = UsernameRegex.findFirstIn(value) match { case Some(username) => username case None => errorFun } - } /** - * Check that the string represents a valid username and escape any special characters. - * - * @param value the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * username. - * @return the same string with escaped special characters. - */ - def validateAndEscapeUsername(value: String, errorFun: => Nothing): String = { + * Check that the string represents a valid username and escape any special characters. + * + * @param value the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * username. + * @return the same string with escaped special characters. + */ + def validateAndEscapeUsername(value: String, errorFun: => Nothing): String = UsernameRegex.findFirstIn(value) match { case Some(username) => toSparqlEncodedString(username, errorFun) case None => errorFun } - } /** - * Check that an optional string represents a valid username. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * username. - * @return the same optional string. - */ - def validateAndEscapeOptionalUsername(maybeString: Option[String], errorFun: => Nothing): Option[String] = { + * Check that an optional string represents a valid username. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * username. + * @return the same optional string. + */ + def validateAndEscapeOptionalUsername(maybeString: Option[String], errorFun: => Nothing): Option[String] = maybeString match { case Some(s) => Some(validateAndEscapeUsername(s, errorFun)) case None => None } - } /** - * Generates an ARK URL for a resource or value, as per [[https://tools.ietf.org/html/draft-kunze-ark-18]]. - * - * @param projectID the shortcode of the project that the resource belongs to. - * @param resourceID the resource's ID (the last component of its IRI). - * @param maybeValueUUID if this is an ARK URL for a value, the value's UUID. - * @param maybeTimestamp a timestamp indicating the point in the resource's version history that the ARK URL should - * cite. - * @return an ARK URL that can be resolved to obtain the resource or value. - */ - private def makeArkUrl(projectID: String, - resourceID: String, - maybeValueUUID: Option[UUID] = None, - maybeTimestamp: Option[Instant] = None): String = { + * Generates an ARK URL for a resource or value, as per [[https://tools.ietf.org/html/draft-kunze-ark-18]]. + * + * @param projectID the shortcode of the project that the resource belongs to. + * @param resourceID the resource's ID (the last component of its IRI). + * @param maybeValueUUID if this is an ARK URL for a value, the value's UUID. + * @param maybeTimestamp a timestamp indicating the point in the resource's version history that the ARK URL should + * cite. + * @return an ARK URL that can be resolved to obtain the resource or value. + */ + private def makeArkUrl( + projectID: String, + resourceID: String, + maybeValueUUID: Option[UUID] = None, + maybeTimestamp: Option[Instant] = None + ): String = { /** - * Adds a check digit to a Base64-encoded ID, and escapes '-' as '=', because '-' can be ignored in ARK URLs. - * - * @param id a Base64-encoded ID. - * @return the ID with a check digit added. - */ + * Adds a check digit to a Base64-encoded ID, and escapes '-' as '=', because '-' can be ignored in ARK URLs. + * + * @param id a Base64-encoded ID. + * @return the ID with a check digit added. + */ def addCheckDigitAndEscape(id: String): String = { val checkDigitTry: Try[String] = Try { base64UrlCheckDigit.calculate(id) @@ -2869,55 +2824,57 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Constructs a URL for accessing a file that has been uploaded to Sipi's temporary storage. - * - * @param settings the application settings. - * @param filename the filename. - * @return a URL for accessing the file. - */ - def makeSipiTempFileUrl(settings: KnoraSettingsImpl, filename: String): String = { + * Constructs a URL for accessing a file that has been uploaded to Sipi's temporary storage. + * + * @param settings the application settings. + * @param filename the filename. + * @return a URL for accessing the file. + */ + def makeSipiTempFileUrl(settings: KnoraSettingsImpl, filename: String): String = s"${settings.internalSipiBaseUrl}/tmp/$filename" - } /** - * Checks whether an IRI already exists in the triplestore. - * - * @param iri the IRI to be checked. - * @param storeManager a reference to the store manager. - * @return `true` if the IRI already exists, `false` otherwise. - */ - def checkIriExists(iri: IRI, storeManager: ActorRef)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Boolean] = { + * Checks whether an IRI already exists in the triplestore. + * + * @param iri the IRI to be checked. + * @param storeManager a reference to the store manager. + * @return `true` if the IRI already exists, `false` otherwise. + */ + def checkIriExists(iri: IRI, storeManager: ActorRef)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[Boolean] = for { askString <- Future(org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkIriExists(iri).toString) response <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] } yield response.result - } /** - * Attempts to create a new IRI that isn't already used in the triplestore. Will try up to [[MAX_IRI_ATTEMPTS]] - * times, then throw an exception if an unused IRI could not be created. - * - * @param iriFun a function that generates a random IRI. - * @param storeManager a reference to the Knora store manager actor. - */ - def makeUnusedIri(iriFun: => IRI, storeManager: ActorRef, log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[IRI] = { + * Attempts to create a new IRI that isn't already used in the triplestore. Will try up to [[MAX_IRI_ATTEMPTS]] + * times, then throw an exception if an unused IRI could not be created. + * + * @param iriFun a function that generates a random IRI. + * @param storeManager a reference to the Knora store manager actor. + */ + def makeUnusedIri(iriFun: => IRI, storeManager: ActorRef, log: LoggingAdapter)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[IRI] = { def makeUnusedIriRec(attempts: Int): Future[IRI] = { val newIri = iriFun for { iriExists <- checkIriExists(newIri, storeManager) - result <- if (!iriExists) { - FastFuture.successful(newIri) - } else if (attempts > 1) { - log.warning("KnoraIdUtil.makeUnusedIri generated an IRI that already exists in the triplestore, retrying") - makeUnusedIriRec(attempts - 1) - } else { - throw UpdateNotPerformedException(s"Could not make an unused new IRI after $MAX_IRI_ATTEMPTS attempts") - } + result <- + if (!iriExists) { + FastFuture.successful(newIri) + } else if (attempts > 1) { + log.warning("KnoraIdUtil.makeUnusedIri generated an IRI that already exists in the triplestore, retrying") + makeUnusedIriRec(attempts - 1) + } else { + throw UpdateNotPerformedException(s"Could not make an unused new IRI after $MAX_IRI_ATTEMPTS attempts") + } } yield result } @@ -2925,24 +2882,24 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Generates a type 4 UUID using [[java.util.UUID]], and Base64-encodes it using a URL and filename safe - * Base64 encoder from [[java.util.Base64]], without padding. This results in a 22-character string that - * can be used as a unique identifier in IRIs. - * - * @return a random, Base64-encoded UUID. - */ + * Generates a type 4 UUID using [[java.util.UUID]], and Base64-encodes it using a URL and filename safe + * Base64 encoder from [[java.util.Base64]], without padding. This results in a 22-character string that + * can be used as a unique identifier in IRIs. + * + * @return a random, Base64-encoded UUID. + */ def makeRandomBase64EncodedUuid: String = { val uuid = UUID.randomUUID base64EncodeUuid(uuid) } /** - * Base64-encodes a [[UUID]] using a URL and filename safe Base64 encoder from [[java.util.Base64]], - * without padding. This results in a 22-character string that can be used as a unique identifier in IRIs. - * - * @param uuid the [[UUID]] to be encoded. - * @return a 22-character string representing the UUID. - */ + * Base64-encodes a [[UUID]] using a URL and filename safe Base64 encoder from [[java.util.Base64]], + * without padding. This results in a 22-character string that can be used as a unique identifier in IRIs. + * + * @param uuid the [[UUID]] to be encoded. + * @return a 22-character string representing the UUID. + */ def base64EncodeUuid(uuid: UUID): String = { val bytes = Array.ofDim[Byte](16) val byteBuffer = ByteBuffer.wrap(bytes) @@ -2952,11 +2909,11 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Decodes a Base64-encoded UUID. - * - * @param base64Uuid the Base64-encoded UUID to be decoded. - * @return the equivalent [[UUID]]. - */ + * Decodes a Base64-encoded UUID. + * + * @param base64Uuid the Base64-encoded UUID to be decoded. + * @return the equivalent [[UUID]]. + */ def base64DecodeUuid(base64Uuid: String): UUID = { val bytes = base64Decoder.decode(base64Uuid) val byteBuffer = ByteBuffer.wrap(bytes) @@ -2964,12 +2921,12 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Validates and decodes a Base64-encoded UUID. - * - * @param base64Uuid the UUID to be validated. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return the decoded UUID. - */ + * Validates and decodes a Base64-encoded UUID. + * + * @param base64Uuid the UUID to be validated. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return the decoded UUID. + */ def validateBase64EncodedUuid(base64Uuid: String, errorFun: => Nothing): UUID = { val decodeTry = Try { base64DecodeUuid(base64Uuid) @@ -2982,44 +2939,42 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Encodes a [[UUID]] as a string in one of two formats: - * - * - The canonical 36-character format. - * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. - * - * @param uuid the UUID to be encoded. - * @param useBase64 if `true`, uses Base64 encoding. - * @return the encoded UUID. - */ - def encodeUuid(uuid: UUID, useBase64: Boolean): String = { + * Encodes a [[UUID]] as a string in one of two formats: + * + * - The canonical 36-character format. + * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. + * + * @param uuid the UUID to be encoded. + * @param useBase64 if `true`, uses Base64 encoding. + * @return the encoded UUID. + */ + def encodeUuid(uuid: UUID, useBase64: Boolean): String = if (useBase64) { base64EncodeUuid(uuid) } else { uuid.toString } - } /** - * Calls `decodeUuidWithErr`, throwing [[InconsistentRepositoryDataException]] if the string cannot be parsed. - */ - def decodeUuid(uuidStr: String): UUID = { + * Calls `decodeUuidWithErr`, throwing [[InconsistentRepositoryDataException]] if the string cannot be parsed. + */ + def decodeUuid(uuidStr: String): UUID = decodeUuidWithErr(uuidStr, throw InconsistentRepositoryDataException(s"Invalid UUID: $uuidStr")) - } /** - * Decodes a string representing a UUID in one of two formats: - * - * - The canonical 36-character format. - * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. - * - * Shorter strings are padded with leading zeroes to 22 characters and parsed in Base64 format - * (this is non-reversible, and is needed only for working with test data). - * - * @param uuidStr the string to be decoded. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return the decoded [[UUID]]. - */ - def decodeUuidWithErr(uuidStr: String, errorFun: => Nothing): UUID = { + * Decodes a string representing a UUID in one of two formats: + * + * - The canonical 36-character format. + * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. + * + * Shorter strings are padded with leading zeroes to 22 characters and parsed in Base64 format + * (this is non-reversible, and is needed only for working with test data). + * + * @param uuidStr the string to be decoded. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return the decoded [[UUID]]. + */ + def decodeUuidWithErr(uuidStr: String, errorFun: => Nothing): UUID = if (uuidStr.length == CanonicalUuidLength) { UUID.fromString(uuidStr) } else if (uuidStr.length == Base64UuidLength) { @@ -3029,36 +2984,34 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } else { errorFun } - } /** - * Checks if a string is the right length to be a canonical or Base64-encoded UUID. - * - * @param idStr the string to check. - * @return `true` if the string is the right length to be a canonical or Base64-encoded UUID. - */ - def couldBeUuid(idStr: String): Boolean = { + * Checks if a string is the right length to be a canonical or Base64-encoded UUID. + * + * @param idStr the string to check. + * @return `true` if the string is the right length to be a canonical or Base64-encoded UUID. + */ + def couldBeUuid(idStr: String): Boolean = idStr.length == CanonicalUuidLength || idStr.length == Base64UuidLength - } /** - * Creates a new resource IRI based on a UUID. - * - * @param projectShortcode the project's shortcode. - * @return a new resource IRI. - */ + * Creates a new resource IRI based on a UUID. + * + * @param projectShortcode the project's shortcode. + * @return a new resource IRI. + */ def makeRandomResourceIri(projectShortcode: String): IRI = { val knoraResourceID = makeRandomBase64EncodedUuid s"http://$IriDomain/$projectShortcode/$knoraResourceID" } /** - * Creates a new value IRI based on a UUID. - * - * @param resourceIri the IRI of the resource that will contain the value. - * @param givenUUID the optional given UUID of the value. If not provided, create a random one. - * @return a new value IRI. - */ + * Creates a new value IRI based on a UUID. + * + * @param resourceIri the IRI of the resource that will contain the value. + * @param givenUUID the optional given UUID of the value. If not provided, create a random one. + * @return a new value IRI. + */ def makeRandomValueIri(resourceIri: IRI, givenUUID: Option[UUID] = None): IRI = { val valueUUID = givenUUID match { case Some(uuid: UUID) => base64EncodeUuid(uuid) @@ -3068,11 +3021,11 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Creates a mapping IRI based on a project IRI and a mapping name. - * - * @param projectIri the IRI of the project the mapping will belong to. - * @return a mapping IRI. - */ + * Creates a mapping IRI based on a project IRI and a mapping name. + * + * @param projectIri the IRI of the project the mapping will belong to. + * @return a mapping IRI. + */ def makeProjectMappingIri(projectIri: IRI, mappingName: String): IRI = { val mappingIri = s"$projectIri/mappings/$mappingName" // check that the mapping IRI is valid (mappingName is user input) @@ -3080,86 +3033,83 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Creates a random IRI for an element of a mapping based on a mapping IRI. - * - * @param mappingIri the IRI of the mapping the element belongs to. - * @return a new mapping element IRI. - */ + * Creates a random IRI for an element of a mapping based on a mapping IRI. + * + * @param mappingIri the IRI of the mapping the element belongs to. + * @return a new mapping element IRI. + */ def makeRandomMappingElementIri(mappingIri: IRI): IRI = { val knoraMappingElementUuid = makeRandomBase64EncodedUuid s"$mappingIri/elements/$knoraMappingElementUuid" } /** - * Creates an IRI used as a lock for the creation of mappings inside a given project. - * This method will always return the same IRI for the given project IRI. - * - * @param projectIri the IRI of the project the mapping will belong to. - * @return an IRI used as a lock for the creation of mappings inside a given project. - */ - def createMappingLockIriForProject(projectIri: IRI): IRI = { + * Creates an IRI used as a lock for the creation of mappings inside a given project. + * This method will always return the same IRI for the given project IRI. + * + * @param projectIri the IRI of the project the mapping will belong to. + * @return an IRI used as a lock for the creation of mappings inside a given project. + */ + def createMappingLockIriForProject(projectIri: IRI): IRI = s"$projectIri/mappings" - } /** - * Creates a new project IRI based on a UUID or project shortcode. - * - * @param shortcode the required project shortcode. - * @return a new project IRI. - */ - def makeRandomProjectIri(shortcode: String): IRI = { + * Creates a new project IRI based on a UUID or project shortcode. + * + * @param shortcode the required project shortcode. + * @return a new project IRI. + */ + def makeRandomProjectIri(shortcode: String): IRI = s"http://$IriDomain/projects/$shortcode" - } /** - * Creates a new group IRI based on a UUID. - * - * @param shortcode the required project shortcode. - * @return a new group IRI. - */ + * Creates a new group IRI based on a UUID. + * + * @param shortcode the required project shortcode. + * @return a new group IRI. + */ def makeRandomGroupIri(shortcode: String): String = { val knoraGroupUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/groups/$shortcode/$knoraGroupUuid" } /** - * Creates a new person IRI based on a UUID. - * - * @return a new person IRI. - */ + * Creates a new person IRI based on a UUID. + * + * @return a new person IRI. + */ def makeRandomPersonIri: IRI = { val knoraPersonUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/users/$knoraPersonUuid" } /** - * Creates a new list IRI based on a UUID. - * - * @param shortcode the required project shortcode. - * @return a new list IRI. - */ + * Creates a new list IRI based on a UUID. + * + * @param shortcode the required project shortcode. + * @return a new list IRI. + */ def makeRandomListIri(shortcode: String): String = { val knoraListUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/lists/$shortcode/$knoraListUuid" } /** - * Creates a new standoff tag IRI based on a UUID. - * - * @param valueIri the IRI of the text value containing the standoff tag. - * @param startIndex the standoff tag's start index. - * @return a standoff tag IRI. - */ - def makeRandomStandoffTagIri(valueIri: IRI, startIndex: Int): IRI = { + * Creates a new standoff tag IRI based on a UUID. + * + * @param valueIri the IRI of the text value containing the standoff tag. + * @param startIndex the standoff tag's start index. + * @return a standoff tag IRI. + */ + def makeRandomStandoffTagIri(valueIri: IRI, startIndex: Int): IRI = s"$valueIri/standoff/$startIndex" - } /** - * Converts the IRI of a property that points to a resource into the IRI of the corresponding link value property. - * - * @param linkPropertyIri the IRI of the property that points to a resource. - * @return the IRI of the corresponding link value property. - */ + * Converts the IRI of a property that points to a resource into the IRI of the corresponding link value property. + * + * @param linkPropertyIri the IRI of the property that points to a resource. + * @return the IRI of the corresponding link value property. + */ def linkPropertyIriToLinkValuePropertyIri(linkPropertyIri: IRI): IRI = { implicit val stringFormatter: StringFormatter = this @@ -3167,11 +3117,11 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Converts the IRI of a property that points to a `knora-base:LinkValue` into the IRI of the corresponding link property. - * - * @param linkValuePropertyIri the IRI of the property that points to the `LinkValue`. - * @return the IRI of the corresponding link property. - */ + * Converts the IRI of a property that points to a `knora-base:LinkValue` into the IRI of the corresponding link property. + * + * @param linkValuePropertyIri the IRI of the property that points to the `LinkValue`. + * @return the IRI of the corresponding link property. + */ def linkValuePropertyIriToLinkPropertyIri(linkValuePropertyIri: IRI): IRI = { implicit val stringFormatter: StringFormatter = this @@ -3179,24 +3129,24 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Creates a new permission IRI based on a UUID. - * - * @param shortcode the required project shortcode. - * @return the IRI of the permission object. - */ + * Creates a new permission IRI based on a UUID. + * + * @param shortcode the required project shortcode. + * @return the IRI of the permission object. + */ def makeRandomPermissionIri(shortcode: String): IRI = { val knoraPermissionUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/permissions/$shortcode/$knoraPermissionUuid" } /** - * Converts a camel-case string like `FooBar` into a string like `foo-bar`. - * - * @param str the string to be converted. - * @param separator the word separator (defaults to `-`). - * @return the converted string. - */ - def camelCaseToSeparatedLowerCase(str: String, separator: String = "-"): String = { + * Converts a camel-case string like `FooBar` into a string like `foo-bar`. + * + * @param str the string to be converted. + * @param separator the word separator (defaults to `-`). + * @return the converted string. + */ + def camelCaseToSeparatedLowerCase(str: String, separator: String = "-"): String = str .replaceAll( "([A-Z]+)([A-Z][a-z])", @@ -3207,16 +3157,15 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No "$1" + separator + "$2" ) .toLowerCase - } /** - * Validates a custom value IRI, throwing [[BadRequestException]] if the IRI is not valid. - * - * @param customValueIri the custom value IRI to be validated. - * @param projectCode the project code of the containing resource. - * @param resourceID the ID of the containing resource. - * @return the validated IRI. - */ + * Validates a custom value IRI, throwing [[BadRequestException]] if the IRI is not valid. + * + * @param customValueIri the custom value IRI to be validated. + * @param projectCode the project code of the containing resource. + * @param resourceID the ID of the containing resource. + * @return the validated IRI. + */ def validateCustomValueIri(customValueIri: SmartIri, projectCode: String, resourceID: String): SmartIri = { if (!customValueIri.isKnoraValueIri) { throw BadRequestException(s"<$customValueIri> is not a Knora value IRI") @@ -3234,27 +3183,25 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No } /** - * Throws [[BadRequestException]] if a Knora API v2 definition API has an ontology name that can only be used - * in the internal schema. - * - * @param iri the IRI to be checked. - */ - def checkExternalOntologyName(iri: SmartIri): Unit = { + * Throws [[BadRequestException]] if a Knora API v2 definition API has an ontology name that can only be used + * in the internal schema. + * + * @param iri the IRI to be checked. + */ + def checkExternalOntologyName(iri: SmartIri): Unit = if (iri.isKnoraApiV2DefinitionIri && OntologyConstants.InternalOntologyLabels.contains(iri.getOntologyName)) { throw BadRequestException(s"Internal ontology <$iri> cannot be served") } - } - def unescapeStringLiteralSeq(stringLiteralSeq: StringLiteralSequenceV2): StringLiteralSequenceV2 = { + def unescapeStringLiteralSeq(stringLiteralSeq: StringLiteralSequenceV2): StringLiteralSequenceV2 = StringLiteralSequenceV2( stringLiterals = stringLiteralSeq.stringLiterals.map(stringLiteral => - StringLiteralV2(value = fromSparqlEncodedString(stringLiteral.value), language = stringLiteral.language)) + StringLiteralV2(value = fromSparqlEncodedString(stringLiteral.value), language = stringLiteral.language) + ) ) - } - def unescapeOptionalString(optionalString: Option[String]): Option[String] = { + def unescapeOptionalString(optionalString: Option[String]): Option[String] = optionalString match { case Some(s: String) => Some(fromSparqlEncodedString(s)) case None => None } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraRequestADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraRequestADM.scala index ef045775d2..a8685c188f 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraRequestADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraRequestADM.scala @@ -20,6 +20,6 @@ package org.knora.webapi.messages.admin.responder /** - * A tagging trait for messages that can be sent to Knora Admin responders. - */ + * A tagging trait for messages that can be sent to Knora Admin responders. + */ trait KnoraRequestADM diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraResponseADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraResponseADM.scala index 7d62deefa8..338bc85d7a 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraResponseADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/KnoraResponseADM.scala @@ -22,6 +22,6 @@ package org.knora.webapi.messages.admin.responder import org.knora.webapi.messages.traits.Jsonable /** - * A trait for Knora Admin response messages. Any response message can be converted into JSON. - */ + * A trait for Knora Admin response messages. Any response message can be converted into JSON. + */ trait KnoraResponseADM extends Jsonable diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala index 3097a1bd59..7c559d3956 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala @@ -35,22 +35,23 @@ import spray.json.{DefaultJsonProtocol, JsValue, JsonFormat, RootJsonFormat} // API requests /** - * Represents an API request payload that asks the Knora API server to create a new group. - * - * @param id the optional IRI of the group to be created (unique). - * @param name the name of the group to be created (unique). - * @param description the description of the group to be created. - * @param project the project inside which the group will be created. - * @param status the status of the group to be created (active = true, inactive = false). - * @param selfjoin the status of self-join of the group to be created. - */ -case class CreateGroupApiRequestADM(id: Option[IRI] = None, - name: String, - description: Option[String], - project: IRI, - status: Boolean, - selfjoin: Boolean) - extends GroupsADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to create a new group. + * + * @param id the optional IRI of the group to be created (unique). + * @param name the name of the group to be created (unique). + * @param description the description of the group to be created. + * @param project the project inside which the group will be created. + * @param status the status of the group to be created (active = true, inactive = false). + * @param selfjoin the status of self-join of the group to be created. + */ +case class CreateGroupApiRequestADM( + id: Option[IRI] = None, + name: String, + description: Option[String], + project: IRI, + status: Boolean, + selfjoin: Boolean +) extends GroupsADMJsonProtocol { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -61,22 +62,23 @@ case class CreateGroupApiRequestADM(id: Option[IRI] = None, } /** - * Represents an API request payload that asks the Knora API server to update - * an existing group. There are two change cases that are covered with this - * data structure: - * (1) change of name, description, and selfjoin - * (2) change of status - * - * @param name the new group's name. - * @param description the new group's description. - * @param status the new group's status. - * @param selfjoin the new group's self-join status. - */ -case class ChangeGroupApiRequestADM(name: Option[String] = None, - description: Option[String] = None, - status: Option[Boolean] = None, - selfjoin: Option[Boolean] = None) - extends GroupsADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to update + * an existing group. There are two change cases that are covered with this + * data structure: + * (1) change of name, description, and selfjoin + * (2) change of status + * + * @param name the new group's name. + * @param description the new group's description. + * @param status the new group's status. + * @param selfjoin the new group's self-join status. + */ +case class ChangeGroupApiRequestADM( + name: Option[String] = None, + description: Option[String] = None, + status: Option[Boolean] = None, + selfjoin: Option[Boolean] = None +) extends GroupsADMJsonProtocol { private val parametersCount = List( name, @@ -89,8 +91,8 @@ case class ChangeGroupApiRequestADM(name: Option[String] = None, if (parametersCount == 0) throw BadRequestException("No data sent in API request.") /** - * check that only allowed information for the 2 cases is sent and not more. - */ + * check that only allowed information for the 2 cases is sent and not more. + */ // change status case if (status.isDefined) { if (parametersCount > 1) throw BadRequestException("Too many parameters sent for group status change.") @@ -106,163 +108,167 @@ case class ChangeGroupApiRequestADM(name: Option[String] = None, // Messages /** - * An abstract trait representing a request message that can be sent to 'GroupsResponderADM'. - */ + * An abstract trait representing a request message that can be sent to 'GroupsResponderADM'. + */ sealed trait GroupsResponderRequestADM extends KnoraRequestADM // Requests /** - * Get all information about all groups. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - */ + * Get all information about all groups. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + */ case class GroupsGetADM(featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends GroupsResponderRequestADM /** - * Get all information about all groups. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - */ + * Get all information about all groups. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + */ case class GroupsGetRequestADM(featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends GroupsResponderRequestADM /** - * Get everything about a single group identified through its IRI. A successful response will be - * an [[Option[GroupADM] ]], which will be `None` if the group was not found. - * - * @param groupIri IRI of the group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - */ + * Get everything about a single group identified through its IRI. A successful response will be + * an [[Option[GroupADM] ]], which will be `None` if the group was not found. + * + * @param groupIri IRI of the group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + */ case class GroupGetADM(groupIri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends GroupsResponderRequestADM /** - * Get everything about a single group identified through its IRI. The response will be a - * [[GroupGetResponseADM]], or an error if the group was not found. - * - * @param groupIri IRI of the group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - */ + * Get everything about a single group identified through its IRI. The response will be a + * [[GroupGetResponseADM]], or an error if the group was not found. + * + * @param groupIri IRI of the group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + */ case class GroupGetRequestADM(groupIri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends GroupsResponderRequestADM /** - * Get everything about a multiple groups identified by their IRIs. The response will be a - * [[Set[GroupGetResponseADM] ]], or an error if one or more groups was not found. - * - * @param groupIris the IRIs of the groups being requested. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - */ -case class MultipleGroupsGetRequestADM(groupIris: Set[IRI], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends GroupsResponderRequestADM + * Get everything about a multiple groups identified by their IRIs. The response will be a + * [[Set[GroupGetResponseADM] ]], or an error if one or more groups was not found. + * + * @param groupIris the IRIs of the groups being requested. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + */ +case class MultipleGroupsGetRequestADM( + groupIris: Set[IRI], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends GroupsResponderRequestADM /** - * Returns all members of the group identified by iri. - * - * @param groupIri IRI of the group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - */ + * Returns all members of the group identified by iri. + * + * @param groupIri IRI of the group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + */ case class GroupMembersGetRequestADM(groupIri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends GroupsResponderRequestADM /** - * Requests the creation of a new group. - * - * @param createRequest the [[CreateGroupApiRequestADM]] information for creating the new group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class GroupCreateRequestADM(createRequest: CreateGroupApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends GroupsResponderRequestADM + * Requests the creation of a new group. + * + * @param createRequest the [[CreateGroupApiRequestADM]] information for creating the new group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class GroupCreateRequestADM( + createRequest: CreateGroupApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends GroupsResponderRequestADM /** - * Request updating of an existing group. - * - * @param groupIri the IRI of the group to be updated. - * @param changeGroupRequest the data which needs to be update. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class GroupChangeRequestADM(groupIri: IRI, - changeGroupRequest: ChangeGroupApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends GroupsResponderRequestADM + * Request updating of an existing group. + * + * @param groupIri the IRI of the group to be updated. + * @param changeGroupRequest the data which needs to be update. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class GroupChangeRequestADM( + groupIri: IRI, + changeGroupRequest: ChangeGroupApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends GroupsResponderRequestADM /** - * Request changing the status (active/inactive) of an existing group. - * - * @param groupIri the IRI of the group to be deleted. - * @param changeGroupRequest the data which needs to be update. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class GroupChangeStatusRequestADM(groupIri: IRI, - changeGroupRequest: ChangeGroupApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends GroupsResponderRequestADM + * Request changing the status (active/inactive) of an existing group. + * + * @param groupIri the IRI of the group to be deleted. + * @param changeGroupRequest the data which needs to be update. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class GroupChangeStatusRequestADM( + groupIri: IRI, + changeGroupRequest: ChangeGroupApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends GroupsResponderRequestADM /** - * Request updating the group's permissions. - * - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ + * Request updating the group's permissions. + * + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ case class GroupPermissionUpdateRequestADM(requestingUser: UserADM, apiRequestID: UUID) extends GroupsResponderRequestADM // Responses /** - * Represents the Knora API v1 JSON response to a request for information about all groups. - * - * @param groups information about all existing groups. - */ + * Represents the Knora API v1 JSON response to a request for information about all groups. + * + * @param groups information about all existing groups. + */ case class GroupsGetResponseADM(groups: Seq[GroupADM]) extends KnoraResponseADM with GroupsADMJsonProtocol { def toJsValue = groupsGetResponseADMFormat.write(this) } /** - * Represents the Knora API v1 JSON response to a request for information about a single group. - * - * @param group all information about the group. - */ + * Represents the Knora API v1 JSON response to a request for information about a single group. + * + * @param group all information about the group. + */ case class GroupGetResponseADM(group: GroupADM) extends KnoraResponseADM with GroupsADMJsonProtocol { def toJsValue = groupResponseADMFormat.write(this) } /** - * Represents an answer to a group membership request. - * - * @param members the group's members. - */ + * Represents an answer to a group membership request. + * + * @param members the group's members. + */ case class GroupMembersGetResponseADM(members: Seq[UserADM]) extends KnoraResponseADM with GroupsADMJsonProtocol { def toJsValue = groupMembersResponseADMFormat.write(this) } /** - * Represents an answer to a group creating/modifying operation. - * - * @param group the new group information of the created/modified group. - */ + * Represents an answer to a group creating/modifying operation. + * + * @param group the new group information of the created/modified group. + */ case class GroupOperationResponseADM(group: GroupADM) extends KnoraResponseADM with GroupsADMJsonProtocol { def toJsValue = groupOperationResponseADMFormat.write(this) } @@ -271,26 +277,24 @@ case class GroupOperationResponseADM(group: GroupADM) extends KnoraResponseADM w // Components of messages /** - * The information describing a group. - * - * @param id the IRI if the group. - * @param name the name of the group. - * @param description the description of the group. - * @param project the project this group belongs to. - * @param status the group's status. - * @param selfjoin the group's self-join status. - * - */ + * The information describing a group. + * + * @param id the IRI if the group. + * @param name the name of the group. + * @param description the description of the group. + * @param project the project this group belongs to. + * @param status the group's status. + * @param selfjoin the group's self-join status. + */ case class GroupADM(id: IRI, name: String, description: String, project: ProjectADM, status: Boolean, selfjoin: Boolean) extends Ordered[GroupADM] { /** - * Allows to sort collections of GroupADM. Sorting is done by the id. - */ + * Allows to sort collections of GroupADM. Sorting is done by the id. + */ def compare(that: GroupADM): Int = this.id.compareTo(that.id) - def asGroupShortADM: GroupShortADM = { - + def asGroupShortADM: GroupShortADM = GroupShortADM( id = id, name = name, @@ -298,40 +302,40 @@ case class GroupADM(id: IRI, name: String, description: String, project: Project status = status, selfjoin = selfjoin ) - } } /** - * The information describing a group (without project). - * - * @param id the IRI if the group. - * @param name the name of the group. - * @param description the description of the group. - * @param status the group's status. - * @param selfjoin the group's self-join status. - * - */ + * The information describing a group (without project). + * + * @param id the IRI if the group. + * @param name the name of the group. + * @param description the description of the group. + * @param status the group's status. + * @param selfjoin the group's self-join status. + */ case class GroupShortADM(id: IRI, name: String, description: String, status: Boolean, selfjoin: Boolean) /** - * Payload used for updating of an existing group. - * - * @param name the name of the group. - * @param description the description of the group. - * @param status the group's status. - * @param selfjoin the group's self-join status. - */ -case class GroupUpdatePayloadADM(name: Option[String] = None, - description: Option[String] = None, - status: Option[Boolean] = None, - selfjoin: Option[Boolean] = None) + * Payload used for updating of an existing group. + * + * @param name the name of the group. + * @param description the description of the group. + * @param status the group's status. + * @param selfjoin the group's self-join status. + */ +case class GroupUpdatePayloadADM( + name: Option[String] = None, + description: Option[String] = None, + status: Option[Boolean] = None, + selfjoin: Option[Boolean] = None +) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON providing data about groups. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about groups. + */ trait GroupsADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with ProjectsADMJsonProtocol { import org.knora.webapi.messages.admin.responder.usersmessages.UsersADMJsonProtocol._ diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala index 563d480f95..d76efa21c1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala @@ -40,21 +40,22 @@ import spray.json._ // API requests /** - * Represents an API request payload that asks the Knora API server to create a new list. At least one - * label needs to be supplied. - * - * @param id the optional custom list IRI. - * @param projectIri the IRI of the project the list belongs to. - * @param name the optional name of the list. - * @param labels the list's labels. - * @param comments the list's comments. - */ -case class CreateListApiRequestADM(id: Option[IRI] = None, - projectIri: IRI, - name: Option[String] = None, - labels: Seq[StringLiteralV2], - comments: Seq[StringLiteralV2]) - extends ListADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to create a new list. At least one + * label needs to be supplied. + * + * @param id the optional custom list IRI. + * @param projectIri the IRI of the project the list belongs to. + * @param name the optional name of the list. + * @param labels the list's labels. + * @param comments the list's comments. + */ +case class CreateListApiRequestADM( + id: Option[IRI] = None, + projectIri: IRI, + name: Option[String] = None, + labels: Seq[StringLiteralV2], + comments: Seq[StringLiteralV2] +) extends ListADMJsonProtocol { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -76,30 +77,30 @@ case class CreateListApiRequestADM(id: Option[IRI] = None, } /** - * Represents an API request payload that asks the Knora API server to create a new node. - * If the IRI of the parent node is given, the new node is attached to the parent node as a sublist node. - * If a specific position is given, insert the child node there. Otherwise, the newly created list node will be appended - * to the end of the list of children. - * If no parent node IRI is given in the payload, a new list is created with this node as its root node. - * At least one label needs to be supplied. - * - * @param id the optional custom IRI of the list node. - * @param parentNodeIri the optional IRI of the parent node. - * @param projectIri the IRI of the project. - * @param name the optional name of the list node. - * @param position the optional position of the node. - * @param labels labels of the list node. - * @param comments comments of the list node. - * - */ -case class CreateNodeApiRequestADM(id: Option[IRI] = None, - parentNodeIri: Option[IRI] = None, - projectIri: IRI, - name: Option[String] = None, - position: Option[Int] = None, - labels: Seq[StringLiteralV2], - comments: Seq[StringLiteralV2]) - extends ListADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to create a new node. + * If the IRI of the parent node is given, the new node is attached to the parent node as a sublist node. + * If a specific position is given, insert the child node there. Otherwise, the newly created list node will be appended + * to the end of the list of children. + * If no parent node IRI is given in the payload, a new list is created with this node as its root node. + * At least one label needs to be supplied. + * + * @param id the optional custom IRI of the list node. + * @param parentNodeIri the optional IRI of the parent node. + * @param projectIri the IRI of the project. + * @param name the optional name of the list node. + * @param position the optional position of the node. + * @param labels labels of the list node. + * @param comments comments of the list node. + */ +case class CreateNodeApiRequestADM( + id: Option[IRI] = None, + parentNodeIri: Option[IRI] = None, + projectIri: IRI, + name: Option[String] = None, + position: Option[Int] = None, + labels: Seq[StringLiteralV2], + comments: Seq[StringLiteralV2] +) extends ListADMJsonProtocol { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateOptionalListIri(id, throw BadRequestException(s"Invalid list node IRI")) @@ -127,9 +128,8 @@ case class CreateNodeApiRequestADM(id: Option[IRI] = None, def toJsValue: JsValue = createListNodeApiRequestADMFormat.write(this) /** - * Escapes special characters within strings - * - */ + * Escapes special characters within strings + */ def escape: CreateNodeApiRequestADM = { val escapedLabels: Seq[StringLiteralV2] = labels.map { label => val escapedLabel = @@ -138,8 +138,10 @@ case class CreateNodeApiRequestADM(id: Option[IRI] = None, } val escapedComments = comments.map { comment => val escapedComment = - stringFormatter.toSparqlEncodedString(comment.value, - throw BadRequestException(s"Invalid comment: ${comment.value}")) + stringFormatter.toSparqlEncodedString( + comment.value, + throw BadRequestException(s"Invalid comment: ${comment.value}") + ) StringLiteralV2(value = escapedComment, language = comment.language) } val escapedName: Option[String] = name match { @@ -152,24 +154,25 @@ case class CreateNodeApiRequestADM(id: Option[IRI] = None, } /** - * Represents an API request payload that asks the Knora API server to update an existing node's basic information (root or child). - * - * @param listIri the IRI of the node to change. - * @param projectIri the IRI of the project the list belongs to. - * @param hasRootNode the flag to identify a child node. - * @param position the position of the node, if not a root node. - * @param name the name of the node - * @param labels the labels. - * @param comments the comments. - */ -case class ChangeNodeInfoApiRequestADM(listIri: IRI, - projectIri: IRI, - hasRootNode: Option[IRI] = None, - position: Option[Int] = None, - name: Option[String] = None, - labels: Option[Seq[StringLiteralV2]] = None, - comments: Option[Seq[StringLiteralV2]] = None) - extends ListADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to update an existing node's basic information (root or child). + * + * @param listIri the IRI of the node to change. + * @param projectIri the IRI of the project the list belongs to. + * @param hasRootNode the flag to identify a child node. + * @param position the position of the node, if not a root node. + * @param name the name of the node + * @param labels the labels. + * @param comments the comments. + */ +case class ChangeNodeInfoApiRequestADM( + listIri: IRI, + projectIri: IRI, + hasRootNode: Option[IRI] = None, + position: Option[Int] = None, + name: Option[String] = None, + labels: Option[Seq[StringLiteralV2]] = None, + comments: Option[Seq[StringLiteralV2]] = None +) extends ListADMJsonProtocol { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -207,41 +210,41 @@ case class ChangeNodeInfoApiRequestADM(listIri: IRI, } /** - * Represents an API request payload that asks the Knora API server to update an existing node's name (root or child). - * - * @param name the new name of the node. - */ + * Represents an API request payload that asks the Knora API server to update an existing node's name (root or child). + * + * @param name the new name of the node. + */ case class ChangeNodeNameApiRequestADM(name: String) extends ListADMJsonProtocol { def toJsValue: JsValue = changeNodeNameApiRequestADMFormat.write(this) } /** - * Represents an API request payload that asks the Knora API server to update an existing node's labels (root or child). - * - * @param labels the new labels of the node - */ + * Represents an API request payload that asks the Knora API server to update an existing node's labels (root or child). + * + * @param labels the new labels of the node + */ case class ChangeNodeLabelsApiRequestADM(labels: Seq[StringLiteralV2]) extends ListADMJsonProtocol { def toJsValue: JsValue = changeNodeLabelsApiRequestADMFormat.write(this) } /** - * Represents an API request payload that asks the Knora API server to update an existing node's comments (root or child). - * - * @param comments the new comments of the node. - */ + * Represents an API request payload that asks the Knora API server to update an existing node's comments (root or child). + * + * @param comments the new comments of the node. + */ case class ChangeNodeCommentsApiRequestADM(comments: Seq[StringLiteralV2]) extends ListADMJsonProtocol { def toJsValue: JsValue = changeNodeCommentsApiRequestADMFormat.write(this) } /** - * Represents an API request payload that asks the Knora API server to update the position of child node. - * - * @param position the new position of the node. - * @param parentIri the parent node Iri. - */ + * Represents an API request payload that asks the Knora API server to update the position of child node. + * + * @param position the new position of the node. + * @param parentIri the parent node Iri. + */ case class ChangeNodePositionApiRequestADM(position: Int, parentIri: IRI) extends ListADMJsonProtocol { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -262,90 +265,99 @@ case class ChangeNodePositionApiRequestADM(position: Int, parentIri: IRI) extend // Messages /** - * An abstract trait for messages that can be sent to `HierarchicalListsResponderV2`. - */ + * An abstract trait for messages that can be sent to `HierarchicalListsResponderV2`. + */ sealed trait ListsResponderRequestADM extends KnoraRequestADM /** - * Requests a list of all lists or the lists inside a project. A successful response will be a [[ListsGetResponseADM]] - * - * @param projectIri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ListsGetRequestADM(projectIri: Option[IRI] = None, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ListsResponderRequestADM + * Requests a list of all lists or the lists inside a project. A successful response will be a [[ListsGetResponseADM]] + * + * @param projectIri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ListsGetRequestADM( + projectIri: Option[IRI] = None, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ListsResponderRequestADM /** - * Requests a node (root or child). A successful response will be a [[ListItemGetResponseADM]] - * - * @param iri the IRI of the node (root or child). - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Requests a node (root or child). A successful response will be a [[ListItemGetResponseADM]] + * + * @param iri the IRI of the node (root or child). + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class ListGetRequestADM(iri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ListsResponderRequestADM /** - * Request basic information about a node (root or child). A successful response will be a [[NodeInfoGetResponseADM]] - * - * @param iri the IRI of the list node. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Request basic information about a node (root or child). A successful response will be a [[NodeInfoGetResponseADM]] + * + * @param iri the IRI of the list node. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class ListNodeInfoGetRequestADM(iri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ListsResponderRequestADM /** - * Requests the path from the root node of a list to a particular node. A successful response will be - * a [[NodePathGetResponseADM]]. - * - * @param iri the IRI of the node. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Requests the path from the root node of a list to a particular node. A successful response will be + * a [[NodePathGetResponseADM]]. + * + * @param iri the IRI of the node. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class NodePathGetRequestADM(iri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ListsResponderRequestADM /** - * Requests the creation of a new list. - * - * @param createRootNode the [[CreateNodeApiRequestADM]] information used for creating the root node of the list. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user creating the new list. - * @param apiRequestID the ID of the API request. - */ -case class ListCreateRequestADM(createRootNode: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM { + * Requests the creation of a new list. + * + * @param createRootNode the [[CreateNodeApiRequestADM]] information used for creating the root node of the list. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user creating the new list. + * @param apiRequestID the ID of the API request. + */ +case class ListCreateRequestADM( + createRootNode: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM { // check if the requesting user is allowed to perform operation - if (!requestingUser.permissions.isProjectAdmin(createRootNode.projectIri) && !requestingUser.permissions.isSystemAdmin) { + if ( + !requestingUser.permissions.isProjectAdmin(createRootNode.projectIri) && !requestingUser.permissions.isSystemAdmin + ) { // not project or a system admin throw ForbiddenException(LIST_CREATE_PERMISSION_ERROR) } } /** - * Request updating basic information of an existing node. - * - * @param listIri the IRI of the node to be updated (root or child ). - * @param changeNodeRequest the data which needs to be update. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class NodeInfoChangeRequestADM(listIri: IRI, - changeNodeRequest: ChangeNodeInfoApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM { + * Request updating basic information of an existing node. + * + * @param listIri the IRI of the node to be updated (root or child ). + * @param changeNodeRequest the data which needs to be update. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class NodeInfoChangeRequestADM( + listIri: IRI, + changeNodeRequest: ChangeNodeInfoApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM { // check if the requesting user is allowed to perform operation - if (!requestingUser.permissions.isProjectAdmin(changeNodeRequest.projectIri) && !requestingUser.permissions.isSystemAdmin) { + if ( + !requestingUser.permissions.isProjectAdmin( + changeNodeRequest.projectIri + ) && !requestingUser.permissions.isSystemAdmin + ) { // not project or a system admin throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR) } @@ -353,20 +365,25 @@ case class NodeInfoChangeRequestADM(listIri: IRI, } /** - * Request the creation of a new list node, root or child. - * - * @param createChildNodeRequest the new node information. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class ListChildNodeCreateRequestADM(createChildNodeRequest: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM { + * Request the creation of a new list node, root or child. + * + * @param createChildNodeRequest the new node information. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class ListChildNodeCreateRequestADM( + createChildNodeRequest: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM { // check if the requesting user is allowed to perform operation - if (!requestingUser.permissions.isProjectAdmin(createChildNodeRequest.projectIri) && !requestingUser.permissions.isSystemAdmin) { + if ( + !requestingUser.permissions.isProjectAdmin( + createChildNodeRequest.projectIri + ) && !requestingUser.permissions.isSystemAdmin + ) { // not project or a system admin throw ForbiddenException(LIST_NODE_CREATE_PERMISSION_ERROR) } @@ -374,90 +391,95 @@ case class ListChildNodeCreateRequestADM(createChildNodeRequest: CreateNodeApiRe } /** - * Request updating the name of an existing node. - * - * @param nodeIri the IRI of the node whose name should be updated. - * @param changeNodeNameRequest the payload containing the new name. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class NodeNameChangeRequestADM(nodeIri: IRI, - changeNodeNameRequest: ChangeNodeNameApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM + * Request updating the name of an existing node. + * + * @param nodeIri the IRI of the node whose name should be updated. + * @param changeNodeNameRequest the payload containing the new name. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class NodeNameChangeRequestADM( + nodeIri: IRI, + changeNodeNameRequest: ChangeNodeNameApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM /** - * Request updating the labels of an existing node. - * - * @param nodeIri the IRI of the node whose name should be updated. - * @param changeNodeLabelsRequest the payload containing the new labels. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class NodeLabelsChangeRequestADM(nodeIri: IRI, - changeNodeLabelsRequest: ChangeNodeLabelsApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM + * Request updating the labels of an existing node. + * + * @param nodeIri the IRI of the node whose name should be updated. + * @param changeNodeLabelsRequest the payload containing the new labels. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class NodeLabelsChangeRequestADM( + nodeIri: IRI, + changeNodeLabelsRequest: ChangeNodeLabelsApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM /** - * Request updating the comments of an existing node. - * - * @param nodeIri the IRI of the node whose name should be updated. - * @param changeNodeCommentsRequest the payload containing the new comments. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class NodeCommentsChangeRequestADM(nodeIri: IRI, - changeNodeCommentsRequest: ChangeNodeCommentsApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM + * Request updating the comments of an existing node. + * + * @param nodeIri the IRI of the node whose name should be updated. + * @param changeNodeCommentsRequest the payload containing the new comments. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class NodeCommentsChangeRequestADM( + nodeIri: IRI, + changeNodeCommentsRequest: ChangeNodeCommentsApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM /** - * Request updating the position of an existing node. - * - * @param nodeIri the IRI of the node whose position should be updated. - * @param changeNodePositionRequest the payload containing the new comments. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @param apiRequestID the ID of the API request. - */ -case class NodePositionChangeRequestADM(nodeIri: IRI, - changeNodePositionRequest: ChangeNodePositionApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM + * Request updating the position of an existing node. + * + * @param nodeIri the IRI of the node whose position should be updated. + * @param changeNodePositionRequest the payload containing the new comments. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @param apiRequestID the ID of the API request. + */ +case class NodePositionChangeRequestADM( + nodeIri: IRI, + changeNodePositionRequest: ChangeNodePositionApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM /** - * Requests deletion of a node (root or child). A successful response will be a [[ListDeleteResponseADM]] - * - * @param nodeIri the IRI of the node (root or child). - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ListItemDeleteRequestADM(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ListsResponderRequestADM + * Requests deletion of a node (root or child). A successful response will be a [[ListDeleteResponseADM]] + * + * @param nodeIri the IRI of the node (root or child). + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ListItemDeleteRequestADM( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ListsResponderRequestADM ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Responses /** - * Represents a sequence of list info nodes. - * - * @param lists a [[ListRootNodeInfoADM]] sequence. - */ + * Represents a sequence of list info nodes. + * + * @param lists a [[ListRootNodeInfoADM]] sequence. + */ case class ListsGetResponseADM(lists: Seq[ListNodeInfoADM]) extends KnoraResponseADM with ListADMJsonProtocol { def toJsValue: JsValue = listsGetResponseADMFormat.write(this) } @@ -465,57 +487,57 @@ case class ListsGetResponseADM(lists: Seq[ListNodeInfoADM]) extends KnoraRespons abstract class ListItemGetResponseADM(listItem: ListItemADM) extends KnoraResponseADM with ListADMJsonProtocol /** - * Provides completes information about the list. The basic information (rood node) and all the child nodes. - * - * @param list the complete list. - */ + * Provides completes information about the list. The basic information (rood node) and all the child nodes. + * + * @param list the complete list. + */ case class ListGetResponseADM(list: ListADM) extends ListItemGetResponseADM(list) { def toJsValue: JsValue = listGetResponseADMFormat.write(this) } /** - * Provides completes information about the node. The basic information (child node) and all its children. - * - * @param node the node. - */ + * Provides completes information about the node. The basic information (child node) and all its children. + * + * @param node the node. + */ case class ListNodeGetResponseADM(node: NodeADM) extends ListItemGetResponseADM(node) { def toJsValue: JsValue = listNodeGetResponseADMFormat.write(this) } /** - * Provides basic information about any node (root or child) without it's children. - * - * @param nodeinfo the basic information about a node. - */ + * Provides basic information about any node (root or child) without it's children. + * + * @param nodeinfo the basic information about a node. + */ abstract class NodeInfoGetResponseADM(nodeinfo: ListNodeInfoADM) extends KnoraResponseADM with ListADMJsonProtocol /** - * Provides basic information about a root node without it's children. - * - * @param listinfo the basic information about a list. - */ + * Provides basic information about a root node without it's children. + * + * @param listinfo the basic information about a list. + */ case class RootNodeInfoGetResponseADM(listinfo: ListRootNodeInfoADM) extends NodeInfoGetResponseADM(listinfo) { def toJsValue: JsValue = listInfoGetResponseADMFormat.write(this) } /** - * Provides basic information about a child node without it's children. - * - * @param nodeinfo the basic information about a list node. - */ + * Provides basic information about a child node without it's children. + * + * @param nodeinfo the basic information about a list node. + */ case class ChildNodeInfoGetResponseADM(nodeinfo: ListChildNodeInfoADM) extends NodeInfoGetResponseADM(nodeinfo) { def toJsValue: JsValue = listNodeInfoGetResponseADMFormat.write(this) } /** - * Responds to a [[NodePathGetRequestADM]] by providing the path to a particular hierarchical list node. - * - * @param elements a list of the nodes composing the path from the list's root node up to and including the specified node. - */ + * Responds to a [[NodePathGetRequestADM]] by providing the path to a particular hierarchical list node. + * + * @param elements a list of the nodes composing the path from the list's root node up to and including the specified node. + */ case class NodePathGetResponseADM(elements: Seq[NodePathElementADM]) extends KnoraResponseADM with ListADMJsonProtocol { def toJsValue: JsValue = nodePathGetResponseADMFormat.write(this) @@ -524,31 +546,31 @@ case class NodePathGetResponseADM(elements: Seq[NodePathElementADM]) extends Kno abstract class ListItemDeleteResponseADM extends KnoraResponseADM with ListADMJsonProtocol /** - * Responds to deletion of a list by returning a success message. - * - * @param iri the IRI of the list that is deleted. - */ + * Responds to deletion of a list by returning a success message. + * + * @param iri the IRI of the list that is deleted. + */ case class ListDeleteResponseADM(iri: IRI, deleted: Boolean) extends ListItemDeleteResponseADM { def toJsValue: JsValue = listDeleteResponseADMFormat.write(this) } /** - * Responds to deletion of a child node by returning its parent node together with list of its immediate children - * whose position is updated. - * - * @param node the updated parent node. - */ + * Responds to deletion of a child node by returning its parent node together with list of its immediate children + * whose position is updated. + * + * @param node the updated parent node. + */ case class ChildNodeDeleteResponseADM(node: ListNodeADM) extends ListItemDeleteResponseADM { def toJsValue: JsValue = listNodeDeleteResponseADMFormat.write(this) } /** - * Responds to change of a child node's position by returning its parent node together with list of its children. - * - * @param node the updated parent node. - */ + * Responds to change of a child node's position by returning its parent node together with list of its children. + * + * @param node the updated parent node. + */ case class NodePositionChangeResponseADM(node: ListNodeADM) extends KnoraResponseADM with ListADMJsonProtocol { def toJsValue: JsValue = changeNodePositionApiResponseADMFormat.write(this) @@ -562,52 +584,52 @@ case class ListADM(listinfo: ListRootNodeInfoADM, children: Seq[ListChildNodeADM extends ListItemADM(listinfo, children) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[List]]. - */ - def sorted: ListADM = { + * Sorts the whole hierarchy. + * + * @return a sorted [[List]]. + */ + def sorted: ListADM = ListADM( listinfo = listinfo, children = children.sortBy(_.position).map(_.sorted) ) - } } case class NodeADM(nodeinfo: ListChildNodeInfoADM, children: Seq[ListChildNodeADM]) extends ListItemADM(nodeinfo, children) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[List]]. - */ - def sorted: NodeADM = { + * Sorts the whole hierarchy. + * + * @return a sorted [[List]]. + */ + def sorted: NodeADM = NodeADM( nodeinfo = nodeinfo, children = children.sortBy(_.position).map(_.sorted) ) - } } /** - * Represents basic information about a list node, the information which is found in the list's root or child node. - * - * @param id the IRI of the list. - * @param name the name of the list node. - * @param labels the labels of the node in all available languages. - * @param comments the comments attached to the node in all available languages. - */ -abstract class ListNodeInfoADM(id: IRI, - name: Option[String], - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2) { + * Represents basic information about a list node, the information which is found in the list's root or child node. + * + * @param id the IRI of the list. + * @param name the name of the list node. + * @param labels the labels of the node in all available languages. + * @param comments the comments attached to the node in all available languages. + */ +abstract class ListNodeInfoADM( + id: IRI, + name: Option[String], + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2 +) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[ListNodeInfoADM]]. - */ + * Sorts the whole hierarchy. + * + * @return a sorted [[ListNodeInfoADM]]. + */ def sorted: ListNodeInfoADM def getName: Option[String] = name @@ -617,38 +639,39 @@ abstract class ListNodeInfoADM(id: IRI, def getComments: StringLiteralSequenceV2 = comments /** - * Gets the label in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if label is not available in user's preferred language. - * @return the label in the preferred language. - */ + * Gets the label in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if label is not available in user's preferred language. + * @return the label in the preferred language. + */ def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] /** - * Gets the comment in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if comment is not available in user's preferred language. - * @return the comment in the preferred language. - */ + * Gets the comment in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if comment is not available in user's preferred language. + * @return the comment in the preferred language. + */ def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] } -case class ListRootNodeInfoADM(id: IRI, - projectIri: IRI, - name: Option[String] = None, - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2) - extends ListNodeInfoADM(id, name, labels, comments) { +case class ListRootNodeInfoADM( + id: IRI, + projectIri: IRI, + name: Option[String] = None, + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2 +) extends ListNodeInfoADM(id, name, labels, comments) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[ListRootNodeInfoADM]]. - */ - def sorted: ListRootNodeInfoADM = { + * Sorts the whole hierarchy. + * + * @return a sorted [[ListRootNodeInfoADM]]. + */ + def sorted: ListRootNodeInfoADM = ListRootNodeInfoADM( id = id, projectIri = projectIri, @@ -656,12 +679,10 @@ case class ListRootNodeInfoADM(id: IRI, labels = labels.sortByStringValue, comments = comments.sortByStringValue ) - } /** - * unescapes the special characters in labels, comments, and name for comparison in tests. - * - */ + * unescapes the special characters in labels, comments, and name for comparison in tests. + */ def unescape: ListRootNodeInfoADM = { val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -678,43 +699,42 @@ case class ListRootNodeInfoADM(id: IRI, } /** - * Gets the label in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if label is not available in user's preferred language. - * @return the label in the preferred language. - */ - def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the label in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if label is not available in user's preferred language. + * @return the label in the preferred language. + */ + def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = labels.getPreferredLanguage(userLang, fallbackLang) - } /** - * Gets the comment in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if comment is not available in user's preferred language. - * @return the comment in the preferred language. - */ - def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the comment in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if comment is not available in user's preferred language. + * @return the comment in the preferred language. + */ + def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = comments.getPreferredLanguage(userLang, fallbackLang) - } } -case class ListChildNodeInfoADM(id: IRI, - name: Option[String], - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2, - position: Int, - hasRootNode: IRI) - extends ListNodeInfoADM(id, name, labels, comments) { +case class ListChildNodeInfoADM( + id: IRI, + name: Option[String], + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2, + position: Int, + hasRootNode: IRI +) extends ListNodeInfoADM(id, name, labels, comments) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[ListChildNodeInfoADM]]. - */ - def sorted: ListChildNodeInfoADM = { + * Sorts the whole hierarchy. + * + * @return a sorted [[ListChildNodeInfoADM]]. + */ + def sorted: ListChildNodeInfoADM = ListChildNodeInfoADM( id = id, name = name, @@ -723,12 +743,10 @@ case class ListChildNodeInfoADM(id: IRI, position = position, hasRootNode = hasRootNode ) - } /** - * unescapes the special characters in labels, comments, and name for comparison in tests. - * - */ + * unescapes the special characters in labels, comments, and name for comparison in tests. + */ def unescape: ListChildNodeInfoADM = { val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -745,48 +763,48 @@ case class ListChildNodeInfoADM(id: IRI, } /** - * Gets the label in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if label is not available in user's preferred language. - * @return the label in the preferred language. - */ - def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the label in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if label is not available in user's preferred language. + * @return the label in the preferred language. + */ + def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = labels.getPreferredLanguage(userLang, fallbackLang) - } /** - * Gets the comment in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if comment is not available in user's preferred language. - * @return the comment in the preferred language. - */ - def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the comment in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if comment is not available in user's preferred language. + * @return the comment in the preferred language. + */ + def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = comments.getPreferredLanguage(userLang, fallbackLang) - } } /** - * Represents a hierarchical list node. - * - * @param id the IRI of the list node. - * @param name the name of the list node. - * @param labels the label(s) of the list node. - * @param comments the comment(s) attached to the list in a specific language (if language tags are used) . - * @param children the list node's child nodes. - */ -abstract class ListNodeADM(id: IRI, - name: Option[String], - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2, - children: Seq[ListChildNodeADM]) { + * Represents a hierarchical list node. + * + * @param id the IRI of the list node. + * @param name the name of the list node. + * @param labels the label(s) of the list node. + * @param comments the comment(s) attached to the list in a specific language (if language tags are used) . + * @param children the list node's child nodes. + */ +abstract class ListNodeADM( + id: IRI, + name: Option[String], + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2, + children: Seq[ListChildNodeADM] +) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[ListNodeADM]]. - */ + * Sorts the whole hierarchy. + * + * @return a sorted [[ListNodeADM]]. + */ def sorted: ListNodeADM def getName: Option[String] = name @@ -800,48 +818,49 @@ abstract class ListNodeADM(id: IRI, def getNodeId: IRI = id /** - * Gets the label in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if label is not available in user's preferred language. - * @return the label in the preferred language. - */ + * Gets the label in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if label is not available in user's preferred language. + * @return the label in the preferred language. + */ def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] /** - * Gets the comment in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if comment is not available in user's preferred language. - * @return the comment in the preferred language. - */ + * Gets the comment in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if comment is not available in user's preferred language. + * @return the comment in the preferred language. + */ def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] } /** - * Represents a hierarchical list root node. - * - * @param id the IRI of the list node. - * @param projectIri the IRI of the project the list belongs to. - * @param name the name of the list node. - * @param labels the label(s) of the list node. - * @param comments the comment(s) attached to the list in a specific language (if language tags are used) . - * @param children the list node's child nodes. - */ -case class ListRootNodeADM(id: IRI, - projectIri: IRI, - name: Option[String], - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2, - children: Seq[ListChildNodeADM]) - extends ListNodeADM(id, name, labels, comments, children) { + * Represents a hierarchical list root node. + * + * @param id the IRI of the list node. + * @param projectIri the IRI of the project the list belongs to. + * @param name the name of the list node. + * @param labels the label(s) of the list node. + * @param comments the comment(s) attached to the list in a specific language (if language tags are used) . + * @param children the list node's child nodes. + */ +case class ListRootNodeADM( + id: IRI, + projectIri: IRI, + name: Option[String], + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2, + children: Seq[ListChildNodeADM] +) extends ListNodeADM(id, name, labels, comments, children) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[ListNodeADM]]. - */ - def sorted: ListRootNodeADM = { + * Sorts the whole hierarchy. + * + * @return a sorted [[ListNodeADM]]. + */ + def sorted: ListRootNodeADM = ListRootNodeADM( id = id, projectIri = projectIri, @@ -850,12 +869,10 @@ case class ListRootNodeADM(id: IRI, comments = comments.sortByStringValue, children = children.sortBy(_.position).map(_.sorted) ) - } /** - * unescapes the special characters in labels, comments, and name for comparison in tests. - * - */ + * unescapes the special characters in labels, comments, and name for comparison in tests. + */ def unescape: ListRootNodeADM = { val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -871,54 +888,53 @@ case class ListRootNodeADM(id: IRI, } /** - * Gets the label in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if label is not available in user's preferred language. - * @return the label in the preferred language. - */ - def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the label in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if label is not available in user's preferred language. + * @return the label in the preferred language. + */ + def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = labels.getPreferredLanguage(userLang, fallbackLang) - } /** - * Gets the comment in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if comment is not available in user's preferred language. - * @return the comment in the preferred language. - */ - def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the comment in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if comment is not available in user's preferred language. + * @return the comment in the preferred language. + */ + def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = comments.getPreferredLanguage(userLang, fallbackLang) - } } /** - * Represents a hierarchical list child node. - * - * @param id the IRI of the list node. - * @param name the name of the list node. - * @param labels the label(s) of the list node. - * @param comments the comment(s) attached to the list in a specific language (if language tags are used) . - * @param children the list node's child nodes. - * @param position the position of the node among its siblings. - * @param hasRootNode the root node of the list. - */ -case class ListChildNodeADM(id: IRI, - name: Option[String], - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2, - position: Int, - hasRootNode: IRI, - children: Seq[ListChildNodeADM]) - extends ListNodeADM(id, name, labels, comments, children) { + * Represents a hierarchical list child node. + * + * @param id the IRI of the list node. + * @param name the name of the list node. + * @param labels the label(s) of the list node. + * @param comments the comment(s) attached to the list in a specific language (if language tags are used) . + * @param children the list node's child nodes. + * @param position the position of the node among its siblings. + * @param hasRootNode the root node of the list. + */ +case class ListChildNodeADM( + id: IRI, + name: Option[String], + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2, + position: Int, + hasRootNode: IRI, + children: Seq[ListChildNodeADM] +) extends ListNodeADM(id, name, labels, comments, children) { /** - * Sorts the whole hierarchy. - * - * @return a sorted [[ListNodeADM]]. - */ - def sorted: ListChildNodeADM = { + * Sorts the whole hierarchy. + * + * @return a sorted [[ListNodeADM]]. + */ + def sorted: ListChildNodeADM = ListChildNodeADM( id = id, name = name, @@ -928,12 +944,10 @@ case class ListChildNodeADM(id: IRI, hasRootNode = hasRootNode, children = children.sortBy(_.position).map(_.sorted) ) - } /** - * unescapes the special characters in labels, comments, and name for comparison in tests. - * - */ + * unescapes the special characters in labels, comments, and name for comparison in tests. + */ def unescape: ListChildNodeADM = { val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -949,83 +963,78 @@ case class ListChildNodeADM(id: IRI, } /** - * Gets the label in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if label is not available in user's preferred language. - * @return the label in the preferred language. - */ - def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the label in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if label is not available in user's preferred language. + * @return the label in the preferred language. + */ + def getLabelInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = labels.getPreferredLanguage(userLang, fallbackLang) - } /** - * Gets the comment in the user's preferred language. - * - * @param userLang the user's preferred language. - * @param fallbackLang language to use if comment is not available in user's preferred language. - * @return the comment in the preferred language. - */ - def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = { + * Gets the comment in the user's preferred language. + * + * @param userLang the user's preferred language. + * @param fallbackLang language to use if comment is not available in user's preferred language. + * @return the comment in the preferred language. + */ + def getCommentInPreferredLanguage(userLang: String, fallbackLang: String): Option[String] = comments.getPreferredLanguage(userLang, fallbackLang) - } } /** - * Represents an element of a node path. - * - * @param id the IRI of the node path element. - * @param name the optional name of the node path element. - * @param labels the label(s) of the node path element. - * @param comments the comment(s) of the node path element. - */ -case class NodePathElementADM(id: IRI, - name: Option[String], - labels: StringLiteralSequenceV2, - comments: StringLiteralSequenceV2) + * Represents an element of a node path. + * + * @param id the IRI of the node path element. + * @param name the optional name of the node path element. + * @param labels the label(s) of the node path element. + * @param comments the comment(s) of the node path element. + */ +case class NodePathElementADM( + id: IRI, + name: Option[String], + labels: StringLiteralSequenceV2, + comments: StringLiteralSequenceV2 +) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting /** - * A spray-json protocol for generating Knora API V2 JSON providing data about lists. - */ + * A spray-json protocol for generating Knora API V2 JSON providing data about lists. + */ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with TriplestoreJsonProtocol { implicit object ListRootNodeInfoFormat extends JsonFormat[ListRootNodeInfoADM] { - def write(node: ListRootNodeInfoADM): JsValue = { + def write(node: ListRootNodeInfoADM): JsValue = ListNodeInfoFormat.write(node) - } - def read(value: JsValue): ListRootNodeInfoADM = { + def read(value: JsValue): ListRootNodeInfoADM = ListNodeInfoFormat.read(value).asInstanceOf[ListRootNodeInfoADM] - } } implicit object ListChildNodeInfoFormat extends JsonFormat[ListChildNodeInfoADM] { - def write(node: ListChildNodeInfoADM): JsValue = { + def write(node: ListChildNodeInfoADM): JsValue = ListNodeInfoFormat.write(node) - } - def read(value: JsValue): ListChildNodeInfoADM = { + def read(value: JsValue): ListChildNodeInfoADM = ListNodeInfoFormat.read(value).asInstanceOf[ListChildNodeInfoADM] - } } implicit object ListNodeInfoFormat extends JsonFormat[ListNodeInfoADM] { /** - * Converts a [[ListNodeInfoADM]] to a [[JsValue]]. - * - * @param nodeInfo a [[ListNodeInfoADM]]. - * @return a [[JsValue]]. - */ - def write(nodeInfo: ListNodeInfoADM): JsValue = { - + * Converts a [[ListNodeInfoADM]] to a [[JsValue]]. + * + * @param nodeInfo a [[ListNodeInfoADM]]. + * @return a [[JsValue]]. + */ + def write(nodeInfo: ListNodeInfoADM): JsValue = nodeInfo match { case root: ListRootNodeInfoADM => if (root.name.nonEmpty) { @@ -1067,14 +1076,13 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with ) } } - } /** - * Converts a [[JsValue]] to a [[ListNodeInfoADM]]. - * - * @param value a [[JsValue]]. - * @return a [[ListNodeInfoADM]]. - */ + * Converts a [[JsValue]] to a [[ListNodeInfoADM]]. + * + * @param value a [[JsValue]]. + * @return a [[ListNodeInfoADM]]. + */ def read(value: JsValue): ListNodeInfoADM = { val fields = value.asJsObject.fields @@ -1130,38 +1138,33 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with implicit object ListRootNodeFormat extends JsonFormat[ListRootNodeADM] { - def write(node: ListRootNodeADM): JsValue = { + def write(node: ListRootNodeADM): JsValue = ListNodeFormat.write(node) - } - def read(value: JsValue): ListRootNodeADM = { + def read(value: JsValue): ListRootNodeADM = ListNodeFormat.read(value).asInstanceOf[ListRootNodeADM] - } } implicit object ListChildNodeFormat extends JsonFormat[ListChildNodeADM] { - def write(node: ListChildNodeADM): JsValue = { + def write(node: ListChildNodeADM): JsValue = ListNodeFormat.write(node) - } - def read(value: JsValue): ListChildNodeADM = { + def read(value: JsValue): ListChildNodeADM = ListNodeFormat.read(value).asInstanceOf[ListChildNodeADM] - } } implicit object ListNodeFormat extends JsonFormat[ListNodeADM] { /** - * Converts a [[ListNodeADM]] to a [[JsValue]]. - * - * @param node a [[ListNodeADM]]. - * @return a [[JsValue]]. - */ - def write(node: ListNodeADM): JsValue = { - + * Converts a [[ListNodeADM]] to a [[JsValue]]. + * + * @param node a [[ListNodeADM]]. + * @return a [[JsValue]]. + */ + def write(node: ListNodeADM): JsValue = node match { case root: ListRootNodeADM => JsObject( @@ -1185,14 +1188,13 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with "children" -> JsArray(child.children.map(write).toVector) ) } - } /** - * Converts a [[JsValue]] to a [[ListNodeADM]]. - * - * @param value a [[JsValue]]. - * @return a [[ListNodeADM]]. - */ + * Converts a [[JsValue]] to a [[ListNodeADM]]. + * + * @param value a [[JsValue]]. + * @return a [[ListNodeADM]]. + */ def read(value: JsValue): ListNodeADM = { val fields = value.asJsObject.fields @@ -1257,27 +1259,25 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with implicit object NodePathElementFormat extends JsonFormat[NodePathElementADM] { /** - * Converts a [[NodePathElementADM]] to a [[JsValue]]. - * - * @param element a [[NodePathElementADM]]. - * @return a [[JsValue]]. - */ - def write(element: NodePathElementADM): JsValue = { - + * Converts a [[NodePathElementADM]] to a [[JsValue]]. + * + * @param element a [[NodePathElementADM]]. + * @return a [[JsValue]]. + */ + def write(element: NodePathElementADM): JsValue = JsObject( "id" -> element.id.toJson, "name" -> element.name.toJson, "labels" -> JsArray(element.labels.stringLiterals.map(_.toJson)), "comments" -> JsArray(element.comments.stringLiterals.map(_.toJson)) ) - } /** - * Converts a [[JsValue]] to a [[ListNodeInfoADM]]. - * - * @param value a [[JsValue]]. - * @return a [[ListNodeInfoADM]]. - */ + * Converts a [[JsValue]] to a [[ListNodeInfoADM]]. + * + * @param value a [[JsValue]]. + * @return a [[ListNodeInfoADM]]. + */ def read(value: JsValue): NodePathElementADM = { val fields = value.asJsObject.fields @@ -1310,24 +1310,23 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with implicit object ListFormat extends JsonFormat[ListADM] { /** - * Converts a [[ListADM]] to a [[JsValue]]. - * - * @param list a [[ListADM]]. - * @return a [[JsValue]]. - */ - def write(list: ListADM): JsValue = { + * Converts a [[ListADM]] to a [[JsValue]]. + * + * @param list a [[ListADM]]. + * @return a [[JsValue]]. + */ + def write(list: ListADM): JsValue = JsObject( "listinfo" -> list.listinfo.toJson, "children" -> JsArray(list.children.map(_.toJson).toVector) ) - } /** - * Converts a [[JsValue]] to a [[List]]. - * - * @param value a [[JsValue]]. - * @return a [[List]]. - */ + * Converts a [[JsValue]] to a [[List]]. + * + * @param value a [[JsValue]]. + * @return a [[List]]. + */ def read(value: JsValue): ListADM = { val fields = value.asJsObject.fields @@ -1351,24 +1350,23 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with implicit object SublistFormat extends JsonFormat[NodeADM] { /** - * Converts a [[NodeADM]] to a [[JsValue]]. - * - * @param node a [[NodeADM]]. - * @return a [[JsValue]]. - */ - def write(node: NodeADM): JsValue = { + * Converts a [[NodeADM]] to a [[JsValue]]. + * + * @param node a [[NodeADM]]. + * @return a [[JsValue]]. + */ + def write(node: NodeADM): JsValue = JsObject( "nodeinfo" -> node.nodeinfo.toJson, "children" -> JsArray(node.children.map(_.toJson).toVector) ) - } /** - * Converts a [[JsValue]] to a [[NodeADM]]. - * - * @param value a [[JsValue]]. - * @return a [[NodeADM]]. - */ + * Converts a [[JsValue]] to a [[NodeADM]]. + * + * @param value a [[JsValue]]. + * @return a [[NodeADM]]. + */ def read(value: JsValue): NodeADM = { val fields = value.asJsObject.fields @@ -1401,7 +1399,8 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with "position", "name", "labels", - "comments") + "comments" + ) implicit val nodePathGetResponseADMFormat: RootJsonFormat[NodePathGetResponseADM] = jsonFormat(NodePathGetResponseADM, "elements") implicit val listsGetResponseADMFormat: RootJsonFormat[ListsGetResponseADM] = jsonFormat(ListsGetResponseADM, "lists") diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADM.scala index 0cc454d2a6..854c37a2e4 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADM.scala @@ -39,18 +39,19 @@ import spray.json._ // API requests /** - * Represents a payload that asks the Knora API server to create a new - * administrative permission - * - * @param forProject the project for which this permission is created. - * @param forGroup the group for which this permission is created. - * @param hasPermissions the set of permissions. - */ -case class CreateAdministrativePermissionAPIRequestADM(id: Option[IRI] = None, - forProject: IRI, - forGroup: IRI, - hasPermissions: Set[PermissionADM]) - extends PermissionsADMJsonProtocol { + * Represents a payload that asks the Knora API server to create a new + * administrative permission + * + * @param forProject the project for which this permission is created. + * @param forGroup the group for which this permission is created. + * @param hasPermissions the set of permissions. + */ +case class CreateAdministrativePermissionAPIRequestADM( + id: Option[IRI] = None, + forProject: IRI, + forGroup: IRI, + hasPermissions: Set[PermissionADM] +) extends PermissionsADMJsonProtocol { def toJsValue: JsValue = createAdministrativePermissionAPIRequestADMFormat.write(this) @@ -58,44 +59,46 @@ case class CreateAdministrativePermissionAPIRequestADM(id: Option[IRI] = None, stringFormatter.validateAndEscapeProjectIri(forProject, throw BadRequestException(s"Invalid project IRI $forProject")) stringFormatter.validateOptionalPermissionIri( id, - throw BadRequestException(s"Invalid permission IRI ${id.get} is given.")) + throw BadRequestException(s"Invalid permission IRI ${id.get} is given.") + ) if (hasPermissions.isEmpty) throw BadRequestException("Permissions needs to be supplied.") if (!OntologyConstants.KnoraAdmin.BuiltInGroups.contains(forGroup)) { stringFormatter.validateGroupIri(forGroup, throw BadRequestException(s"Invalid group IRI $forGroup")) } - def prepareHasPermissions: CreateAdministrativePermissionAPIRequestADM = { + def prepareHasPermissions: CreateAdministrativePermissionAPIRequestADM = copy( hasPermissions = PermissionsMessagesUtilADM.verifyHasPermissionsAP(hasPermissions) ) - } } /** - * Represents a payload that asks the Knora API server to create a new - * default object access permission - * - * @param forProject the project - * @param forGroup the group - * @param forResourceClass the resource class - * @param forProperty the property - * @param hasPermissions the permissions - */ -case class CreateDefaultObjectAccessPermissionAPIRequestADM(id: Option[IRI] = None, - forProject: IRI, - forGroup: Option[IRI] = None, - forResourceClass: Option[IRI] = None, - forProperty: Option[IRI] = None, - hasPermissions: Set[PermissionADM]) - extends PermissionsADMJsonProtocol { + * Represents a payload that asks the Knora API server to create a new + * default object access permission + * + * @param forProject the project + * @param forGroup the group + * @param forResourceClass the resource class + * @param forProperty the property + * @param hasPermissions the permissions + */ +case class CreateDefaultObjectAccessPermissionAPIRequestADM( + id: Option[IRI] = None, + forProject: IRI, + forGroup: Option[IRI] = None, + forResourceClass: Option[IRI] = None, + forProperty: Option[IRI] = None, + hasPermissions: Set[PermissionADM] +) extends PermissionsADMJsonProtocol { def toJsValue: JsValue = createDefaultObjectAccessPermissionAPIRequestADMFormat.write(this) implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(forProject, throw BadRequestException(s"Invalid project IRI $forProject")) stringFormatter.validateOptionalPermissionIri( id, - throw BadRequestException(s"Invalid permission IRI ${id.get} is given.")) + throw BadRequestException(s"Invalid permission IRI ${id.get} is given.") + ) forGroup match { case Some(iri: IRI) => @@ -105,14 +108,17 @@ case class CreateDefaultObjectAccessPermissionAPIRequestADM(id: Option[IRI] = No throw BadRequestException("Not allowed to supply groupIri and propertyIri together.") else { if (!OntologyConstants.KnoraAdmin.BuiltInGroups.contains(iri)) { - stringFormatter.validateOptionalGroupIri(forGroup, - throw BadRequestException(s"Invalid group IRI ${forGroup.get}")) + stringFormatter.validateOptionalGroupIri( + forGroup, + throw BadRequestException(s"Invalid group IRI ${forGroup.get}") + ) } } case None => if (forResourceClass.isEmpty && forProperty.isEmpty) { throw BadRequestException( - "Either a group, a resource class, a property, or a combination of resource class and property must be given.") + "Either a group, a resource class, a property, or a combination of resource class and property must be given." + ) } } @@ -134,18 +140,17 @@ case class CreateDefaultObjectAccessPermissionAPIRequestADM(id: Option[IRI] = No if (hasPermissions.isEmpty) throw BadRequestException("Permissions needs to be supplied.") - def prepareHasPermissions: CreateDefaultObjectAccessPermissionAPIRequestADM = { + def prepareHasPermissions: CreateDefaultObjectAccessPermissionAPIRequestADM = copy( hasPermissions = PermissionsMessagesUtilADM.verifyHasPermissionsDOAP(hasPermissions) ) - } } /** - * Represents an API request payload that asks the Knora API server to update the group of a permission. - * - * @param forGroup the new group IRI. - */ + * Represents an API request payload that asks the Knora API server to update the group of a permission. + * + * @param forGroup the new group IRI. + */ case class ChangePermissionGroupApiRequestADM(forGroup: IRI) extends PermissionsADMJsonProtocol { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -158,10 +163,10 @@ case class ChangePermissionGroupApiRequestADM(forGroup: IRI) extends Permissions } /** - * Represents an API request payload that asks the Knora API server to update hasPermissions property of a permission. - * - * @param hasPermissions the new set of permission values. - */ + * Represents an API request payload that asks the Knora API server to update hasPermissions property of a permission. + * + * @param hasPermissions the new set of permission values. + */ case class ChangePermissionHasPermissionsApiRequestADM(hasPermissions: Set[PermissionADM]) extends PermissionsADMJsonProtocol { if (hasPermissions.isEmpty) { @@ -173,10 +178,10 @@ case class ChangePermissionHasPermissionsApiRequestADM(hasPermissions: Set[Permi } /** - * Represents an API request payload that asks the Knora API server to update resourceClassIri of a doap permission. - * - * @param forResourceClass the new resource class IRI of the doap permission. - */ + * Represents an API request payload that asks the Knora API server to update resourceClassIri of a doap permission. + * + * @param forResourceClass the new resource class IRI of the doap permission. + */ case class ChangePermissionResourceClassApiRequestADM(forResourceClass: IRI) extends PermissionsADMJsonProtocol { if (forResourceClass.isEmpty) { throw BadRequestException(s"Resource class IRI cannot be empty.") @@ -184,92 +189,100 @@ case class ChangePermissionResourceClassApiRequestADM(forResourceClass: IRI) ext private val stringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeIri( forResourceClass, - throw BadRequestException(s"Invalid resource class IRI $forResourceClass is given.")) + throw BadRequestException(s"Invalid resource class IRI $forResourceClass is given.") + ) def toJsValue: JsValue = changePermissionResourceClassApiRequestADMFormat.write(this) } /** - * Represents an API request payload that asks the Knora API server to update property of a doap permission. - * - * @param forProperty the new property IRI of the doap permission. - */ + * Represents an API request payload that asks the Knora API server to update property of a doap permission. + * + * @param forProperty the new property IRI of the doap permission. + */ case class ChangePermissionPropertyApiRequestADM(forProperty: IRI) extends PermissionsADMJsonProtocol { if (forProperty.isEmpty) { throw BadRequestException(s"Property IRI cannot be empty.") } private val stringFormatter = StringFormatter.getInstanceForConstantOntologies - stringFormatter.validateAndEscapeIri(forProperty, - throw BadRequestException(s"Invalid property IRI $forProperty is given.")) + stringFormatter.validateAndEscapeIri( + forProperty, + throw BadRequestException(s"Invalid property IRI $forProperty is given.") + ) def toJsValue: JsValue = changePermissionPropertyApiRequestADMFormat.write(this) } /** - * An abstract trait representing message that can be sent to `PermissionsResponderV1`. - */ + * An abstract trait representing message that can be sent to `PermissionsResponderV1`. + */ sealed trait PermissionsResponderRequestADM extends KnoraRequestADM /** - * A message that requests the user's [[PermissionsDataADM]]. - * - * @param projectIris the projects the user is part of. - * @param groupIris the groups the user is member of. - * @param isInProjectAdminGroups the projects for which the user is member of the ProjectAdmin group. - * @param isInSystemAdminGroup the flag denoting users membership in the SystemAdmin group. - * @param featureFactoryConfig the feature factory configuration. - */ -case class PermissionDataGetADM(projectIris: Seq[IRI], - groupIris: Seq[IRI], - isInProjectAdminGroups: Seq[IRI], - isInSystemAdminGroup: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends PermissionsResponderRequestADM { + * A message that requests the user's [[PermissionsDataADM]]. + * + * @param projectIris the projects the user is part of. + * @param groupIris the groups the user is member of. + * @param isInProjectAdminGroups the projects for which the user is member of the ProjectAdmin group. + * @param isInSystemAdminGroup the flag denoting users membership in the SystemAdmin group. + * @param featureFactoryConfig the feature factory configuration. + */ +case class PermissionDataGetADM( + projectIris: Seq[IRI], + groupIris: Seq[IRI], + isInProjectAdminGroups: Seq[IRI], + isInSystemAdminGroup: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends PermissionsResponderRequestADM { if (!requestingUser.isSystemUser) throw ForbiddenException("Permission data can only by queried by a SystemUser.") } /** - * A message that requests all permissions defined inside a project. - * A successful response will be a [[PermissionsForProjectGetResponseADM]]. - * - * @param projectIri the project for which the permissions are queried. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ -case class PermissionsForProjectGetRequestADM(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests all permissions defined inside a project. + * A successful response will be a [[PermissionsForProjectGetResponseADM]]. + * + * @param projectIri the project for which the permissions are queried. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ +case class PermissionsForProjectGetRequestADM( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri)) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + ) { // not a system or project admin throw ForbiddenException("Permissions can only be queried by system and project admin.") } } /** - * A message that requests update of a permission's group. - * A successful response will be a [[PermissionItemADM]]. - * - * @param permissionIri the IRI of the permission to be updated. - * @param changePermissionGroupRequest the request to update permission's group. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ -case class PermissionChangeGroupRequestADM(permissionIri: IRI, - changePermissionGroupRequest: ChangePermissionGroupApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests update of a permission's group. + * A successful response will be a [[PermissionItemADM]]. + * + * @param permissionIri the IRI of the permission to be updated. + * @param changePermissionGroupRequest the request to update permission's group. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ +case class PermissionChangeGroupRequestADM( + permissionIri: IRI, + changePermissionGroupRequest: ChangePermissionGroupApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies if (!stringFormatter.isKnoraPermissionIriStr(permissionIri)) { @@ -279,20 +292,20 @@ case class PermissionChangeGroupRequestADM(permissionIri: IRI, } /** - * A message that requests update of a permission's hasPermissions property. - * A successful response will be a [[PermissionItemADM]]. - * - * @param permissionIri the IRI of the permission to be updated. - * @param changePermissionHasPermissionsRequest the request to update hasPermissions. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ + * A message that requests update of a permission's hasPermissions property. + * A successful response will be a [[PermissionItemADM]]. + * + * @param permissionIri the IRI of the permission to be updated. + * @param changePermissionHasPermissionsRequest the request to update hasPermissions. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ case class PermissionChangeHasPermissionsRequestADM( - permissionIri: IRI, - changePermissionHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + permissionIri: IRI, + changePermissionHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies if (!stringFormatter.isKnoraPermissionIriStr(permissionIri)) { @@ -302,20 +315,20 @@ case class PermissionChangeHasPermissionsRequestADM( } /** - * A message that requests update of a doap permission's resource class. - * A successful response will be a [[PermissionItemADM]]. - * - * @param permissionIri the IRI of the permission to be updated. - * @param changePermissionResourceClassRequest the request to update permission's resource class. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ + * A message that requests update of a doap permission's resource class. + * A successful response will be a [[PermissionItemADM]]. + * + * @param permissionIri the IRI of the permission to be updated. + * @param changePermissionResourceClassRequest the request to update permission's resource class. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ case class PermissionChangeResourceClassRequestADM( - permissionIri: IRI, - changePermissionResourceClassRequest: ChangePermissionResourceClassApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + permissionIri: IRI, + changePermissionResourceClassRequest: ChangePermissionResourceClassApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies if (!stringFormatter.isKnoraPermissionIriStr(permissionIri)) { @@ -324,19 +337,20 @@ case class PermissionChangeResourceClassRequestADM( } /** - * A message that requests update of a doap permission's resource class. - * A successful response will be a [[PermissionItemADM]]. - * - * @param permissionIri the IRI of the permission to be updated. - * @param changePermissionPropertyRequest the request to update permission's property. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ -case class PermissionChangePropertyRequestADM(permissionIri: IRI, - changePermissionPropertyRequest: ChangePermissionPropertyApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests update of a doap permission's resource class. + * A successful response will be a [[PermissionItemADM]]. + * + * @param permissionIri the IRI of the permission to be updated. + * @param changePermissionPropertyRequest the request to update permission's property. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ +case class PermissionChangePropertyRequestADM( + permissionIri: IRI, + changePermissionPropertyRequest: ChangePermissionPropertyApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies if (!stringFormatter.isKnoraPermissionIriStr(permissionIri)) { @@ -347,56 +361,61 @@ case class PermissionChangePropertyRequestADM(permissionIri: IRI, // Administrative Permissions /** - * A message that requests all administrative permissions defined inside a project. - * A successful response will be a [[AdministrativePermissionsForProjectGetResponseADM]]. - * - * @param projectIri the project for which the administrative permissions are queried. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ -case class AdministrativePermissionsForProjectGetRequestADM(projectIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests all administrative permissions defined inside a project. + * A successful response will be a [[AdministrativePermissionsForProjectGetResponseADM]]. + * + * @param projectIri the project for which the administrative permissions are queried. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ +case class AdministrativePermissionsForProjectGetRequestADM( + projectIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri)) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + ) { // not a system or project admin throw ForbiddenException("Administrative permission can only be queried by system and project admin.") } } /** - * A message that requests an administrative permission object identified through his IRI. - * A successful response will be a [[AdministrativePermissionGetResponseADM]] object. - * - * @param administrativePermissionIri the iri of the administrative permission object. - * @param requestingUser the user initiating the request. - * @param apiRequestID the API request ID. - */ -case class AdministrativePermissionForIriGetRequestADM(administrativePermissionIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests an administrative permission object identified through his IRI. + * A successful response will be a [[AdministrativePermissionGetResponseADM]] object. + * + * @param administrativePermissionIri the iri of the administrative permission object. + * @param requestingUser the user initiating the request. + * @param apiRequestID the API request ID. + */ +case class AdministrativePermissionForIriGetRequestADM( + administrativePermissionIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validatePermissionIri( administrativePermissionIri, - throw BadRequestException(s"Invalid permission IRI $administrativePermissionIri is given.")) + throw BadRequestException(s"Invalid permission IRI $administrativePermissionIri is given.") + ) } /** - * A message that requests an administrative permission object identified by project and group. - * A response will contain an optional [[AdministrativePermissionGetResponseADM]] object. - * - * @param projectIri the project. - * @param groupIri the group. - * @param requestingUser the user initiating the request. - */ + * A message that requests an administrative permission object identified by project and group. + * A response will contain an optional [[AdministrativePermissionGetResponseADM]] object. + * + * @param projectIri the project. + * @param groupIri the group. + * @param requestingUser the user initiating the request. + */ case class AdministrativePermissionForProjectGroupGetADM(projectIri: IRI, groupIri: IRI, requestingUser: UserADM) extends PermissionsResponderRequestADM { @@ -404,51 +423,58 @@ case class AdministrativePermissionForProjectGroupGetADM(projectIri: IRI, groupI stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("Administrative permission can only be queried by system and project admin.") } } /** - * A message that requests an administrative permission object identified by project and group. - * A successful response will be an [[AdministrativePermissionGetResponseADM]] object. - * - * @param projectIri - * @param groupIri - * @param requestingUser - */ + * A message that requests an administrative permission object identified by project and group. + * A successful response will be an [[AdministrativePermissionGetResponseADM]] object. + * + * @param projectIri + * @param groupIri + * @param requestingUser + */ case class AdministrativePermissionForProjectGroupGetRequestADM(projectIri: IRI, groupIri: IRI, requestingUser: UserADM) extends PermissionsResponderRequestADM { // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("Administrative permission can only be queried by system and project admin.") } } /** - * Create a single [[AdministrativePermissionADM]]. - * - * @param createRequest the API create request payload. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the API request ID. - */ -case class AdministrativePermissionCreateRequestADM(createRequest: CreateAdministrativePermissionAPIRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * Create a single [[AdministrativePermissionADM]]. + * + * @param createRequest the API create request payload. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the API request ID. + */ +case class AdministrativePermissionCreateRequestADM( + createRequest: CreateAdministrativePermissionAPIRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { // check if the requesting user is allowed to add the administrative permission // Allowed are SystemAdmin, ProjectAdmin and SystemUser - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(createRequest.forProject) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(createRequest.forProject) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("A new administrative permission can only be added by system or project admin.") } @@ -457,10 +483,10 @@ case class AdministrativePermissionCreateRequestADM(createRequest: CreateAdminis // Object Access Permissions /** - * A message that requests the object access permissions attached to a resource via the 'knora-base:hasPermissions' property. - * - * @param resourceIri the IRI of the resource. - */ + * A message that requests the object access permissions attached to a resource via the 'knora-base:hasPermissions' property. + * + * @param resourceIri the IRI of the resource. + */ case class ObjectAccessPermissionsForResourceGetADM(resourceIri: IRI, requestingUser: UserADM) extends PermissionsResponderRequestADM { @@ -473,10 +499,10 @@ case class ObjectAccessPermissionsForResourceGetADM(resourceIri: IRI, requesting } /** - * A message that requests the object access permissions attached to a value via the 'knora-base:hasPermissions' property. - * - * @param valueIri the IRI of the value. - */ + * A message that requests the object access permissions attached to a value via the 'knora-base:hasPermissions' property. + * + * @param valueIri the IRI of the value. + */ case class ObjectAccessPermissionsForValueGetADM(valueIri: IRI, requestingUser: UserADM) extends PermissionsResponderRequestADM { @@ -490,53 +516,59 @@ case class ObjectAccessPermissionsForValueGetADM(valueIri: IRI, requestingUser: // Default Object Access Permissions /** - * A message that requests all default object access permissions defined inside a project. - * A successful response will be a list of [[DefaultObjectAccessPermissionADM]]. - * - * @param projectIri the project for which the default object access permissions are queried. - * @param requestingUser the user initiating this request. - * @param apiRequestID the API request ID. - */ -case class DefaultObjectAccessPermissionsForProjectGetRequestADM(projectIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests all default object access permissions defined inside a project. + * A successful response will be a list of [[DefaultObjectAccessPermissionADM]]. + * + * @param projectIri the project for which the default object access permissions are queried. + * @param requestingUser the user initiating this request. + * @param apiRequestID the API request ID. + */ +case class DefaultObjectAccessPermissionsForProjectGetRequestADM( + projectIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri)) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + ) { // not a system or project admin throw ForbiddenException("Default object access permissions can only be queried by system and project admin.") } } /** - * A message that requests an object access permission identified by project and either group / resource class / property. - * A successful response will be a [[DefaultObjectAccessPermissionGetResponseADM]]. - * - * @param projectIri the project. - * @param groupIri the group. - * @param resourceClassIri the resource class. - * @param propertyIri the property. - * @param requestingUser the user initiating this request. - */ -case class DefaultObjectAccessPermissionGetRequestADM(projectIri: IRI, - groupIri: Option[IRI] = None, - resourceClassIri: Option[IRI] = None, - propertyIri: Option[IRI] = None, - requestingUser: UserADM) - extends PermissionsResponderRequestADM { + * A message that requests an object access permission identified by project and either group / resource class / property. + * A successful response will be a [[DefaultObjectAccessPermissionGetResponseADM]]. + * + * @param projectIri the project. + * @param groupIri the group. + * @param resourceClassIri the resource class. + * @param propertyIri the property. + * @param requestingUser the user initiating this request. + */ +case class DefaultObjectAccessPermissionGetRequestADM( + projectIri: IRI, + groupIri: Option[IRI] = None, + resourceClassIri: Option[IRI] = None, + propertyIri: Option[IRI] = None, + requestingUser: UserADM +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("Default object access permissions can only be queried by system and project admin.") } @@ -551,7 +583,8 @@ case class DefaultObjectAccessPermissionGetRequestADM(projectIri: IRI, case None => if (resourceClassIri.isEmpty && propertyIri.isEmpty) { throw BadRequestException( - "Either a group, a resource class, a property, or a combination of resource class and property must be given.") + "Either a group, a resource class, a property, or a combination of resource class and property must be given." + ) } else None } @@ -573,46 +606,51 @@ case class DefaultObjectAccessPermissionGetRequestADM(projectIri: IRI, } /** - * A message that requests a default object access permission object identified through his IRI. - * A successful response will be an [[DefaultObjectAccessPermissionGetResponseADM]] object. - * - * @param defaultObjectAccessPermissionIri the iri of the default object access permission object. - * @param requestingUser the user initiation the request. - * @param apiRequestID the API request ID. - */ -case class DefaultObjectAccessPermissionForIriGetRequestADM(defaultObjectAccessPermissionIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + * A message that requests a default object access permission object identified through his IRI. + * A successful response will be an [[DefaultObjectAccessPermissionGetResponseADM]] object. + * + * @param defaultObjectAccessPermissionIri the iri of the default object access permission object. + * @param requestingUser the user initiation the request. + * @param apiRequestID the API request ID. + */ +case class DefaultObjectAccessPermissionForIriGetRequestADM( + defaultObjectAccessPermissionIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validatePermissionIri( defaultObjectAccessPermissionIri, - throw BadRequestException(s"Invalid permission IRI $defaultObjectAccessPermissionIri is given.")) + throw BadRequestException(s"Invalid permission IRI $defaultObjectAccessPermissionIri is given.") + ) } /** - * A message that requests the default object access permissions string for a resource class inside a specific project. - * A successful response will be a [[DefaultObjectAccessPermissionsStringResponseADM]]. - * - * @param projectIri the project for which the default object permissions need to be retrieved. - * @param resourceClassIri the resource class which can also carry default object access permissions. - * @param targetUser the user for whom we calculate the permission - * @param requestingUser the requesting user. - */ -case class DefaultObjectAccessPermissionsStringForResourceClassGetADM(projectIri: IRI, - resourceClassIri: IRI, - targetUser: UserADM, - requestingUser: UserADM) - extends PermissionsResponderRequestADM { + * A message that requests the default object access permissions string for a resource class inside a specific project. + * A successful response will be a [[DefaultObjectAccessPermissionsStringResponseADM]]. + * + * @param projectIri the project for which the default object permissions need to be retrieved. + * @param resourceClassIri the resource class which can also carry default object access permissions. + * @param targetUser the user for whom we calculate the permission + * @param requestingUser the requesting user. + */ +case class DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri: IRI, + resourceClassIri: IRI, + targetUser: UserADM, + requestingUser: UserADM +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("Default object access permissions can only be queried by system and project admin.") } @@ -625,30 +663,33 @@ case class DefaultObjectAccessPermissionsStringForResourceClassGetADM(projectIri } /** - * A message that requests default object access permissions for a - * resource class / property combination inside a specific project. A successful response will be a - * [[DefaultObjectAccessPermissionsStringResponseADM]]. - * - * @param projectIri the project for which the default object permissions need to be retrieved. - * @param resourceClassIri the resource class which can also cary default object access permissions. - * @param propertyIri the property type which can also cary default object access permissions. - * @param targetUser the user for whom we calculate the permission - * @param requestingUser the requesting user. - */ -case class DefaultObjectAccessPermissionsStringForPropertyGetADM(projectIri: IRI, - resourceClassIri: IRI, - propertyIri: IRI, - targetUser: UserADM, - requestingUser: UserADM) - extends PermissionsResponderRequestADM { + * A message that requests default object access permissions for a + * resource class / property combination inside a specific project. A successful response will be a + * [[DefaultObjectAccessPermissionsStringResponseADM]]. + * + * @param projectIri the project for which the default object permissions need to be retrieved. + * @param resourceClassIri the resource class which can also cary default object access permissions. + * @param propertyIri the property type which can also cary default object access permissions. + * @param targetUser the user for whom we calculate the permission + * @param requestingUser the requesting user. + */ +case class DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri: IRI, + resourceClassIri: IRI, + propertyIri: IRI, + targetUser: UserADM, + requestingUser: UserADM +) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeProjectIri(projectIri, throw BadRequestException(s"Invalid project IRI $projectIri")) // Check user's permission for the operation - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("Default object access permissions can only be queried by system and project admin.") } @@ -665,58 +706,64 @@ case class DefaultObjectAccessPermissionsStringForPropertyGetADM(projectIri: IRI } /** - * Create a single [[DefaultObjectAccessPermissionADM]]. - * - * @param createRequest the create request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the API request ID. - */ + * Create a single [[DefaultObjectAccessPermissionADM]]. + * + * @param createRequest the create request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the API request ID. + */ case class DefaultObjectAccessPermissionCreateRequestADM( - createRequest: CreateDefaultObjectAccessPermissionAPIRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends PermissionsResponderRequestADM { + createRequest: CreateDefaultObjectAccessPermissionAPIRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends PermissionsResponderRequestADM { // check if the requesting user is allowed to add the default object access permission // Allowed are SystemAdmin, ProjectAdmin and SystemUser - if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(createRequest.forProject) - && !requestingUser.isSystemUser) { + if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(createRequest.forProject) + && !requestingUser.isSystemUser + ) { // not a system admin throw ForbiddenException("A new default object access permission can only be added by a system admin.") } } /** - * A message that requests a permission (doap or ap) by its IRI. - * A successful response will be an [[PermissionGetResponseADM]] object. - * - * @param permissionIri the iri of the default object access permission object. - * @param requestingUser the user initiation the request. - */ + * A message that requests a permission (doap or ap) by its IRI. + * A successful response will be an [[PermissionGetResponseADM]] object. + * + * @param permissionIri the iri of the default object access permission object. + * @param requestingUser the user initiation the request. + */ case class PermissionByIriGetRequestADM(permissionIri: IRI, requestingUser: UserADM) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies - stringFormatter.validatePermissionIri(permissionIri, - throw BadRequestException(s"Invalid permission IRI $permissionIri is given.")) + stringFormatter.validatePermissionIri( + permissionIri, + throw BadRequestException(s"Invalid permission IRI $permissionIri is given.") + ) } /** - * A message that requests deletion of a permission identified through its IRI. - * A successful response will be [[PermissionDeleteResponseADM]] with deleted=true. - * - * @param permissionIri the iri of the permission object. - * @param requestingUser the user initiating the request. - * @param apiRequestID the API request ID. - */ + * A message that requests deletion of a permission identified through its IRI. + * A successful response will be [[PermissionDeleteResponseADM]] with deleted=true. + * + * @param permissionIri the iri of the permission object. + * @param requestingUser the user initiating the request. + * @param apiRequestID the API request ID. + */ case class PermissionDeleteRequestADM(permissionIri: IRI, requestingUser: UserADM, apiRequestID: UUID) extends PermissionsResponderRequestADM { implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies - stringFormatter.validatePermissionIri(permissionIri, - throw BadRequestException(s"Invalid permission IRI $permissionIri is given.")) + stringFormatter.validatePermissionIri( + permissionIri, + throw BadRequestException(s"Invalid permission IRI $permissionIri is given.") + ) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -724,10 +771,10 @@ case class PermissionDeleteRequestADM(permissionIri: IRI, requestingUser: UserAD // All Permissions for project /** - * Represents an answer to [[PermissionsForProjectForProjectGetRequestADM]]. - * - * @param allPermissions the retrieved sequence of [[PermissionInfoADM]] - */ + * Represents an answer to [[PermissionsForProjectForProjectGetRequestADM]]. + * + * @param allPermissions the retrieved sequence of [[PermissionInfoADM]] + */ case class PermissionsForProjectGetResponseADM(allPermissions: Set[PermissionInfoADM]) extends KnoraResponseADM with PermissionsADMJsonProtocol { @@ -736,26 +783,26 @@ case class PermissionsForProjectGetResponseADM(allPermissions: Set[PermissionInf // All administrative Permissions for project /** - * Represents an answer to [[AdministrativePermissionsForProjectGetRequestADM]]. - * - * @param administrativePermissions the retrieved sequence of [[AdministrativePermissionADM]] - */ + * Represents an answer to [[AdministrativePermissionsForProjectGetRequestADM]]. + * + * @param administrativePermissions the retrieved sequence of [[AdministrativePermissionADM]] + */ case class AdministrativePermissionsForProjectGetResponseADM( - administrativePermissions: Seq[AdministrativePermissionADM]) - extends KnoraResponseADM + administrativePermissions: Seq[AdministrativePermissionADM] +) extends KnoraResponseADM with PermissionsADMJsonProtocol { def toJsValue = administrativePermissionsForProjectGetResponseADMFormat.write(this) } // All Default Object Access Permissions for project /** - * Represents an answer to [[DefaultObjectAccessPermissionsForProjectGetRequestADM]] - * - * @param defaultObjectAccessPermissions the retrieved sequence of [[DefaultObjectAccessPermissionADM]] - */ + * Represents an answer to [[DefaultObjectAccessPermissionsForProjectGetRequestADM]] + * + * @param defaultObjectAccessPermissions the retrieved sequence of [[DefaultObjectAccessPermissionADM]] + */ case class DefaultObjectAccessPermissionsForProjectGetResponseADM( - defaultObjectAccessPermissions: Seq[DefaultObjectAccessPermissionADM]) - extends KnoraResponseADM + defaultObjectAccessPermissions: Seq[DefaultObjectAccessPermissionADM] +) extends KnoraResponseADM with PermissionsADMJsonProtocol { def toJsValue = defaultObjectAccessPermissionsForProjectGetResponseADMFormat.write(this) } @@ -765,30 +812,30 @@ abstract class PermissionGetResponseADM(permissionItem: PermissionItemADM) with PermissionsADMJsonProtocol /** - * Represents an answer to a request for getting a default object access permission. - * - * @param defaultObjectAccessPermission the retrieved [[DefaultObjectAccessPermissionADM]]. - */ + * Represents an answer to a request for getting a default object access permission. + * + * @param defaultObjectAccessPermission the retrieved [[DefaultObjectAccessPermissionADM]]. + */ case class DefaultObjectAccessPermissionGetResponseADM(defaultObjectAccessPermission: DefaultObjectAccessPermissionADM) extends PermissionGetResponseADM(defaultObjectAccessPermission) { def toJsValue = defaultObjectAccessPermissionGetResponseADMFormat.write(this) } /** - * Represents an answer to a request for getting an administrative permission. - * - * @param administrativePermission the retrieved [[AdministrativePermissionADM]]. - */ + * Represents an answer to a request for getting an administrative permission. + * + * @param administrativePermission the retrieved [[AdministrativePermissionADM]]. + */ case class AdministrativePermissionGetResponseADM(administrativePermission: AdministrativePermissionADM) extends PermissionGetResponseADM(administrativePermission) { def toJsValue = administrativePermissionGetResponseADMFormat.write(this) } /** - * Represents an answer to [[AdministrativePermissionCreateRequestADM]]. - * - * @param administrativePermission the newly created [[AdministrativePermissionADM]]. - */ + * Represents an answer to [[AdministrativePermissionCreateRequestADM]]. + * + * @param administrativePermission the newly created [[AdministrativePermissionADM]]. + */ case class AdministrativePermissionCreateResponseADM(administrativePermission: AdministrativePermissionADM) extends KnoraResponseADM with PermissionsADMJsonProtocol { @@ -796,30 +843,30 @@ case class AdministrativePermissionCreateResponseADM(administrativePermission: A } /** - * Represents an answer to [[DefaultObjectAccessPermissionCreateRequestADM]]. - * - * @param defaultObjectAccessPermission the newly created [[DefaultObjectAccessPermissionADM]]. - */ + * Represents an answer to [[DefaultObjectAccessPermissionCreateRequestADM]]. + * + * @param defaultObjectAccessPermission the newly created [[DefaultObjectAccessPermissionADM]]. + */ case class DefaultObjectAccessPermissionCreateResponseADM( - defaultObjectAccessPermission: DefaultObjectAccessPermissionADM) - extends KnoraResponseADM + defaultObjectAccessPermission: DefaultObjectAccessPermissionADM +) extends KnoraResponseADM with PermissionsADMJsonProtocol { def toJsValue = defaultObjectAccessPermissionCreateResponseADMFormat.write(this) } /** - * Represents default permissions for an object, formatted as the literal object of `knora-base:hasPermissions`. - * - * @param permissionLiteral a permission literal string. - */ + * Represents default permissions for an object, formatted as the literal object of `knora-base:hasPermissions`. + * + * @param permissionLiteral a permission literal string. + */ case class DefaultObjectAccessPermissionsStringResponseADM(permissionLiteral: String) /** - * Responds to deletion of a permission by returning a success message. - * - * @param permissionIri the IRI of the permission that is deleted. - * @param deleted status of delete operation. - */ + * Responds to deletion of a permission by returning a success message. + * + * @param permissionIri the IRI of the permission that is deleted. + * @param deleted status of delete operation. + */ case class PermissionDeleteResponseADM(permissionIri: IRI, deleted: Boolean) extends KnoraResponseADM with PermissionsADMJsonProtocol { @@ -831,28 +878,30 @@ case class PermissionDeleteResponseADM(permissionIri: IRI, deleted: Boolean) // Components of messages /** - * Represents a user's permission data. The permission data object is user centric and holds permission information - * used during permission checking administrative operations and resource creation. - * - * @param groupsPerProject the groups the user belongs to for each project. - * @param administrativePermissionsPerProject the user's administrative permissions for each project. - */ -case class PermissionsDataADM(groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[IRI, Seq[IRI]], - administrativePermissionsPerProject: Map[IRI, Set[PermissionADM]] = - Map.empty[IRI, Set[PermissionADM]]) { + * Represents a user's permission data. The permission data object is user centric and holds permission information + * used during permission checking administrative operations and resource creation. + * + * @param groupsPerProject the groups the user belongs to for each project. + * @param administrativePermissionsPerProject the user's administrative permissions for each project. + */ +case class PermissionsDataADM( + groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[IRI, Seq[IRI]], + administrativePermissionsPerProject: Map[IRI, Set[PermissionADM]] = Map.empty[IRI, Set[PermissionADM]] +) { /** - * Returns [[PermissionsDataADM]] of the requested type. - * - * @return a [[PermissionsDataADM]] - */ - def ofType(permissionProfileType: PermissionProfileType): PermissionsDataADM = { + * Returns [[PermissionsDataADM]] of the requested type. + * + * @return a [[PermissionsDataADM]] + */ + def ofType(permissionProfileType: PermissionProfileType): PermissionsDataADM = permissionProfileType match { case PermissionDataType.RESTRICTED => PermissionsDataADM( groupsPerProject = groupsPerProject, - administrativePermissionsPerProject = Map.empty[IRI, Set[PermissionADM]] // remove administrative permission information + administrativePermissionsPerProject = + Map.empty[IRI, Set[PermissionADM]] // remove administrative permission information ) case PermissionDataType.FULL => @@ -863,45 +912,41 @@ case class PermissionsDataADM(groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[I case _ => throw BadRequestException(s"The requested userProfileType: $permissionProfileType is invalid.") } - } /* Is the user a member of the SystemAdmin group */ - def isSystemAdmin: Boolean = { + def isSystemAdmin: Boolean = groupsPerProject .getOrElse(OntologyConstants.KnoraAdmin.SystemProject, List.empty[IRI]) .contains(OntologyConstants.KnoraAdmin.SystemAdmin) - } /* Is the user a member of the ProjectAdmin group in any project */ - def isProjectAdminInAnyProject(): Boolean = { + def isProjectAdminInAnyProject(): Boolean = groupsPerProject.flatMap(_._2).toSeq.contains(OntologyConstants.KnoraAdmin.ProjectAdmin) - } /* Is the user a member of the ProjectAdmin group */ - def isProjectAdmin(projectIri: IRI): Boolean = { + def isProjectAdmin(projectIri: IRI): Boolean = groupsPerProject.getOrElse(projectIri, List.empty[IRI]).contains(OntologyConstants.KnoraAdmin.ProjectAdmin) - } /* Does the user have the 'ProjectAdminAllPermission' permission for the project */ - def hasProjectAdminAllPermissionFor(projectIri: IRI): Boolean = { + def hasProjectAdminAllPermissionFor(projectIri: IRI): Boolean = administrativePermissionsPerProject.get(projectIri) match { case Some(permissions) => permissions(PermissionADM.ProjectAdminAllPermission) case None => false } - } /** - * Given an operation, checks if the user is allowed to perform it. - * - * @param operation the name of the operation. - * @param insideProject the IRI of the project inside which the operation will be performed. - * @return a boolean value. - */ - def hasPermissionFor(operation: OperationADM, - insideProject: IRI, - objectAccessPermissions: Option[Set[PermissionADM]]): Boolean = { + * Given an operation, checks if the user is allowed to perform it. + * + * @param operation the name of the operation. + * @param insideProject the IRI of the project inside which the operation will be performed. + * @return a boolean value. + */ + def hasPermissionFor( + operation: OperationADM, + insideProject: IRI, + objectAccessPermissions: Option[Set[PermissionADM]] + ): Boolean = // println(s"hasPermissionFor - administrativePermissionsPerProject: ${administrativePermissionsPerProject}, operation: $operation, insideProject: $insideProject") - if (this.isSystemAdmin) { /* A member of the SystemAdmin group is allowed to perform any operation */ // println("TRUE: A member of the SystemAdmin group is allowed to perform any operation") @@ -912,7 +957,8 @@ case class PermissionsDataADM(groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[I this.administrativePermissionsPerProject.get(insideProject) match { case Some(set) => { set(PermissionADM.ProjectResourceCreateAllPermission) || set( - PermissionADM.projectResourceCreateRestrictedPermission(resourceClassIri)) + PermissionADM.projectResourceCreateRestrictedPermission(resourceClassIri) + ) } case None => { // println("FALSE: No administrative permissions defined for this project.") @@ -923,8 +969,6 @@ case class PermissionsDataADM(groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[I } } - } - /* custom equality implementation with additional debugging output */ def canEqual(a: Any): Boolean = a.isInstanceOf[PermissionsDataADM] @@ -943,7 +987,9 @@ case class PermissionsDataADM(groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[I } val apppEqual = - if (this.administrativePermissionsPerProject.hashCode != that.administrativePermissionsPerProject.hashCode) { + if ( + this.administrativePermissionsPerProject.hashCode != that.administrativePermissionsPerProject.hashCode + ) { println("administrativePermissionsPerProject not equal") println(s"this (expected): ${this.administrativePermissionsPerProject}") println(s"that (found): ${that.administrativePermissionsPerProject}") @@ -957,20 +1003,19 @@ case class PermissionsDataADM(groupsPerProject: Map[IRI, Seq[IRI]] = Map.empty[I case _ => false } - def toSourceString: String = { + def toSourceString: String = "PermissionDataV1( \n" + s"\t groupsPerProject = ${groupsPerProject.toString} \n" + s"\t administrativePermissionsPerProject = ${administrativePermissionsPerProject.toString} \n" + ")" - } } /** - * Represents 'knora-base:AdministrativePermission' - * - * @param iri the IRI of the permission. - * @param permissionType the type of the permission. - */ + * Represents 'knora-base:AdministrativePermission' + * + * @param iri the IRI of the permission. + * @param permissionType the type of the permission. + */ case class PermissionInfoADM(iri: IRI, permissionType: IRI) extends Jsonable with PermissionsADMJsonProtocol { def toJsValue = permissionInfoADMFormat.write(this) @@ -979,29 +1024,30 @@ case class PermissionInfoADM(iri: IRI, permissionType: IRI) extends Jsonable wit abstract class PermissionItemADM extends Jsonable with PermissionsADMJsonProtocol /** - * Represents object access permissions attached to a resource OR value via the - * 'knora-base:hasPermission' property. - * - * @param forResource the IRI of the resource. - * @param forValue the IRI of the value. - * @param hasPermissions the permissions. - */ -case class ObjectAccessPermissionADM(forResource: Option[IRI] = None, - forValue: Option[IRI] = None, - hasPermissions: Set[PermissionADM]) - extends PermissionItemADM { + * Represents object access permissions attached to a resource OR value via the + * 'knora-base:hasPermission' property. + * + * @param forResource the IRI of the resource. + * @param forValue the IRI of the value. + * @param hasPermissions the permissions. + */ +case class ObjectAccessPermissionADM( + forResource: Option[IRI] = None, + forValue: Option[IRI] = None, + hasPermissions: Set[PermissionADM] +) extends PermissionItemADM { def toJsValue = objectAccessPermissionADMFormat.write(this) } /** - * Represents 'knora-base:AdministrativePermission' - * - * @param iri the IRI of the administrative permission. - * @param forProject the project this permission applies to. - * @param forGroup the group this permission applies to. - * @param hasPermissions the administrative permissions. - */ + * Represents 'knora-base:AdministrativePermission' + * + * @param iri the IRI of the administrative permission. + * @param forProject the project this permission applies to. + * @param forGroup the group this permission applies to. + * @param hasPermissions the administrative permissions. + */ case class AdministrativePermissionADM(iri: IRI, forProject: IRI, forGroup: IRI, hasPermissions: Set[PermissionADM]) extends PermissionItemADM { @@ -1009,41 +1055,44 @@ case class AdministrativePermissionADM(iri: IRI, forProject: IRI, forGroup: IRI, } /** - * Represents 'knora-base:DefaultObjectAccessPermission' - * - * @param iri the permission IRI. - * @param forProject the project. - * @param forGroup the group. - * @param forResourceClass the resource class. - * @param forProperty the property. - * @param hasPermissions the permissions. - */ -case class DefaultObjectAccessPermissionADM(iri: IRI, - forProject: IRI, - forGroup: Option[IRI] = None, - forResourceClass: Option[IRI] = None, - forProperty: Option[IRI] = None, - hasPermissions: Set[PermissionADM]) - extends PermissionItemADM { + * Represents 'knora-base:DefaultObjectAccessPermission' + * + * @param iri the permission IRI. + * @param forProject the project. + * @param forGroup the group. + * @param forResourceClass the resource class. + * @param forProperty the property. + * @param hasPermissions the permissions. + */ +case class DefaultObjectAccessPermissionADM( + iri: IRI, + forProject: IRI, + forGroup: Option[IRI] = None, + forResourceClass: Option[IRI] = None, + forProperty: Option[IRI] = None, + hasPermissions: Set[PermissionADM] +) extends PermissionItemADM { /** - * @return a simple string representing the permission which can be used as the cache key. - */ + * @return a simple string representing the permission which can be used as the cache key. + */ def cacheKey: String = - PermissionsMessagesUtilADM.getDefaultObjectAccessPermissionADMKey(forProject, - forGroup, - forResourceClass, - forProperty) + PermissionsMessagesUtilADM.getDefaultObjectAccessPermissionADMKey( + forProject, + forGroup, + forResourceClass, + forProperty + ) def toJsValue = defaultObjectAccessPermissionADMFormat.write(this) } /** - * Case class representing a permission. - * - * @param name the name of the permission. - * @param additionalInformation an optional IRI (e.g., group IRI, resource class IRI). - */ + * Case class representing a permission. + * + * @param name the name of the permission. + * @param additionalInformation an optional IRI (e.g., group IRI, resource class IRI). + */ case class PermissionADM(name: String, additionalInformation: Option[IRI] = None, permissionCode: Option[Int] = None) extends Jsonable with PermissionsADMJsonProtocol { @@ -1054,8 +1103,8 @@ case class PermissionADM(name: String, additionalInformation: Option[IRI] = None } /** - * The permission companion object, used to create specific permissions. - */ + * The permission companion object, used to create specific permissions. + */ object PermissionADM { /////////////////////////////////////////////////////////////////////////// @@ -1070,13 +1119,12 @@ object PermissionADM { ) } - def projectResourceCreateRestrictedPermission(restriction: IRI): PermissionADM = { + def projectResourceCreateRestrictedPermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraAdmin.ProjectResourceCreateRestrictedPermission, additionalInformation = Some(restriction), permissionCode = None ) - } val ProjectAdminAllPermission: PermissionADM = { PermissionADM( @@ -1094,13 +1142,12 @@ object PermissionADM { ) } - def projectAdminGroupRestrictedPermission(restriction: IRI): PermissionADM = { + def projectAdminGroupRestrictedPermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraAdmin.ProjectAdminGroupRestrictedPermission, additionalInformation = Some(restriction), permissionCode = None ) - } val ProjectAdminRightsAllPermission: PermissionADM = { PermissionADM( @@ -1114,63 +1161,58 @@ object PermissionADM { // Object Access Permissions /////////////////////////////////////////////////////////////////////////// - def changeRightsPermission(restriction: IRI): PermissionADM = { + def changeRightsPermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraBase.ChangeRightsPermission, additionalInformation = Some(restriction), permissionCode = Some(8) ) - } - def deletePermission(restriction: IRI): PermissionADM = { + def deletePermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraBase.DeletePermission, additionalInformation = Some(restriction), permissionCode = Some(7) ) - } - def modifyPermission(restriction: IRI): PermissionADM = { + def modifyPermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraBase.ModifyPermission, additionalInformation = Some(restriction), permissionCode = Some(6) ) - } - def viewPermission(restriction: IRI): PermissionADM = { + def viewPermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraBase.ViewPermission, additionalInformation = Some(restriction), permissionCode = Some(2) ) - } - def restrictedViewPermission(restriction: IRI): PermissionADM = { + def restrictedViewPermission(restriction: IRI): PermissionADM = PermissionADM( name = OntologyConstants.KnoraBase.RestrictedViewPermission, additionalInformation = Some(restriction), permissionCode = Some(1) ) - } } /** - * An abstract trait representing operations for which the user needs Administrative Permissions. - */ + * An abstract trait representing operations for which the user needs Administrative Permissions. + */ sealed trait OperationADM /* Creating a resource of a certain class */ case class ResourceCreateOperation(resourceClass: IRI) extends OperationADM /** - * Permission data types: - * restricted: only group memberships, without administrative and default permissions. - * full: everything - * - * Used in the 'ofType' method. - */ + * Permission data types: + * restricted: only group memberships, without administrative and default permissions. + * full: everything + * + * Used in the 'ofType' method. + */ object PermissionDataType extends Enumeration { /* TODO: Extend to incorporate user privacy wishes */ @@ -1183,23 +1225,22 @@ object PermissionDataType extends Enumeration { val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an - * [[InconsistentRepositoryDataException]]. - * - * @param name the name of the value. - * @return the requested value. - */ - def lookup(name: String): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an + * [[InconsistentRepositoryDataException]]. + * + * @param name the name of the value. + * @return the requested value. + */ + def lookup(name: String): Value = valueMap.get(name) match { case Some(value) => value case None => throw InconsistentRepositoryDataException(s"Permission profile type not supported: $name") } - } } /** - * The permission type. - */ + * The permission type. + */ object PermissionType extends Enumeration { type PermissionType = Value @@ -1221,35 +1262,37 @@ trait PermissionsADMJsonProtocol implicit object PermissionProfileTypeFormat extends JsonFormat[PermissionProfileType] { /** - * Not implemented. - */ + * Not implemented. + */ def read(jsonVal: JsValue) = ??? /** - * Converts a [[PermissionDataType]] into [[JsValue]] for formatting as JSON. - * - * @param permissionProfileType the [[PermissionDataType]] to be converted. - * @return a [[JsValue]]. - */ - def write(permissionProfileType: PermissionDataType.Value): JsValue = { + * Converts a [[PermissionDataType]] into [[JsValue]] for formatting as JSON. + * + * @param permissionProfileType the [[PermissionDataType]] to be converted. + * @return a [[JsValue]]. + */ + def write(permissionProfileType: PermissionDataType.Value): JsValue = JsObject(Map("permission_profile_type" -> permissionProfileType.toString.toJson)) - } } implicit val permissionADMFormat: JsonFormat[PermissionADM] = jsonFormat(PermissionADM.apply, "name", "additionalInformation", "permissionCode") implicit val permissionInfoADMFormat: JsonFormat[PermissionInfoADM] = lazyFormat( - jsonFormat(PermissionInfoADM, "iri", "permissionType")) + jsonFormat(PermissionInfoADM, "iri", "permissionType") + ) implicit val administrativePermissionADMFormat: JsonFormat[AdministrativePermissionADM] = lazyFormat( - jsonFormat(AdministrativePermissionADM, "iri", "forProject", "forGroup", "hasPermissions")) + jsonFormat(AdministrativePermissionADM, "iri", "forProject", "forGroup", "hasPermissions") + ) implicit val objectAccessPermissionADMFormat: JsonFormat[ObjectAccessPermissionADM] = jsonFormat(ObjectAccessPermissionADM, "forResource", "forValue", "hasPermissions") implicit val defaultObjectAccessPermissionADMFormat: JsonFormat[DefaultObjectAccessPermissionADM] = lazyFormat( - jsonFormat6(DefaultObjectAccessPermissionADM)) + jsonFormat6(DefaultObjectAccessPermissionADM) + ) implicit val permissionsDataADMFormat: JsonFormat[PermissionsDataADM] = jsonFormat2(PermissionsDataADM) @@ -1274,22 +1317,29 @@ trait PermissionsADMJsonProtocol implicit val createAdministrativePermissionAPIRequestADMFormat : RootJsonFormat[CreateAdministrativePermissionAPIRequestADM] = rootFormat( lazyFormat( - jsonFormat(CreateAdministrativePermissionAPIRequestADM, "id", "forProject", "forGroup", "hasPermissions"))) + jsonFormat(CreateAdministrativePermissionAPIRequestADM, "id", "forProject", "forGroup", "hasPermissions") + ) + ) implicit val createDefaultObjectAccessPermissionAPIRequestADMFormat : RootJsonFormat[CreateDefaultObjectAccessPermissionAPIRequestADM] = rootFormat( lazyFormat( - jsonFormat(CreateDefaultObjectAccessPermissionAPIRequestADM, - "id", - "forProject", - "forGroup", - "forResourceClass", - "forProperty", - "hasPermissions"))) + jsonFormat( + CreateDefaultObjectAccessPermissionAPIRequestADM, + "id", + "forProject", + "forGroup", + "forResourceClass", + "forProperty", + "hasPermissions" + ) + ) + ) implicit val administrativePermissionCreateResponseADMFormat : RootJsonFormat[AdministrativePermissionCreateResponseADM] = rootFormat( - lazyFormat(jsonFormat(AdministrativePermissionCreateResponseADM, "administrative_permission"))) + lazyFormat(jsonFormat(AdministrativePermissionCreateResponseADM, "administrative_permission")) + ) implicit val defaultObjectAccessPermissionCreateResponseADMFormat : RootJsonFormat[DefaultObjectAccessPermissionCreateResponseADM] = diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesUtilADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesUtilADM.scala index ebbfbd918a..957c596c21 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesUtilADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesUtilADM.scala @@ -33,8 +33,8 @@ import org.knora.webapi.messages.OntologyConstants.KnoraBase.{ import org.knora.webapi.util.cache.CacheUtil /** - * Providing helper methods. - */ + * Providing helper methods. + */ object PermissionsMessagesUtilADM { val PermissionsCacheName = "permissionsCache" @@ -52,29 +52,29 @@ object PermissionsMessagesUtilADM { //////////////////// /** - * Creates a key representing the supplied parameters. - * - * @param projectIri the project IRI - * @param groupIri the group IRI - * @param resourceClassIri the resource class IRI - * @param propertyIri the property IRI - * @return a string. - */ - def getDefaultObjectAccessPermissionADMKey(projectIri: IRI, - groupIri: Option[IRI], - resourceClassIri: Option[IRI], - propertyIri: Option[IRI]): String = { - + * Creates a key representing the supplied parameters. + * + * @param projectIri the project IRI + * @param groupIri the group IRI + * @param resourceClassIri the resource class IRI + * @param propertyIri the property IRI + * @return a string. + */ + def getDefaultObjectAccessPermissionADMKey( + projectIri: IRI, + groupIri: Option[IRI], + resourceClassIri: Option[IRI], + propertyIri: Option[IRI] + ): String = projectIri.toString + " | " + groupIri.toString + " | " + resourceClassIri.toString + " | " + propertyIri.toString - } /** - * Writes a [[DefaultObjectAccessPermissionADM]] object to cache. - * - * @param doap a [[DefaultObjectAccessPermissionADM]]. - * @return true if writing was successful. - * @throws ApplicationCacheException when there is a problem with writing to cache. - */ + * Writes a [[DefaultObjectAccessPermissionADM]] object to cache. + * + * @param doap a [[DefaultObjectAccessPermissionADM]]. + * @return true if writing was successful. + * @throws ApplicationCacheException when there is a problem with writing to cache. + */ def writeDefaultObjectAccessPermissionADMToCache(doap: DefaultObjectAccessPermissionADM): Boolean = { val key = doap.cacheKey @@ -89,17 +89,19 @@ object PermissionsMessagesUtilADM { } /** - * Removes a [[DefaultObjectAccessPermissionADM]] object from cache. - * - * @param projectIri the project IRI - * @param groupIri the group IRI - * @param resourceClassIri the resource class IRI - * @param propertyIri the property IRI - */ - def invalidateCachedDefaultObjectAccessPermissionADM(projectIri: IRI, - groupIri: Option[IRI], - resourceClassIri: Option[IRI], - propertyIri: Option[IRI]): Unit = { + * Removes a [[DefaultObjectAccessPermissionADM]] object from cache. + * + * @param projectIri the project IRI + * @param groupIri the group IRI + * @param resourceClassIri the resource class IRI + * @param propertyIri the property IRI + */ + def invalidateCachedDefaultObjectAccessPermissionADM( + projectIri: IRI, + groupIri: Option[IRI], + resourceClassIri: Option[IRI], + propertyIri: Option[IRI] + ): Unit = { val key = getDefaultObjectAccessPermissionADMKey(projectIri, groupIri, resourceClassIri, propertyIri) @@ -107,12 +109,11 @@ object PermissionsMessagesUtilADM { } /** - * Validates the parameters of the `hasPermissions` collections of a DOAP. - * - * @param hasPermissions Set of the permissions. - */ - def validateDOAPHasPermissions(hasPermissions: Set[PermissionADM]) = { - + * Validates the parameters of the `hasPermissions` collections of a DOAP. + * + * @param hasPermissions Set of the permissions. + */ + def validateDOAPHasPermissions(hasPermissions: Set[PermissionADM]) = hasPermissions.foreach { permission => if (permission.additionalInformation.isEmpty) { throw BadRequestException(s"additionalInformation of a default object access permission type cannot be empty.") @@ -120,40 +121,44 @@ object PermissionsMessagesUtilADM { if (permission.name.nonEmpty && !EntityPermissionAbbreviations.contains(permission.name)) throw BadRequestException( s"Invalid value for name parameter of hasPermissions: ${permission.name}, it should be one of " + - s"${EntityPermissionAbbreviations.toString}") + s"${EntityPermissionAbbreviations.toString}" + ) if (permission.permissionCode.nonEmpty) { val code = permission.permissionCode.get if (!PermissionTypeAndCodes.values.toSet.contains(code)) { throw BadRequestException( s"Invalid value for permissionCode parameter of hasPermissions: $code, it should be one of " + - s"${PermissionTypeAndCodes.values.toString}") + s"${PermissionTypeAndCodes.values.toString}" + ) } } if (permission.permissionCode.isEmpty && permission.name.isEmpty) { throw BadRequestException( - s"One of permission code or permission name must be provided for a default object access permission.") + s"One of permission code or permission name must be provided for a default object access permission." + ) } if (permission.permissionCode.nonEmpty && permission.name.nonEmpty) { val code = permission.permissionCode.get if (PermissionTypeAndCodes(permission.name) != code) { throw BadRequestException( - s"Given permission code $code and permission name ${permission.name} are not consistent.") + s"Given permission code $code and permission name ${permission.name} are not consistent." + ) } } } - } /** - * For administrative permission we only need the name parameter of each PermissionADM given in hasPermissions collection. - * This method, validates the content of hasPermissions collection by only keeping the values of name params. - * @param hasPermissions Set of the permissions. - */ + * For administrative permission we only need the name parameter of each PermissionADM given in hasPermissions collection. + * This method, validates the content of hasPermissions collection by only keeping the values of name params. + * @param hasPermissions Set of the permissions. + */ def verifyHasPermissionsAP(hasPermissions: Set[PermissionADM]): Set[PermissionADM] = { val updatedPermissions = hasPermissions.map { permission => if (!AdministrativePermissionAbbreviations.contains(permission.name)) throw BadRequestException( s"Invalid value for name parameter of hasPermissions: ${permission.name}, it should be one of " + - s"${AdministrativePermissionAbbreviations.toString}") + s"${AdministrativePermissionAbbreviations.toString}" + ) PermissionADM( name = permission.name, additionalInformation = None, @@ -164,13 +169,13 @@ object PermissionsMessagesUtilADM { } /** - * For default object access permission, we need to make sure that the value given for the permissionCode matches - * the value of name parameter. - * This method, validates the content of hasPermissions collection by verifying that both permissionCode and name - * indicate the same type of permission. - * - * @param hasPermissions Set of the permissions. - */ + * For default object access permission, we need to make sure that the value given for the permissionCode matches + * the value of name parameter. + * This method, validates the content of hasPermissions collection by verifying that both permissionCode and name + * indicate the same type of permission. + * + * @param hasPermissions Set of the permissions. + */ def verifyHasPermissionsDOAP(hasPermissions: Set[PermissionADM]): Set[PermissionADM] = { validateDOAPHasPermissions(hasPermissions) hasPermissions.map { permission => @@ -180,8 +185,8 @@ object PermissionsMessagesUtilADM { } val name = permission.name.isEmpty match { case true => - val nameCodeSet: Option[(String, Int)] = PermissionTypeAndCodes.find { - case (name, code) => code == permission.permissionCode.get + val nameCodeSet: Option[(String, Int)] = PermissionTypeAndCodes.find { case (name, code) => + code == permission.permissionCode.get } nameCodeSet.get._1 case false => permission.name diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala index d5e81296f8..2b6285aa03 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala @@ -39,28 +39,29 @@ import spray.json.{DefaultJsonProtocol, JsValue, JsonFormat, RootJsonFormat} // API requests /** - * Represents an API request payload that asks the Knora API server to create a new project. - * - * @param id the optional IRI of the project to be created. - * @param shortname the shortname of the project to be created (unique). - * @param shortcode the shortcode of the project to be creates (unique) - * @param longname the longname of the project to be created. - * @param description the description of the project to be created. - * @param keywords the keywords of the project to be created (optional). - * @param logo the logo of the project to be created. - * @param status the status of the project to be created (active = true, inactive = false). - * @param selfjoin the status of self-join of the project to be created. - */ -case class CreateProjectApiRequestADM(id: Option[IRI] = None, - shortname: String, - shortcode: String, - longname: Option[String], - description: Seq[StringLiteralV2], - keywords: Seq[String], - logo: Option[String], - status: Boolean, - selfjoin: Boolean) - extends ProjectsADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to create a new project. + * + * @param id the optional IRI of the project to be created. + * @param shortname the shortname of the project to be created (unique). + * @param shortcode the shortcode of the project to be creates (unique) + * @param longname the longname of the project to be created. + * @param description the description of the project to be created. + * @param keywords the keywords of the project to be created (optional). + * @param logo the logo of the project to be created. + * @param status the status of the project to be created (active = true, inactive = false). + * @param selfjoin the status of self-join of the project to be created. + */ +case class CreateProjectApiRequestADM( + id: Option[IRI] = None, + shortname: String, + shortcode: String, + longname: Option[String], + description: Seq[StringLiteralV2], + keywords: Seq[String], + logo: Option[String], + status: Boolean, + selfjoin: Boolean +) extends ProjectsADMJsonProtocol { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Check that collection parameters are not empty. @@ -78,33 +79,39 @@ case class CreateProjectApiRequestADM(id: Option[IRI] = None, stringFormatter.validateAndEscapeOptionalProjectIri(id, throw BadRequestException(s"Invalid project IRI")) val validatedShortcode = stringFormatter.validateAndEscapeProjectShortcode( shortcode, - errorFun = throw BadRequestException(s"The supplied short code: '$shortcode' is not valid.")) + errorFun = throw BadRequestException(s"The supplied short code: '$shortcode' is not valid.") + ) val validatedShortname = stringFormatter.validateAndEscapeProjectShortname( shortname, - errorFun = throw BadRequestException(s"The supplied short name: '$shortname' is not valid.")) + errorFun = throw BadRequestException(s"The supplied short name: '$shortname' is not valid.") + ) val validatedLongName = stringFormatter.escapeOptionalString( longname, - errorFun = throw BadRequestException(s"The supplied longname: '$longname' is not valid.")) + errorFun = throw BadRequestException(s"The supplied longname: '$longname' is not valid.") + ) val validatedLogo = stringFormatter.escapeOptionalString( logo, - errorFun = throw BadRequestException(s"The supplied logo: '$logo' is not valid.")) + errorFun = throw BadRequestException(s"The supplied logo: '$logo' is not valid.") + ) val validatedDescriptions: Seq[StringLiteralV2] = description.map { des => val escapedValue = stringFormatter.toSparqlEncodedString( des.value, - errorFun = throw BadRequestException(s"The supplied description: '${des.value}' is not valid.")) + errorFun = throw BadRequestException(s"The supplied description: '${des.value}' is not valid.") + ) StringLiteralV2(value = escapedValue, language = des.language) } - val validatedKeywords = keywords.map( - keyword => - stringFormatter.toSparqlEncodedString( - keyword, - errorFun = throw BadRequestException(s"The supplied keyword: '$keyword' is not valid."))) + val validatedKeywords = keywords.map(keyword => + stringFormatter.toSparqlEncodedString( + keyword, + errorFun = throw BadRequestException(s"The supplied keyword: '$keyword' is not valid.") + ) + ) copy( id = maybeProjectIri, shortcode = validatedShortcode, @@ -119,24 +126,25 @@ case class CreateProjectApiRequestADM(id: Option[IRI] = None, } /** - * Represents an API request payload that asks the Knora API server to update an existing project. - * - * @param shortname the new project's shortname. - * @param longname the new project's longname. - * @param description the new project's description. - * @param keywords the new project's keywords. - * @param logo the new project's logo. - * @param status the new project's status. - * @param selfjoin the new project's self-join status. - */ -case class ChangeProjectApiRequestADM(shortname: Option[String] = None, - longname: Option[String] = None, - description: Option[Seq[StringLiteralV2]] = None, - keywords: Option[Seq[String]] = None, - logo: Option[String] = None, - status: Option[Boolean] = None, - selfjoin: Option[Boolean] = None) - extends ProjectsADMJsonProtocol { + * Represents an API request payload that asks the Knora API server to update an existing project. + * + * @param shortname the new project's shortname. + * @param longname the new project's longname. + * @param description the new project's description. + * @param keywords the new project's keywords. + * @param logo the new project's logo. + * @param status the new project's status. + * @param selfjoin the new project's self-join status. + */ +case class ChangeProjectApiRequestADM( + shortname: Option[String] = None, + longname: Option[String] = None, + description: Option[Seq[StringLiteralV2]] = None, + keywords: Option[Seq[String]] = None, + logo: Option[String] = None, + status: Option[Boolean] = None, + selfjoin: Option[Boolean] = None +) extends ProjectsADMJsonProtocol { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val parametersCount: Int = List( @@ -159,15 +167,18 @@ case class ChangeProjectApiRequestADM(shortname: Option[String] = None, val validatedShortname: Option[String] = stringFormatter.validateAndEscapeOptionalProjectShortname( shortname, - errorFun = throw BadRequestException(s"The supplied short name: '$shortname' is not valid.")) + errorFun = throw BadRequestException(s"The supplied short name: '$shortname' is not valid.") + ) val validatedLongName: Option[String] = stringFormatter.escapeOptionalString( longname, - errorFun = throw BadRequestException(s"The supplied longname: '$longname' is not valid.")) + errorFun = throw BadRequestException(s"The supplied longname: '$longname' is not valid.") + ) val validatedLogo: Option[String] = stringFormatter.escapeOptionalString( logo, - errorFun = throw BadRequestException(s"The supplied logo: '$logo' is not valid.")) + errorFun = throw BadRequestException(s"The supplied logo: '$logo' is not valid.") + ) val validatedDescriptions: Option[Seq[StringLiteralV2]] = description match { case Some(descriptions: Seq[StringLiteralV2]) => @@ -175,7 +186,8 @@ case class ChangeProjectApiRequestADM(shortname: Option[String] = None, val escapedValue = stringFormatter.toSparqlEncodedString( des.value, - errorFun = throw BadRequestException(s"The supplied description: '${des.value}' is not valid.")) + errorFun = throw BadRequestException(s"The supplied description: '${des.value}' is not valid.") + ) StringLiteralV2(value = escapedValue, language = des.language) } Some(escapedDescriptions) @@ -184,11 +196,12 @@ case class ChangeProjectApiRequestADM(shortname: Option[String] = None, val validatedKeywords: Option[Seq[String]] = keywords match { case Some(givenKeywords: Seq[String]) => - val escapedKeywords = givenKeywords.map( - keyword => - stringFormatter.toSparqlEncodedString( - keyword, - errorFun = throw BadRequestException(s"The supplied keyword: '$keyword' is not valid."))) + val escapedKeywords = givenKeywords.map(keyword => + stringFormatter.toSparqlEncodedString( + keyword, + errorFun = throw BadRequestException(s"The supplied keyword: '$keyword' is not valid.") + ) + ) Some(escapedKeywords) case None => None } @@ -206,205 +219,215 @@ case class ChangeProjectApiRequestADM(shortname: Option[String] = None, // Messages /** - * An abstract trait representing a request message that can be sent to [[org.knora.webapi.responders.admin.ProjectsResponderADM]]. - */ + * An abstract trait representing a request message that can be sent to [[org.knora.webapi.responders.admin.ProjectsResponderADM]]. + */ sealed trait ProjectsResponderRequestADM extends KnoraRequestADM // Requests /** - * Get all information about all projects in form of [[ProjectsGetResponseADM]]. The ProjectsGetRequestV1 returns either - * something or a NotFound exception if there are no projects found. Administration permission checking is performed. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Get all information about all projects in form of [[ProjectsGetResponseADM]]. The ProjectsGetRequestV1 returns either + * something or a NotFound exception if there are no projects found. Administration permission checking is performed. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class ProjectsGetRequestADM(featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ProjectsResponderRequestADM /** - * Get all information about all projects in form of a sequence of [[ProjectADM]]. Returns an empty sequence if - * no projects are found. Administration permission checking is skipped. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Get all information about all projects in form of a sequence of [[ProjectADM]]. Returns an empty sequence if + * no projects are found. Administration permission checking is skipped. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class ProjectsGetADM(featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ProjectsResponderRequestADM /** - * Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form - * of [[ProjectGetResponseADM]]. External use. - * - * @param identifier the IRI, email, or username of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectGetRequestADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM + * Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form + * of [[ProjectGetResponseADM]]. External use. + * + * @param identifier the IRI, email, or username of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectGetRequestADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form - * of [[ProjectADM]]. Internal use only. - * - * @param identifier the IRI, email, or username of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectGetADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM + * Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form + * of [[ProjectADM]]. Internal use only. + * + * @param identifier the IRI, email, or username of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectGetADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Returns all users belonging to a project identified either through its IRI, shortname or shortcode. - * - * @param identifier the IRI, email, or username of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectMembersGetRequestADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM + * Returns all users belonging to a project identified either through its IRI, shortname or shortcode. + * + * @param identifier the IRI, email, or username of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectMembersGetRequestADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Returns all admin users of a project identified either through its IRI, shortname or shortcode. - * - * @param identifier the IRI, email, or username of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectAdminMembersGetRequestADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM + * Returns all admin users of a project identified either through its IRI, shortname or shortcode. + * + * @param identifier the IRI, email, or username of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectAdminMembersGetRequestADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Returns all unique keywords for all projects. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Returns all unique keywords for all projects. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class ProjectsKeywordsGetRequestADM(featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ProjectsResponderRequestADM /** - * Returns all keywords for a project identified through IRI. - * - * @param projectIri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectKeywordsGetRequestADM(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM + * Returns all keywords for a project identified through IRI. + * + * @param projectIri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectKeywordsGetRequestADM( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Return project's RestrictedView settings. A successful response will be a [[ProjectRestrictedViewSettingsADM]] - * - * @param identifier the identifier of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Return project's RestrictedView settings. A successful response will be a [[ProjectRestrictedViewSettingsADM]] + * + * @param identifier the identifier of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ @ApiMayChange -case class ProjectRestrictedViewSettingsGetADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM +case class ProjectRestrictedViewSettingsGetADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Return project's RestrictedView settings. A successful response will be a [[ProjectRestrictedViewSettingsGetResponseADM]]. - * - * @param identifier the identifier of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Return project's RestrictedView settings. A successful response will be a [[ProjectRestrictedViewSettingsGetResponseADM]]. + * + * @param identifier the identifier of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ @ApiMayChange -case class ProjectRestrictedViewSettingsGetRequestADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM +case class ProjectRestrictedViewSettingsGetRequestADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Requests all the data in the project. A successful response will be a [[ProjectDataGetResponseADM]]. - * - * @param projectIdentifier the identifier of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectDataGetRequestADM(projectIdentifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ProjectsResponderRequestADM + * Requests all the data in the project. A successful response will be a [[ProjectDataGetResponseADM]]. + * + * @param projectIdentifier the identifier of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectDataGetRequestADM( + projectIdentifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ProjectsResponderRequestADM /** - * Requests the creation of a new project. - * - * @param createRequest the [[CreateProjectApiRequestADM]] information for creation a new project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class ProjectCreateRequestADM(createRequest: CreateProjectApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ProjectsResponderRequestADM + * Requests the creation of a new project. + * + * @param createRequest the [[CreateProjectApiRequestADM]] information for creation a new project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class ProjectCreateRequestADM( + createRequest: CreateProjectApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ProjectsResponderRequestADM /** - * Requests updating an existing project. - * - * @param projectIri the IRI of the project to be updated. - * @param changeProjectRequest the data which needs to be update. - * @param requestingUser the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class ProjectChangeRequestADM(projectIri: IRI, - changeProjectRequest: ChangeProjectApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ProjectsResponderRequestADM + * Requests updating an existing project. + * + * @param projectIri the IRI of the project to be updated. + * @param changeProjectRequest the data which needs to be update. + * @param requestingUser the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class ProjectChangeRequestADM( + projectIri: IRI, + changeProjectRequest: ChangeProjectApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ProjectsResponderRequestADM // Responses /** - * Represents the Knora API ADM JSON response to a request for information about all projects. - * - * @param projects information about all existing projects. - */ + * Represents the Knora API ADM JSON response to a request for information about all projects. + * + * @param projects information about all existing projects. + */ case class ProjectsGetResponseADM(projects: Seq[ProjectADM]) extends KnoraResponseADM with ProjectsADMJsonProtocol { def toJsValue: JsValue = projectsResponseADMFormat.write(this) } /** - * Represents the Knora API ADM JSON response to a request for information about a single project. - * - * @param project all information about the project. - */ + * Represents the Knora API ADM JSON response to a request for information about a single project. + * + * @param project all information about the project. + */ case class ProjectGetResponseADM(project: ProjectADM) extends KnoraResponseADM with ProjectsADMJsonProtocol { def toJsValue: JsValue = projectResponseADMFormat.write(this) } /** - * Represents the Knora API ADM JSON response to a request for a list of members inside a single project. - * - * @param members a list of members. - */ + * Represents the Knora API ADM JSON response to a request for a list of members inside a single project. + * + * @param members a list of members. + */ case class ProjectMembersGetResponseADM(members: Seq[UserADM]) extends KnoraResponseADM with ProjectsADMJsonProtocol { def toJsValue: JsValue = projectMembersGetResponseADMFormat.write(this) } /** - * Represents the Knora API ADM JSON response to a request for a list of admin members inside a single project. - * - * @param members a list of admin members. - */ + * Represents the Knora API ADM JSON response to a request for a list of admin members inside a single project. + * + * @param members a list of admin members. + */ case class ProjectAdminMembersGetResponseADM(members: Seq[UserADM]) extends KnoraResponseADM with ProjectsADMJsonProtocol { @@ -413,28 +436,28 @@ case class ProjectAdminMembersGetResponseADM(members: Seq[UserADM]) } /** - * Represents a response to a request for all keywords of all projects. - * - * @param keywords a list of keywords. - */ + * Represents a response to a request for all keywords of all projects. + * + * @param keywords a list of keywords. + */ case class ProjectsKeywordsGetResponseADM(keywords: Seq[String]) extends KnoraResponseADM with ProjectsADMJsonProtocol { def toJsValue: JsValue = projectsKeywordsGetResponseADMFormat.write(this) } /** - * Represents a response to a request for all keywords of a single project. - * - * @param keywords a list of keywords. - */ + * Represents a response to a request for all keywords of a single project. + * + * @param keywords a list of keywords. + */ case class ProjectKeywordsGetResponseADM(keywords: Seq[String]) extends KnoraResponseADM with ProjectsADMJsonProtocol { def toJsValue: JsValue = projectKeywordsGetResponseADMFormat.write(this) } /** - * API MAY CHANGE: Represents a response to a request for the project's restricted view settings. - * - * @param settings the restricted view settings. - */ + * API MAY CHANGE: Represents a response to a request for the project's restricted view settings. + * + * @param settings the restricted view settings. + */ @ApiMayChange case class ProjectRestrictedViewSettingsGetResponseADM(settings: ProjectRestrictedViewSettingsADM) extends KnoraResponseADM @@ -443,57 +466,58 @@ case class ProjectRestrictedViewSettingsGetResponseADM(settings: ProjectRestrict } /** - * Represents an answer to a project creating/modifying operation. - * - * @param project the new project info of the created/modified project. - */ + * Represents an answer to a project creating/modifying operation. + * + * @param project the new project info of the created/modified project. + */ case class ProjectOperationResponseADM(project: ProjectADM) extends KnoraResponseADM with ProjectsADMJsonProtocol { def toJsValue: JsValue = projectOperationResponseADMFormat.write(this) } /** - * Represents a project's data in TriG format. - * - * @param projectDataFile a file containing the project's data in TriG format. - */ + * Represents a project's data in TriG format. + * + * @param projectDataFile a file containing the project's data in TriG format. + */ case class ProjectDataGetResponseADM(projectDataFile: Path) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Components of messages /** - * Represents basic information about a project. - * - * @param id The project's IRI. - * @param shortname The project's shortname. [[ServerUnique]]. - * @param shortcode The project's shortcode. [[ServerUnique]]. - * @param longname The project's long name. - * @param description The project's description. - * @param keywords The project's keywords. - * @param logo The project's logo. - * @param ontologies The project's ontologies. - * @param status The project's status. - * @param selfjoin The project's self-join status. - */ -case class ProjectADM(id: IRI, - @ServerUnique shortname: String, - @ServerUnique shortcode: String, - longname: Option[String], - description: Seq[StringLiteralV2], - keywords: Seq[String], - logo: Option[String], - ontologies: Seq[IRI], - status: Boolean, - selfjoin: Boolean) - extends Ordered[ProjectADM] { + * Represents basic information about a project. + * + * @param id The project's IRI. + * @param shortname The project's shortname. [[ServerUnique]]. + * @param shortcode The project's shortcode. [[ServerUnique]]. + * @param longname The project's long name. + * @param description The project's description. + * @param keywords The project's keywords. + * @param logo The project's logo. + * @param ontologies The project's ontologies. + * @param status The project's status. + * @param selfjoin The project's self-join status. + */ +case class ProjectADM( + id: IRI, + @ServerUnique shortname: String, + @ServerUnique shortcode: String, + longname: Option[String], + description: Seq[StringLiteralV2], + keywords: Seq[String], + logo: Option[String], + ontologies: Seq[IRI], + status: Boolean, + selfjoin: Boolean +) extends Ordered[ProjectADM] { if (description.isEmpty) { throw OntologyConstraintException("Project description is a required property.") } /** - * Allows to sort collections of ProjectADM. Sorting is done by the id. - */ + * Allows to sort collections of ProjectADM. Sorting is done by the id. + */ def compare(that: ProjectADM): Int = this.id.compareTo(that.id) // ToDo: Refactor by using implicit conversions (when I manage to understand them) @@ -526,7 +550,7 @@ case class ProjectADM(id: IRI, ) } - override def equals(that: Any): Boolean = { + override def equals(that: Any): Boolean = // Ignore the order of sequences when testing equality for this class. that match { case otherProj: ProjectADM => @@ -543,9 +567,8 @@ case class ProjectADM(id: IRI, case _ => false } - } - override def hashCode(): Int = { + override def hashCode(): Int = // Ignore the order of sequences when generating hash codes for this class. new HashCodeBuilder(19, 39) .append(id) @@ -559,12 +582,12 @@ case class ProjectADM(id: IRI, .append(status) .append(selfjoin) .toHashCode - } def unescape: ProjectADM = { val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val unescapedDescriptions: Seq[StringLiteralV2] = description.map(desc => - StringLiteralV2(value = stringFormatter.fromSparqlEncodedString(desc.value), language = desc.language)) + StringLiteralV2(value = stringFormatter.fromSparqlEncodedString(desc.value), language = desc.language) + ) val unescapedKeywords: Seq[String] = keywords.map(key => stringFormatter.fromSparqlEncodedString(key)) copy( shortcode = stringFormatter.fromSparqlEncodedString(shortcode), @@ -578,12 +601,13 @@ case class ProjectADM(id: IRI, } /** - * The ProjectIdentifierADM factory object, making sure that all necessary checks are performed and all inputs - * validated and escaped. - */ + * The ProjectIdentifierADM factory object, making sure that all necessary checks are performed and all inputs + * validated and escaped. + */ object ProjectIdentifierADM { def apply(maybeIri: Option[IRI] = None, maybeShortname: Option[String] = None, maybeShortcode: Option[String] = None)( - implicit sf: StringFormatter): ProjectIdentifierADM = { + implicit sf: StringFormatter + ): ProjectIdentifierADM = { val parametersCount: Int = List( maybeIri, @@ -601,24 +625,28 @@ object ProjectIdentifierADM { sf.validateAndEscapeOptionalProjectIri(maybeIri, throw BadRequestException(s"Invalid user project $maybeIri")), maybeShortname = sf.validateAndEscapeOptionalProjectShortname( maybeShortname, - throw BadRequestException(s"Invalid user project shortname $maybeShortname")), + throw BadRequestException(s"Invalid user project shortname $maybeShortname") + ), maybeShortcode = sf.validateAndEscapeOptionalProjectShortcode( maybeShortcode, - throw BadRequestException(s"Invalid user project shortcode $maybeShortcode")) + throw BadRequestException(s"Invalid user project shortcode $maybeShortcode") + ) ) } } /** - * Represents the project's identifier. It can be an IRI, shortcode or shortname. - * - * @param maybeIri the project's IRI. - * @param maybeShortname the project's shortname. - * @param maybeShortcode the project's shortcode - */ -class ProjectIdentifierADM private (maybeIri: Option[IRI] = None, - maybeShortname: Option[String] = None, - maybeShortcode: Option[String] = None) { + * Represents the project's identifier. It can be an IRI, shortcode or shortname. + * + * @param maybeIri the project's IRI. + * @param maybeShortname the project's shortname. + * @param maybeShortcode the project's shortcode + */ +class ProjectIdentifierADM private ( + maybeIri: Option[IRI] = None, + maybeShortname: Option[String] = None, + maybeShortcode: Option[String] = None +) { // squash and return value. val value: String = List( @@ -627,8 +655,7 @@ class ProjectIdentifierADM private (maybeIri: Option[IRI] = None, maybeShortcode ).flatten.head - def hasType: ProjectIdentifierType.Value = { - + def hasType: ProjectIdentifierType.Value = if (maybeIri.isDefined) { ProjectIdentifierType.IRI } else if (maybeShortcode.isDefined) { @@ -636,70 +663,63 @@ class ProjectIdentifierADM private (maybeIri: Option[IRI] = None, } else { ProjectIdentifierType.SHORTNAME } - } /** - * Tries to return the value as an IRI. - */ - def toIri: IRI = { + * Tries to return the value as an IRI. + */ + def toIri: IRI = maybeIri.getOrElse( - throw DataConversionException(s"Identifier $value is not of the required 'ProjectIdentifierType.IRI' type.")) - } + throw DataConversionException(s"Identifier $value is not of the required 'ProjectIdentifierType.IRI' type.") + ) /** - * Returns an optional value of the identifier. - */ - def toIriOption: Option[IRI] = { + * Returns an optional value of the identifier. + */ + def toIriOption: Option[IRI] = maybeIri - } /** - * Tries to return the value as an SHORTNAME. - */ - def toShortname: String = { + * Tries to return the value as an SHORTNAME. + */ + def toShortname: String = maybeShortname.getOrElse( - throw DataConversionException( - s"Identifier $value is not of the required 'ProjectIdentifierType.SHORTNAME' type.")) - } + throw DataConversionException(s"Identifier $value is not of the required 'ProjectIdentifierType.SHORTNAME' type.") + ) /** - * Returns an optional value of the identifier. - */ - def toShortnameOption: Option[String] = { + * Returns an optional value of the identifier. + */ + def toShortnameOption: Option[String] = maybeShortname - } /** - * Tries to return the value as an SHORTCODE. - */ - def toShortcode: String = { + * Tries to return the value as an SHORTCODE. + */ + def toShortcode: String = maybeShortcode.getOrElse( - throw DataConversionException( - s"Identifier $value is not of the required 'ProjectIdentifierType.SHORTCODE' type.")) - } + throw DataConversionException(s"Identifier $value is not of the required 'ProjectIdentifierType.SHORTCODE' type.") + ) /** - * Returns an optional value of the identifier. - */ - def toShortcodeOption: Option[String] = { + * Returns an optional value of the identifier. + */ + def toShortcodeOption: Option[String] = maybeShortcode - } /** - * Returns the string representation - */ - override def toString: IRI = { + * Returns the string representation + */ + override def toString: IRI = s"ProjectIdentifierADM(${this.value})" - } } /** - * Project identifier types: - * - IRI - * - Shortcode - * - Shortname - */ + * Project identifier types: + * - IRI + * - Shortcode + * - Shortname + */ object ProjectIdentifierType extends Enumeration { type ProjectIdentifierType @@ -710,88 +730,105 @@ object ProjectIdentifierType extends Enumeration { } /** - * API MAY CHANGE: Represents the project's restricted view settings. - * - * @param size the restricted view size. - * @param watermark the watermark file. - */ + * API MAY CHANGE: Represents the project's restricted view settings. + * + * @param size the restricted view size. + * @param watermark the watermark file. + */ @ApiMayChange case class ProjectRestrictedViewSettingsADM(size: Option[String] = None, watermark: Option[String] = None) /** - * Payload used for updating of an existing project. - * - * @param shortname The project's shortname. Needs to be system wide unique. - * @param longname The project's long name. - * @param description The project's description. - * @param keywords The project's keywords. - * @param logo The project's logo. - * @param status The project's status. - * @param selfjoin The project's self-join status. - */ -case class ProjectUpdatePayloadADM(shortname: Option[String] = None, - longname: Option[String] = None, - description: Option[Seq[StringLiteralV2]] = None, - keywords: Option[Seq[String]] = None, - logo: Option[String] = None, - status: Option[Boolean] = None, - selfjoin: Option[Boolean] = None) + * Payload used for updating of an existing project. + * + * @param shortname The project's shortname. Needs to be system wide unique. + * @param longname The project's long name. + * @param description The project's description. + * @param keywords The project's keywords. + * @param logo The project's logo. + * @param status The project's status. + * @param selfjoin The project's self-join status. + */ +case class ProjectUpdatePayloadADM( + shortname: Option[String] = None, + longname: Option[String] = None, + description: Option[Seq[StringLiteralV2]] = None, + keywords: Option[Seq[String]] = None, + logo: Option[String] = None, + status: Option[Boolean] = None, + selfjoin: Option[Boolean] = None +) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formating /** - * A spray-json protocol for generating Knora API v1 JSON providing data about projects. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about projects. + */ trait ProjectsADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with TriplestoreJsonProtocol { import org.knora.webapi.messages.admin.responder.usersmessages.UsersADMJsonProtocol._ implicit val projectADMFormat: JsonFormat[ProjectADM] = lazyFormat( - jsonFormat(ProjectADM, - "id", - "shortname", - "shortcode", - "longname", - "description", - "keywords", - "logo", - "ontologies", - "status", - "selfjoin")) + jsonFormat( + ProjectADM, + "id", + "shortname", + "shortcode", + "longname", + "description", + "keywords", + "logo", + "ontologies", + "status", + "selfjoin" + ) + ) implicit val projectsResponseADMFormat: RootJsonFormat[ProjectsGetResponseADM] = rootFormat( - lazyFormat(jsonFormat(ProjectsGetResponseADM, "projects"))) + lazyFormat(jsonFormat(ProjectsGetResponseADM, "projects")) + ) implicit val projectResponseADMFormat: RootJsonFormat[ProjectGetResponseADM] = rootFormat( - lazyFormat(jsonFormat(ProjectGetResponseADM, "project"))) + lazyFormat(jsonFormat(ProjectGetResponseADM, "project")) + ) implicit val projectRestrictedViewSettingsADMFormat: RootJsonFormat[ProjectRestrictedViewSettingsADM] = jsonFormat(ProjectRestrictedViewSettingsADM, "size", "watermark") implicit val projectAdminMembersGetResponseADMFormat: RootJsonFormat[ProjectAdminMembersGetResponseADM] = rootFormat( - lazyFormat(jsonFormat(ProjectAdminMembersGetResponseADM, "members"))) + lazyFormat(jsonFormat(ProjectAdminMembersGetResponseADM, "members")) + ) implicit val projectMembersGetResponseADMFormat: RootJsonFormat[ProjectMembersGetResponseADM] = rootFormat( - lazyFormat(jsonFormat(ProjectMembersGetResponseADM, "members"))) + lazyFormat(jsonFormat(ProjectMembersGetResponseADM, "members")) + ) implicit val createProjectApiRequestADMFormat: RootJsonFormat[CreateProjectApiRequestADM] = rootFormat( lazyFormat( - jsonFormat(CreateProjectApiRequestADM, - "id", - "shortname", - "shortcode", - "longname", - "description", - "keywords", - "logo", - "status", - "selfjoin"))) + jsonFormat( + CreateProjectApiRequestADM, + "id", + "shortname", + "shortcode", + "longname", + "description", + "keywords", + "logo", + "status", + "selfjoin" + ) + ) + ) implicit val changeProjectApiRequestADMFormat: RootJsonFormat[ChangeProjectApiRequestADM] = rootFormat( lazyFormat( - jsonFormat(ChangeProjectApiRequestADM, - "shortname", - "longname", - "description", - "keywords", - "logo", - "status", - "selfjoin"))) + jsonFormat( + ChangeProjectApiRequestADM, + "shortname", + "longname", + "description", + "keywords", + "logo", + "status", + "selfjoin" + ) + ) + ) implicit val projectsKeywordsGetResponseADMFormat: RootJsonFormat[ProjectsKeywordsGetResponseADM] = jsonFormat(ProjectsKeywordsGetResponseADM, "keywords") implicit val projectKeywordsGetResponseADMFormat: RootJsonFormat[ProjectKeywordsGetResponseADM] = @@ -800,6 +837,7 @@ trait ProjectsADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol jsonFormat(ProjectRestrictedViewSettingsGetResponseADM, "settings") implicit val projectOperationResponseADMFormat: RootJsonFormat[ProjectOperationResponseADM] = rootFormat( - lazyFormat(jsonFormat(ProjectOperationResponseADM, "project"))) + lazyFormat(jsonFormat(ProjectOperationResponseADM, "project")) + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/sipimessages/SipiMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/sipimessages/SipiMessagesADM.scala index b63600cdf4..dcb5a605ec 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/sipimessages/SipiMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/sipimessages/SipiMessagesADM.scala @@ -30,33 +30,35 @@ import org.knora.webapi.messages.admin.responder.{KnoraRequestADM, KnoraResponse import spray.json.{DefaultJsonProtocol, JsValue, NullOptions, RootJsonFormat} /** - * An abstract trait representing a Knora v1 API request message that can be sent to `SipiResponderV2`. - */ + * An abstract trait representing a Knora v1 API request message that can be sent to `SipiResponderV2`. + */ sealed trait SipiResponderRequestADM extends KnoraRequestADM /** - * A Knora v1 API request message that requests information about a `FileValue`. - * - * @param projectID the project shortcode. - * @param filename the name of the file belonging to the file value to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the profile of the user making the request. - */ -case class SipiFileInfoGetRequestADM(projectID: String, - filename: String, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SipiResponderRequestADM + * A Knora v1 API request message that requests information about a `FileValue`. + * + * @param projectID the project shortcode. + * @param filename the name of the file belonging to the file value to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the profile of the user making the request. + */ +case class SipiFileInfoGetRequestADM( + projectID: String, + filename: String, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SipiResponderRequestADM /** - * Represents the JSON response to a request for a information about a `FileValue`. - * - * @param permissionCode a code representing the user's maximum permission on the file. - * @param restrictedViewSettings the project's restricted view settings. - */ -case class SipiFileInfoGetResponseADM(permissionCode: Int, - restrictedViewSettings: Option[ProjectRestrictedViewSettingsADM]) - extends KnoraResponseADM { + * Represents the JSON response to a request for a information about a `FileValue`. + * + * @param permissionCode a code representing the user's maximum permission on the file. + * @param restrictedViewSettings the project's restricted view settings. + */ +case class SipiFileInfoGetResponseADM( + permissionCode: Int, + restrictedViewSettings: Option[ProjectRestrictedViewSettingsADM] +) extends KnoraResponseADM { def toJsValue: JsValue = SipiResponderResponseADMJsonProtocol.sipiFileInfoGetResponseADMFormat.write(this) } @@ -64,8 +66,8 @@ case class SipiFileInfoGetResponseADM(permissionCode: Int, // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON providing data about representations of a resource. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about representations of a resource. + */ object SipiResponderResponseADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol @@ -73,5 +75,6 @@ object SipiResponderResponseADMJsonProtocol with ProjectsADMJsonProtocol { implicit val sipiFileInfoGetResponseADMFormat: RootJsonFormat[SipiFileInfoGetResponseADM] = jsonFormat2( - SipiFileInfoGetResponseADM) + SipiFileInfoGetResponseADM + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/storesmessages/StoresMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/storesmessages/StoresMessagesADM.scala index 9e04d99d81..555d8281ab 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/storesmessages/StoresMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/storesmessages/StoresMessagesADM.scala @@ -31,18 +31,19 @@ import spray.json._ sealed trait StoreResponderRequestADM extends KnoraRequestADM /** - * Requests to load the triplestore with data referenced inside [[RdfDataObject]]. Any data contained inside the - * triplestore will be deleted first. - * - * @param rdfDataObjects a sequence of [[RdfDataObject]] objects containing the path to the data and the name of - * the named graph into which the data should be loaded. - * @param prependDefaults should a default set of [[RdfDataObject]]s be prepended. The default is `false`. - * @param featureFactoryConfig the feature factory configuration. - */ -case class ResetTriplestoreContentRequestADM(rdfDataObjects: Seq[RdfDataObject], - prependDefaults: Boolean = false, - featureFactoryConfig: FeatureFactoryConfig) - extends StoreResponderRequestADM + * Requests to load the triplestore with data referenced inside [[RdfDataObject]]. Any data contained inside the + * triplestore will be deleted first. + * + * @param rdfDataObjects a sequence of [[RdfDataObject]] objects containing the path to the data and the name of + * the named graph into which the data should be loaded. + * @param prependDefaults should a default set of [[RdfDataObject]]s be prepended. The default is `false`. + * @param featureFactoryConfig the feature factory configuration. + */ +case class ResetTriplestoreContentRequestADM( + rdfDataObjects: Seq[RdfDataObject], + prependDefaults: Boolean = false, + featureFactoryConfig: FeatureFactoryConfig +) extends StoreResponderRequestADM case class ResetTriplestoreContentResponseADM(message: String) extends KnoraResponseADM with StoresADMJsonProtocol { def toJsValue = resetTriplestoreContentResponseADMFormat.write(this) @@ -52,8 +53,8 @@ case class ResetTriplestoreContentResponseADM(message: String) extends KnoraResp // JSON formatting /** - * A spray-json protocol for generating Knora API ADM JSON for property values. - */ + * A spray-json protocol for generating Knora API ADM JSON for property values. + */ trait StoresADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with TriplestoreJsonProtocol { /* Very strange construct at the end is needed, but I don't really understand why and what it means */ diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala index 8f5f8f1d53..30cd8275d0 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala @@ -3,11 +3,11 @@ package org.knora.webapi.messages.admin.responder.usersmessages import org.knora.webapi.IRI sealed trait ValidationError -case object InvalidUsername extends ValidationError -case object InvalidEmail extends ValidationError +case object InvalidUsername extends ValidationError +case object InvalidEmail extends ValidationError case object InvalidGivenOrFamilyName extends ValidationError -case object InvalidPassword extends ValidationError -case object InvalidLanguageCode extends ValidationError +case object InvalidPassword extends ValidationError +case object InvalidLanguageCode extends ValidationError trait UserCreatePayloadTraitADM { def create( diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala index cbc1da7122..55679324d1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala @@ -618,8 +618,8 @@ case class UserADM( */ def isSelf(identifier: UserIdentifierADM): Boolean = { - val iriEquals = identifier.toIriOption.contains(id) - val emailEquals = identifier.toEmailOption.contains(email) + val iriEquals = identifier.toIriOption.contains(id) + val emailEquals = identifier.toEmailOption.contains(email) val usernameEquals = identifier.toUsernameOption.contains(username) iriEquals || emailEquals || usernameEquals @@ -638,8 +638,8 @@ case class UserADM( def fullname: String = givenName + " " + familyName def getDigest: String = { - val md = java.security.MessageDigest.getInstance("SHA-1") - val time = System.currentTimeMillis().toString + val md = java.security.MessageDigest.getInstance("SHA-1") + val time = System.currentTimeMillis().toString val value = (time + this.toString).getBytes("UTF-8") md.digest(value).map("%02x".format(_)).mkString } @@ -730,10 +730,10 @@ object UserInformationTypeADM extends Enumeration { type UserInformationTypeADM = Value - val PUBLIC: Value = Value(0, "public") // a temporary type which only returns firstname and lastname - val SHORT: Value = Value(1, "short") // only basic user information (restricted and additionally without groups + val PUBLIC: Value = Value(0, "public") // a temporary type which only returns firstname and lastname + val SHORT: Value = Value(1, "short") // only basic user information (restricted and additionally without groups val RESTRICTED: Value = Value(2, "restricted") // without sensitive information - val FULL: Value = Value(3, "full") // everything, including sensitive information + val FULL: Value = Value(3, "full") // everything, including sensitive information val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap @@ -758,8 +758,8 @@ object UserIdentifierType extends Enumeration { type UserIdentifierType - val IRI: Value = Value(0, "iri") - val EMAIL: Value = Value(1, "email") + val IRI: Value = Value(0, "iri") + val EMAIL: Value = Value(1, "email") val USERNAME: Value = Value(3, "username") } @@ -1020,7 +1020,7 @@ object UsersADMJsonProtocol "newPassword" ) implicit val usersGetResponseADMFormat: RootJsonFormat[UsersGetResponseADM] = jsonFormat1(UsersGetResponseADM) - implicit val userProfileResponseADMFormat: RootJsonFormat[UserResponseADM] = jsonFormat1(UserResponseADM) + implicit val userProfileResponseADMFormat: RootJsonFormat[UserResponseADM] = jsonFormat1(UserResponseADM) implicit val userProjectMembershipsGetResponseADMFormat: RootJsonFormat[UserProjectMembershipsGetResponseADM] = jsonFormat1(UserProjectMembershipsGetResponseADM) implicit val userProjectAdminMembershipsGetResponseADMFormat diff --git a/webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala index 32b9e1c4ef..f85e7e8e35 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala @@ -22,157 +22,157 @@ package org.knora.webapi.messages.app.appmessages sealed trait ApplicationRequest /** - * Start Application - * - * @param ignoreRepository if `true`, don't read anything from the repository on startup. - * @param requiresIIIFService if `true`, ensure that the IIIF service is started. - * @param retryCnt how many times was this command tried - */ + * Start Application + * + * @param ignoreRepository if `true`, don't read anything from the repository on startup. + * @param requiresIIIFService if `true`, ensure that the IIIF service is started. + * @param retryCnt how many times was this command tried + */ case class AppStart(ignoreRepository: Boolean, requiresIIIFService: Boolean, retryCnt: Int = 0) extends ApplicationRequest /** - * After a successful bind, the ApplicationActor will receive this message and - * change his behaviour to ready. - */ + * After a successful bind, the ApplicationActor will receive this message and + * change his behaviour to ready. + */ case class AppReady() extends ApplicationRequest /** - * Stop Application - */ + * Stop Application + */ case class AppStop() extends ApplicationRequest /** - * Check if actor is ready. - */ + * Check if actor is ready. + */ case class ActorReady() extends ApplicationRequest /** - * Response used to acknowledge that actor is ready. - */ + * Response used to acknowledge that actor is ready. + */ case class ActorReadyAck() /** - * Setter message for storing the LoadDemoData flag. - */ + * Setter message for storing the LoadDemoData flag. + */ case class SetLoadDemoDataState(value: Boolean) extends ApplicationRequest /** - * Getter message for retrieving the LoadDemoData flag value. - */ + * Getter message for retrieving the LoadDemoData flag value. + */ case class GetLoadDemoDataState() extends ApplicationRequest /** - * Setter message for storing the llowReloadOverHTTP flag. - */ + * Setter message for storing the llowReloadOverHTTP flag. + */ case class SetAllowReloadOverHTTPState(value: Boolean) extends ApplicationRequest /** - * Getter message for retrieving the llowReloadOverHTTP flag value. - */ + * Getter message for retrieving the llowReloadOverHTTP flag value. + */ case class GetAllowReloadOverHTTPState() extends ApplicationRequest /** - * Setter message for storing the rometheusReporter flag. - */ + * Setter message for storing the rometheusReporter flag. + */ case class SetPrometheusReporterState(value: Boolean) extends ApplicationRequest /** - * Getter message for retrieving the rometheusReporter flag value. - */ + * Getter message for retrieving the rometheusReporter flag value. + */ case class GetPrometheusReporterState() extends ApplicationRequest /** - * Setter message for storing the ZipkinReporter flag. - */ + * Setter message for storing the ZipkinReporter flag. + */ case class SetZipkinReporterState(value: Boolean) extends ApplicationRequest /** - * Getter message for retrieving the ZipkinReporter flag value. - */ + * Getter message for retrieving the ZipkinReporter flag value. + */ case class GetZipkinReporterState() extends ApplicationRequest /** - * Setter message for storing the JaegerReporter flag. - */ + * Setter message for storing the JaegerReporter flag. + */ case class SetJaegerReporterState(value: Boolean) extends ApplicationRequest /** - * Getter message for retrieving the JaegerReporter flag value. - */ + * Getter message for retrieving the JaegerReporter flag value. + */ case class GetJaegerReporterState() extends ApplicationRequest /** - * Setter message for storing the PrintConfigExtended flag. - */ + * Setter message for storing the PrintConfigExtended flag. + */ case class SetPrintConfigExtendedState(value: Boolean) extends ApplicationRequest /** - * Getter message for retrieving the PrintConfigExtended flag value. - */ + * Getter message for retrieving the PrintConfigExtended flag value. + */ case class GetPrintConfigExtendedState() extends ApplicationRequest /** - * Setter message for setting the current application state. - */ + * Setter message for setting the current application state. + */ case class SetAppState(value: AppState) extends ApplicationRequest /** - * Message for getting the current application state. - */ + * Message for getting the current application state. + */ case class GetAppState() extends ApplicationRequest /** - * Message for initiating the startup sequence. - * - * @param ignoreRepository if `true`, don't read anything from the repository on startup. - * @param requiresIIIFService if `true`, ensure that the IIIF service is started. - */ + * Message for initiating the startup sequence. + * + * @param ignoreRepository if `true`, don't read anything from the repository on startup. + * @param requiresIIIFService if `true`, ensure that the IIIF service is started. + */ case class InitStartUp(ignoreRepository: Boolean, requiresIIIFService: Boolean) extends ApplicationRequest /** - * Acknowledgment message for [[InitStartUp]]. - */ + * Acknowledgment message for [[InitStartUp]]. + */ case class InitStartUpAck() extends ApplicationRequest /** - * Message for checking whether the triplestore is available. Used only inside the actor itself. - */ + * Message for checking whether the triplestore is available. Used only inside the actor itself. + */ case class CheckTriplestore() extends ApplicationRequest /** - * Message for updating the repository to work the current version of Knora. Used only inside the actor itself. - */ + * Message for updating the repository to work the current version of Knora. Used only inside the actor itself. + */ case class UpdateRepository() extends ApplicationRequest /** - * Message for initiating cache creation. Used only inside the actor itself. - */ + * Message for initiating cache creation. Used only inside the actor itself. + */ case class CreateCaches() extends ApplicationRequest /** - * Message for updating the triplestore's full-text search index. Used only inside the actor itself. - */ + * Message for updating the triplestore's full-text search index. Used only inside the actor itself. + */ case class UpdateSearchIndex() extends ApplicationRequest /** - * Message for initiating loading of ontologies. Used only inside the actor itself. - */ + * Message for initiating loading of ontologies. Used only inside the actor itself. + */ case class LoadOntologies() extends ApplicationRequest /** - * Message for initiating IIIF Service checking. Used only inside the actor itself. - */ + * Message for initiating IIIF Service checking. Used only inside the actor itself. + */ case object CheckIIIFService extends ApplicationRequest /** - * Message for initiating Cache Service checking. Used only inside the actor itself. - */ + * Message for initiating Cache Service checking. Used only inside the actor itself. + */ case object CheckCacheService extends ApplicationRequest /** - * Application States at Startup - */ + * Application States at Startup + */ sealed trait AppState object AppStates { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/cacheservicemessages/CacheServiceMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/cacheservicemessages/CacheServiceMessages.scala index 5f067ea286..815ed8a46d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/cacheservicemessages/CacheServiceMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/cacheservicemessages/CacheServiceMessages.scala @@ -26,66 +26,66 @@ import org.knora.webapi.messages.store.StoreRequest sealed trait CacheServiceRequest extends StoreRequest /** - * Message equesting to write project to cache. - */ + * Message equesting to write project to cache. + */ case class CacheServicePutProjectADM(value: ProjectADM) extends CacheServiceRequest /** - * Message requesting to retrieve project from cache. - */ + * Message requesting to retrieve project from cache. + */ case class CacheServiceGetProjectADM(identifier: ProjectIdentifierADM) extends CacheServiceRequest /** - * Message requesting to write user to cache. - */ + * Message requesting to write user to cache. + */ case class CacheServicePutUserADM(value: UserADM) extends CacheServiceRequest /** - * Message requesting to retrieve user from cache. - */ + * Message requesting to retrieve user from cache. + */ case class CacheServiceGetUserADM(identifier: UserIdentifierADM) extends CacheServiceRequest /** - * Message requesting to store a simple string under the supplied key. - */ + * Message requesting to store a simple string under the supplied key. + */ case class CacheServicePutString(key: String, value: String) extends CacheServiceRequest /** - * Message requesting to retrieve simple string stored under the key. - */ + * Message requesting to retrieve simple string stored under the key. + */ case class CacheServiceGetString(key: Option[String]) extends CacheServiceRequest /** - * Message requesting to remove anything stored under the keys. - */ + * Message requesting to remove anything stored under the keys. + */ case class CacheServiceRemoveValues(keys: Set[String]) extends CacheServiceRequest /** - * Message requesting to completely empty the cache (wipe everything). - */ + * Message requesting to completely empty the cache (wipe everything). + */ case class CacheServiceFlushDB(requestingUser: UserADM) extends CacheServiceRequest /** - * Message acknowledging the flush. - */ + * Message acknowledging the flush. + */ case class CacheServiceFlushDBACK() /** - * Queries Cache Service status. - */ + * Queries Cache Service status. + */ case object CacheServiceGetStatus extends CacheServiceRequest /** - * Represents a response for [[CacheServiceGetStatus]]. - */ + * Represents a response for [[CacheServiceGetStatus]]. + */ sealed trait CacheServiceStatusResponse /** - * Represents a positive response for [[CacheServiceGetStatus]]. - */ + * Represents a positive response for [[CacheServiceGetStatus]]. + */ case object CacheServiceStatusOK extends CacheServiceStatusResponse /** - * Represents a negative response for [[CacheServiceGetStatus]]. - */ + * Represents a negative response for [[CacheServiceGetStatus]]. + */ case object CacheServiceStatusNOK extends CacheServiceStatusResponse diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/sipimessages/SipiMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/sipimessages/SipiMessages.scala index e2035679fe..c24b1fd114 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/sipimessages/SipiMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/sipimessages/SipiMessages.scala @@ -24,78 +24,80 @@ import org.knora.webapi.messages.store.StoreRequest import org.knora.webapi.messages.traits.RequestWithSender /** - * An abstract trait for messages that can be sent to the [[org.knora.webapi.store.iiif.IIIFManager]] - */ + * An abstract trait for messages that can be sent to the [[org.knora.webapi.store.iiif.IIIFManager]] + */ sealed trait IIIFRequest extends StoreRequest /** - * An abstract trait for messages that can be sent to [[org.knora.webapi.store.iiif.SipiConnector]]. - */ + * An abstract trait for messages that can be sent to [[org.knora.webapi.store.iiif.SipiConnector]]. + */ sealed trait SipiRequest extends IIIFRequest { def requestingUser: UserADM } /** - * Requests file metadata from Sipi. A successful response is a [[GetFileMetadataResponse]]. - * - * @param fileUrl the URL at which Sipi can serve the file. - * @param requestingUser the user making the request. - */ + * Requests file metadata from Sipi. A successful response is a [[GetFileMetadataResponse]]. + * + * @param fileUrl the URL at which Sipi can serve the file. + * @param requestingUser the user making the request. + */ case class GetFileMetadataRequest(fileUrl: String, requestingUser: UserADM) extends SipiRequest /** - * Represents file metadata returned by Sipi. - * - * @param originalFilename the file's original filename, if known. - * @param originalMimeType the file's original MIME type. - * @param internalMimeType the file's internal MIME type. Always defined (https://dasch.myjetbrains.com/youtrack/issue/DSP-711). - * @param width the file's width in pixels, if applicable. - * @param height the file's height in pixels, if applicable. - * @param pageCount the number of pages in the file, if applicable. - * @param duration the duration of the file in seconds, if applicable. - */ -case class GetFileMetadataResponse(originalFilename: Option[String], - originalMimeType: Option[String], - internalMimeType: String, - width: Option[Int], - height: Option[Int], - pageCount: Option[Int], - duration: Option[BigDecimal], - fps: Option[BigDecimal]) + * Represents file metadata returned by Sipi. + * + * @param originalFilename the file's original filename, if known. + * @param originalMimeType the file's original MIME type. + * @param internalMimeType the file's internal MIME type. Always defined (https://dasch.myjetbrains.com/youtrack/issue/DSP-711). + * @param width the file's width in pixels, if applicable. + * @param height the file's height in pixels, if applicable. + * @param pageCount the number of pages in the file, if applicable. + * @param duration the duration of the file in seconds, if applicable. + */ +case class GetFileMetadataResponse( + originalFilename: Option[String], + originalMimeType: Option[String], + internalMimeType: String, + width: Option[Int], + height: Option[Int], + pageCount: Option[Int], + duration: Option[BigDecimal], + fps: Option[BigDecimal] +) /** - * Asks Sipi to move a file from temporary to permanent storage. - * - * @param internalFilename the name of the file. - * @param prefix the prefix under which the file should be stored. - * @param requestingUser the user making the request. - */ + * Asks Sipi to move a file from temporary to permanent storage. + * + * @param internalFilename the name of the file. + * @param prefix the prefix under which the file should be stored. + * @param requestingUser the user making the request. + */ case class MoveTemporaryFileToPermanentStorageRequest(internalFilename: String, prefix: String, requestingUser: UserADM) extends SipiRequest /** - * Asks Sipi to delete a temporary file. - * - * @param internalFilename the name of the file. - * @param requestingUser the user making the request. - */ + * Asks Sipi to delete a temporary file. + * + * @param internalFilename the name of the file. + * @param requestingUser the user making the request. + */ case class DeleteTemporaryFileRequest(internalFilename: String, requestingUser: UserADM) extends SipiRequest /** - * Asks Sipi for a text file. Currently only for UTF8 encoded text files. - * - * @param fileUrl the URL pointing to the file. - * @param requestingUser the user making the request. - */ + * Asks Sipi for a text file. Currently only for UTF8 encoded text files. + * + * @param fileUrl the URL pointing to the file. + * @param requestingUser the user making the request. + */ case class SipiGetTextFileRequest(fileUrl: String, requestingUser: UserADM, senderName: String) extends SipiRequest with RequestWithSender /** - * Represents a response for [[SipiGetTextFileRequest]]. - * - * @param content the file content. - */ + * Represents a response for [[SipiGetTextFileRequest]]. + * + * @param content the file content. + */ case class SipiGetTextFileResponse(content: String) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -104,21 +106,21 @@ case class SipiGetTextFileResponse(content: String) sealed trait IIIFRequestADM extends IIIFRequest /** - * Queries IIIF Service status. - */ + * Queries IIIF Service status. + */ case object IIIFServiceGetStatus extends IIIFRequestADM /** - * Represents a response for [[IIIFServiceGetStatus]]. - */ + * Represents a response for [[IIIFServiceGetStatus]]. + */ sealed trait IIIFServiceStatusResponse /** - * Represents a positive response for [[IIIFServiceGetStatus]]. - */ + * Represents a positive response for [[IIIFServiceGetStatus]]. + */ case object IIIFServiceStatusOK extends IIIFServiceStatusResponse /** - * Represents a negative response for [[IIIFServiceGetStatus]]. - */ + * Represents a negative response for [[IIIFServiceGetStatus]]. + */ case object IIIFServiceStatusNOK extends IIIFServiceStatusResponse diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/FusekiAPI.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/FusekiAPI.scala index 75caedfea5..de7e097e2b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/FusekiAPI.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/FusekiAPI.scala @@ -23,41 +23,43 @@ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import spray.json.{DefaultJsonProtocol, JsonFormat, RootJsonFormat} /** - * Represents a response from Fuseki about the state of the Fuseki server. - * - * @param version the server version. - * @param built the date when the server was built. - * @param startDateTime the date when the server was started. - * @param uptime the server's uptime. - * @param datasets the datasets available on the server. - */ -case class FusekiServer(version: String, - built: String, - startDateTime: String, - uptime: Int, - datasets: Seq[FusekiDataset]) + * Represents a response from Fuseki about the state of the Fuseki server. + * + * @param version the server version. + * @param built the date when the server was built. + * @param startDateTime the date when the server was started. + * @param uptime the server's uptime. + * @param datasets the datasets available on the server. + */ +case class FusekiServer( + version: String, + built: String, + startDateTime: String, + uptime: Int, + datasets: Seq[FusekiDataset] +) /** - * Represents a Fuseki dataset. - * - * @param dsName the name of the dataset. - * @param dsState the state of the dataset. - * @param dsServices the services available for the dataset. - */ + * Represents a Fuseki dataset. + * + * @param dsName the name of the dataset. + * @param dsState the state of the dataset. + * @param dsServices the services available for the dataset. + */ case class FusekiDataset(dsName: String, dsState: Boolean, dsServices: Seq[FusekiService]) /** - * Represets a service available for a Fuseki dataset. - * - * @param srvType the service type. - * @param srvDescription a description of the service. - * @param srvEndpoints the endpoints provided by the service. - */ + * Represets a service available for a Fuseki dataset. + * + * @param srvType the service type. + * @param srvDescription a description of the service. + * @param srvEndpoints the endpoints provided by the service. + */ case class FusekiService(srvType: String, srvDescription: String, srvEndpoints: Seq[String]) /** - * Parses server status responses from Fuseki. - */ + * Parses server status responses from Fuseki. + */ object FusekiJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol { implicit val fusekiServiceFormat: JsonFormat[FusekiService] = jsonFormat(FusekiService, "srv.type", "srv.description", "srv.endpoints") diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/GraphDBAPI.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/GraphDBAPI.scala index 3caf76951e..e999305fbe 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/GraphDBAPI.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/GraphDBAPI.scala @@ -26,31 +26,34 @@ import spray.json.{DefaultJsonProtocol, RootJsonFormat} sealed trait GraphDBAPI -case class GraphDBRepository(externalUrl: String, - id: String, - local: Boolean, - location: String, - readable: Boolean, - sesameType: String, - title: String, - typeOf: String, - unsupported: Boolean, - uri: String, - writable: Boolean) - extends GraphDBAPI +case class GraphDBRepository( + externalUrl: String, + id: String, + local: Boolean, + location: String, + readable: Boolean, + sesameType: String, + title: String, + typeOf: String, + unsupported: Boolean, + uri: String, + writable: Boolean +) extends GraphDBAPI object GraphDBJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol { // 'typeOf' in the case class is 'type' in json - implicit val graphDBRepositoryFormat: RootJsonFormat[GraphDBRepository] = jsonFormat(GraphDBRepository, - "externalUrl", - "id", - "local", - "location", - "readable", - "sesameType", - "title", - "type", - "unsupported", - "uri", - "writable") + implicit val graphDBRepositoryFormat: RootJsonFormat[GraphDBRepository] = jsonFormat( + GraphDBRepository, + "externalUrl", + "id", + "local", + "location", + "readable", + "sesameType", + "title", + "type", + "unsupported", + "uri", + "writable" + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index 2dbf47288a..7e21200bb1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -45,93 +45,96 @@ import scala.util.{Failure, Success, Try} sealed trait TriplestoreRequest extends StoreRequest /** - * Simple message for initial actor functionality. - */ + * Simple message for initial actor functionality. + */ case class HelloTriplestore(txt: String) extends TriplestoreRequest /** - * Simple message for checking the connection to the triplestore. - */ + * Simple message for checking the connection to the triplestore. + */ case class CheckConnection() extends TriplestoreRequest /** - * Simple message for acknowledging connection check - */ + * Simple message for acknowledging connection check + */ case class CheckConnectionACK() /** - * Represents a SPARQL SELECT query to be sent to the triplestore. A successful response will be a [[SparqlSelectResult]]. - * - * @param sparql the SPARQL string. - */ + * Represents a SPARQL SELECT query to be sent to the triplestore. A successful response will be a [[SparqlSelectResult]]. + * + * @param sparql the SPARQL string. + */ case class SparqlSelectRequest(sparql: String) extends TriplestoreRequest /** - * Represents a SPARQL CONSTRUCT query to be sent to the triplestore. A successful response will be a - * [[SparqlConstructResponse]]. - * - * @param sparql the SPARQL string. - * @param featureFactoryConfig the feature factory configuration. - */ + * Represents a SPARQL CONSTRUCT query to be sent to the triplestore. A successful response will be a + * [[SparqlConstructResponse]]. + * + * @param sparql the SPARQL string. + * @param featureFactoryConfig the feature factory configuration. + */ case class SparqlConstructRequest(sparql: String, featureFactoryConfig: FeatureFactoryConfig) extends TriplestoreRequest /** - * Represents a SPARQL CONSTRUCT query to be sent to the triplestore. The triplestore's will be - * written to the specified file in a quad format. A successful response message will be a [[FileWrittenResponse]]. - * - * @param sparql the SPARQL string. - * @param graphIri the named graph IRI to be used in the TriG file. - * @param outputFile the file to be written. - * @param outputFormat the output file format. - * @param featureFactoryConfig the feature factory configuration. - */ -case class SparqlConstructFileRequest(sparql: String, - graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat, - featureFactoryConfig: FeatureFactoryConfig) - extends TriplestoreRequest + * Represents a SPARQL CONSTRUCT query to be sent to the triplestore. The triplestore's will be + * written to the specified file in a quad format. A successful response message will be a [[FileWrittenResponse]]. + * + * @param sparql the SPARQL string. + * @param graphIri the named graph IRI to be used in the TriG file. + * @param outputFile the file to be written. + * @param outputFormat the output file format. + * @param featureFactoryConfig the feature factory configuration. + */ +case class SparqlConstructFileRequest( + sparql: String, + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat, + featureFactoryConfig: FeatureFactoryConfig +) extends TriplestoreRequest /** - * A response to a [[SparqlConstructRequest]]. - * - * @param statements a map of subject IRIs to statements about each subject. - */ + * A response to a [[SparqlConstructRequest]]. + * + * @param statements a map of subject IRIs to statements about each subject. + */ case class SparqlConstructResponse(statements: Map[IRI, Seq[(IRI, String)]]) /** - * Represents a SPARQL CONSTRUCT query to be sent to the triplestore. A successful response will be a - * [[SparqlExtendedConstructResponse]]. - * - * @param sparql the SPARQL string. - * @param featureFactoryConfig the feature factory configuration. - */ + * Represents a SPARQL CONSTRUCT query to be sent to the triplestore. A successful response will be a + * [[SparqlExtendedConstructResponse]]. + * + * @param sparql the SPARQL string. + * @param featureFactoryConfig the feature factory configuration. + */ case class SparqlExtendedConstructRequest(sparql: String, featureFactoryConfig: FeatureFactoryConfig) extends TriplestoreRequest /** - * Parses Turtle documents and converts them to [[SparqlExtendedConstructResponse]] objects. - */ + * Parses Turtle documents and converts them to [[SparqlExtendedConstructResponse]] objects. + */ object SparqlExtendedConstructResponse { /** - * A map of predicate IRIs to literal objects. - */ + * A map of predicate IRIs to literal objects. + */ type ConstructPredicateObjects = Map[SmartIri, Seq[LiteralV2]] private val logDelimiter = "\n" + StringUtils.repeat('=', 80) + "\n" /** - * Parses a Turtle document, converting it to a [[SparqlExtendedConstructResponse]]. - * - * @param turtleStr the Turtle document. - * @param rdfFormatUtil an [[RdfFormatUtil]]. - * @param log a [[LoggingAdapter]]. - * @return a [[SparqlExtendedConstructResponse]] representing the document. - */ - def parseTurtleResponse(turtleStr: String, - rdfFormatUtil: RdfFormatUtil, - log: LoggingAdapter): Try[SparqlExtendedConstructResponse] = { + * Parses a Turtle document, converting it to a [[SparqlExtendedConstructResponse]]. + * + * @param turtleStr the Turtle document. + * @param rdfFormatUtil an [[RdfFormatUtil]]. + * @param log a [[LoggingAdapter]]. + * @return a [[SparqlExtendedConstructResponse]] representing the document. + */ + def parseTurtleResponse( + turtleStr: String, + rdfFormatUtil: RdfFormatUtil, + log: LoggingAdapter + ): Try[SparqlExtendedConstructResponse] = { val parseTry = Try { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -158,7 +161,8 @@ object SparqlExtendedConstructResponse { IntLiteralV2( datatypeLiteral .integerValue( - throw InconsistentRepositoryDataException(s"Invalid integer: ${datatypeLiteral.value}")) + throw InconsistentRepositoryDataException(s"Invalid integer: ${datatypeLiteral.value}") + ) .toInt ) @@ -173,7 +177,8 @@ object SparqlExtendedConstructResponse { case OntologyConstants.Xsd.Boolean => BooleanLiteralV2( datatypeLiteral.booleanValue( - throw InconsistentRepositoryDataException(s"Invalid xsd:boolean: ${datatypeLiteral.value}")) + throw InconsistentRepositoryDataException(s"Invalid xsd:boolean: ${datatypeLiteral.value}") + ) ) case OntologyConstants.Xsd.String => StringLiteralV2(value = datatypeLiteral.value, language = None) @@ -181,7 +186,8 @@ object SparqlExtendedConstructResponse { case OntologyConstants.Xsd.Decimal => DecimalLiteralV2( datatypeLiteral.decimalValue( - throw InconsistentRepositoryDataException(s"Invalid xsd:decimal: ${datatypeLiteral.value}")) + throw InconsistentRepositoryDataException(s"Invalid xsd:decimal: ${datatypeLiteral.value}") + ) ) case OntologyConstants.Xsd.Uri => IriLiteralV2(datatypeLiteral.value) @@ -218,346 +224,342 @@ object SparqlExtendedConstructResponse { } /** - * A response to a [[SparqlExtendedConstructRequest]]. - * - * @param statements a map of subjects to statements about each subject. - */ + * A response to a [[SparqlExtendedConstructRequest]]. + * + * @param statements a map of subjects to statements about each subject. + */ case class SparqlExtendedConstructResponse( - statements: Map[SubjectV2, SparqlExtendedConstructResponse.ConstructPredicateObjects]) - -/** - * Requests a named graph, which will be written to the specified file. A successful response - * will be a [[FileWrittenResponse]]. - * - * @param graphIri the IRI of the named graph. - * @param outputFile the destination file. - * @param outputFormat the output file format. - * @param featureFactoryConfig the feature factory configuration. - */ -case class NamedGraphFileRequest(graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat, - featureFactoryConfig: FeatureFactoryConfig) - extends TriplestoreRequest + statements: Map[SubjectV2, SparqlExtendedConstructResponse.ConstructPredicateObjects] +) /** - * Requests a named graph, which will be returned as Turtle. A successful response - * will be a [[NamedGraphDataResponse]]. - * - * @param graphIri the IRI of the named graph. - */ + * Requests a named graph, which will be written to the specified file. A successful response + * will be a [[FileWrittenResponse]]. + * + * @param graphIri the IRI of the named graph. + * @param outputFile the destination file. + * @param outputFormat the output file format. + * @param featureFactoryConfig the feature factory configuration. + */ +case class NamedGraphFileRequest( + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat, + featureFactoryConfig: FeatureFactoryConfig +) extends TriplestoreRequest + +/** + * Requests a named graph, which will be returned as Turtle. A successful response + * will be a [[NamedGraphDataResponse]]. + * + * @param graphIri the IRI of the named graph. + */ case class NamedGraphDataRequest(graphIri: IRI) extends TriplestoreRequest /** - * A graph of triples in Turtle format. - */ + * A graph of triples in Turtle format. + */ case class NamedGraphDataResponse(turtle: String) /** - * Represents a SPARQL Update operation to be performed. - * - * @param sparql the SPARQL string. - */ + * Represents a SPARQL Update operation to be performed. + * + * @param sparql the SPARQL string. + */ case class SparqlUpdateRequest(sparql: String) extends TriplestoreRequest /** - * Indicates that the requested SPARQL Update was executed and returned no errors. - */ + * Indicates that the requested SPARQL Update was executed and returned no errors. + */ case class SparqlUpdateResponse() /** - * Represents a SPARQL ASK query to be sent to the triplestore. A successful response will be a - * [[SparqlAskResponse]]. - * - * @param sparql the SPARQL string. - */ + * Represents a SPARQL ASK query to be sent to the triplestore. A successful response will be a + * [[SparqlAskResponse]]. + * + * @param sparql the SPARQL string. + */ case class SparqlAskRequest(sparql: String) extends TriplestoreRequest /** - * Represents a response to a SPARQL ASK query, containing the result. - * - * @param result of the query. - */ + * Represents a response to a SPARQL ASK query, containing the result. + * + * @param result of the query. + */ case class SparqlAskResponse(result: Boolean) /** - * Message for resetting the contents of the repository and loading a fresh set of data. The data needs to be - * stored in an accessible path and supplied via the [[RdfDataObject]]. - * - * @param rdfDataObjects contains a list of [[RdfDataObject]]. - * @param prependDefaults denotes if a default set defined in application.conf should be also loaded - */ + * Message for resetting the contents of the repository and loading a fresh set of data. The data needs to be + * stored in an accessible path and supplied via the [[RdfDataObject]]. + * + * @param rdfDataObjects contains a list of [[RdfDataObject]]. + * @param prependDefaults denotes if a default set defined in application.conf should be also loaded + */ case class ResetRepositoryContent(rdfDataObjects: Seq[RdfDataObject], prependDefaults: Boolean = true) extends TriplestoreRequest /** - * Sent as a response to [[ResetRepositoryContent]] if the request was processed successfully. - */ + * Sent as a response to [[ResetRepositoryContent]] if the request was processed successfully. + */ case class ResetRepositoryContentACK() /** - * Message for removing all content from the repository. - */ + * Message for removing all content from the repository. + */ case class DropAllTRepositoryContent() extends TriplestoreRequest /** - * Sent as a response to [[DropAllTRepositoryContent]] if the request was processed successfully. - */ + * Sent as a response to [[DropAllTRepositoryContent]] if the request was processed successfully. + */ case class DropAllRepositoryContentACK() /** - * Inserts data into the repository. - * - * @param rdfDataObjects contains a list of [[RdfDataObject]]. - */ + * Inserts data into the repository. + * + * @param rdfDataObjects contains a list of [[RdfDataObject]]. + */ case class InsertRepositoryContent(rdfDataObjects: Seq[RdfDataObject]) extends TriplestoreRequest /** - * Sent as a response to [[InsertRepositoryContent]] if the request was processed successfully. - */ + * Sent as a response to [[InsertRepositoryContent]] if the request was processed successfully. + */ case class InsertTriplestoreContentACK() /** - * Inserts raw RDF data into the repository. - * - * @param graphContent contains graph data as turtle. - * @param graphName the name of the graph. - */ + * Inserts raw RDF data into the repository. + * + * @param graphContent contains graph data as turtle. + * @param graphName the name of the graph. + */ case class InsertGraphDataContentRequest(graphContent: String, graphName: String) extends TriplestoreRequest /** - * Sent as a response to [[InsertGraphDataContentRequest]] if the request was processed successfully. - */ + * Sent as a response to [[InsertGraphDataContentRequest]] if the request was processed successfully. + */ case class InsertGraphDataContentResponse() /** - * Initialize the repository. This will initiate the (re)creation of the repository and adding data to it. - * - * @param rdfDataObject contains a list of [[RdfDataObject]]. - */ + * Initialize the repository. This will initiate the (re)creation of the repository and adding data to it. + * + * @param rdfDataObject contains a list of [[RdfDataObject]]. + */ case class InitRepository(rdfDataObject: RdfDataObject) extends TriplestoreRequest /** - * Initialization ((re)creation of repository and loading of data) is finished successfully. - */ + * Initialization ((re)creation of repository and loading of data) is finished successfully. + */ case class InitRepositoryACK() /** - * Ask triplestore if it is ready - */ + * Ask triplestore if it is ready + */ case class CheckTriplestoreRequest() extends TriplestoreRequest /** - * Response indicating whether the triplestore has finished initialization and is ready for processing messages - * - * @param triplestoreStatus the state of the triplestore. - * @param msg further description. - */ + * Response indicating whether the triplestore has finished initialization and is ready for processing messages + * + * @param triplestoreStatus the state of the triplestore. + * @param msg further description. + */ case class CheckTriplestoreResponse(triplestoreStatus: TriplestoreStatus, msg: String) /** - * Simulates a triplestore timeout. Used only in testing. - */ + * Simulates a triplestore timeout. Used only in testing. + */ case class SimulateTimeoutRequest() extends TriplestoreRequest /** - * Requests that the repository is updated to be compatible with the running version of Knora. - */ + * Requests that the repository is updated to be compatible with the running version of Knora. + */ case class UpdateRepositoryRequest() extends TriplestoreRequest /** - * Requests that the repository is downloaded to an N-Quads file. A successful response will be a [[FileWrittenResponse]]. - * - * @param outputFile the output file. - * @param featureFactoryConfig the feature factory configuration. - */ + * Requests that the repository is downloaded to an N-Quads file. A successful response will be a [[FileWrittenResponse]]. + * + * @param outputFile the output file. + * @param featureFactoryConfig the feature factory configuration. + */ case class DownloadRepositoryRequest(outputFile: Path, featureFactoryConfig: FeatureFactoryConfig) extends TriplestoreRequest /** - * Indicates that a file was written successfully. - */ + * Indicates that a file was written successfully. + */ case class FileWrittenResponse() /** - * Requests that repository content is uploaded from an N-Quads. A successful response will be a - * [[RepositoryUploadedResponse]]. - * - * @param inputFile a TriG file containing the content to be uploaded to the repository. - */ + * Requests that repository content is uploaded from an N-Quads. A successful response will be a + * [[RepositoryUploadedResponse]]. + * + * @param inputFile a TriG file containing the content to be uploaded to the repository. + */ case class UploadRepositoryRequest(inputFile: Path) extends TriplestoreRequest /** - * Indicates that repository content was successfully uploaded. - */ + * Indicates that repository content was successfully uploaded. + */ case class RepositoryUploadedResponse() /** - * Indicates whether the repository is up to date. - * - * @param message a message providing details of what was done. - */ + * Indicates whether the repository is up to date. + * + * @param message a message providing details of what was done. + */ case class RepositoryUpdatedResponse(message: String) extends TriplestoreRequest /** - * Updates the triplestore's full-text search index. - * - * @param subjectIri if a subject has changed, update the index for that subject. Otherwise, updates - * the index to add any subjects not yet indexed. - */ + * Updates the triplestore's full-text search index. + * + * @param subjectIri if a subject has changed, update the index for that subject. Otherwise, updates + * the index to add any subjects not yet indexed. + */ case class SearchIndexUpdateRequest(subjectIri: Option[String] = None) extends TriplestoreRequest ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Components of messages /** - * Triplestore status - * - ServiceUnavailable: Triplestore is not responding to HTTP requests. - * - NotInitialized: Triplestore is responding to HTTP requests but the repository defined in 'application.conf' is missing. - * - ServiceAvailable: Everything is OK. - */ + * Triplestore status + * - ServiceUnavailable: Triplestore is not responding to HTTP requests. + * - NotInitialized: Triplestore is responding to HTTP requests but the repository defined in 'application.conf' is missing. + * - ServiceAvailable: Everything is OK. + */ object TriplestoreStatus extends Enumeration { type TriplestoreStatus = Value val ServiceUnavailable, NotInitialized, ServiceAvailable = Value } /** - * Contains the path to the 'ttl' file and the name of the named graph it should be loaded in. - * - * @param path to the 'ttl' file - * @param name of the named graph the data will be load into. - */ + * Contains the path to the 'ttl' file and the name of the named graph it should be loaded in. + * + * @param path to the 'ttl' file + * @param name of the named graph the data will be load into. + */ case class RdfDataObject(path: String, name: String) /** - * Represents the subject of a statement read from the triplestore. - */ + * Represents the subject of a statement read from the triplestore. + */ sealed trait SubjectV2 /** - * Represents an IRI used as the subject of a statement. - */ + * Represents an IRI used as the subject of a statement. + */ case class IriSubjectV2(value: IRI) extends SubjectV2 { override def toString: IRI = value } /** - * Represents a blank node identifier used as the subject of a statement. - */ + * Represents a blank node identifier used as the subject of a statement. + */ case class BlankNodeSubjectV2(value: String) extends SubjectV2 { override def toString: String = value } /** - * Represents a literal read from the triplestore. There are different subclasses - * representing literals with the extended type information stored in the triplestore. - */ + * Represents a literal read from the triplestore. There are different subclasses + * representing literals with the extended type information stored in the triplestore. + */ sealed trait LiteralV2 { /** - * Returns this [[LiteralV2]] as an [[IriLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * an [[IriLiteralV2]]. - * @return an [[IriLiteralV2]]. - */ - def asIriLiteral(errorFun: => Nothing): IriLiteralV2 = { + * Returns this [[LiteralV2]] as an [[IriLiteralV2]]. + * + * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not + * an [[IriLiteralV2]]. + * @return an [[IriLiteralV2]]. + */ + def asIriLiteral(errorFun: => Nothing): IriLiteralV2 = this match { case iriLiteral: IriLiteralV2 => iriLiteral case _ => errorFun } - } /** - * Returns this [[LiteralV2]] as a [[StringLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[StringLiteralV2]]. - * @return a [[StringLiteralV2]]. - */ - def asStringLiteral(errorFun: => Nothing): StringLiteralV2 = { + * Returns this [[LiteralV2]] as a [[StringLiteralV2]]. + * + * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not + * a [[StringLiteralV2]]. + * @return a [[StringLiteralV2]]. + */ + def asStringLiteral(errorFun: => Nothing): StringLiteralV2 = this match { case stringLiteral: StringLiteralV2 => stringLiteral case _ => errorFun } - } /** - * Returns this [[LiteralV2]] as a [[BooleanLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[BooleanLiteralV2]]. - * @return a [[BooleanLiteralV2]]. - */ - def asBooleanLiteral(errorFun: => Nothing): BooleanLiteralV2 = { + * Returns this [[LiteralV2]] as a [[BooleanLiteralV2]]. + * + * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not + * a [[BooleanLiteralV2]]. + * @return a [[BooleanLiteralV2]]. + */ + def asBooleanLiteral(errorFun: => Nothing): BooleanLiteralV2 = this match { case booleanLiteral: BooleanLiteralV2 => booleanLiteral case _ => errorFun } - } /** - * Returns this [[LiteralV2]] as an [[IntLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * an [[IntLiteralV2]]. - * @return an [[IntLiteralV2]]. - */ - def asIntLiteral(errorFun: => Nothing): IntLiteralV2 = { + * Returns this [[LiteralV2]] as an [[IntLiteralV2]]. + * + * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not + * an [[IntLiteralV2]]. + * @return an [[IntLiteralV2]]. + */ + def asIntLiteral(errorFun: => Nothing): IntLiteralV2 = this match { case intLiteral: IntLiteralV2 => intLiteral case _ => errorFun } - } /** - * Returns this [[LiteralV2]] as a [[DecimalLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[DecimalLiteralV2]]. - * @return a [[DecimalLiteralV2]]. - */ - def asDecimalLiteral(errorFun: => Nothing): DecimalLiteralV2 = { + * Returns this [[LiteralV2]] as a [[DecimalLiteralV2]]. + * + * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not + * a [[DecimalLiteralV2]]. + * @return a [[DecimalLiteralV2]]. + */ + def asDecimalLiteral(errorFun: => Nothing): DecimalLiteralV2 = this match { case decimalLiteral: DecimalLiteralV2 => decimalLiteral case _ => errorFun } - } /** - * Returns this [[LiteralV2]] as a [[DateTimeLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[DateTimeLiteralV2]]. - * @return a [[DateTimeLiteralV2]]. - */ - def asDateTimeLiteral(errorFun: => Nothing): DateTimeLiteralV2 = { + * Returns this [[LiteralV2]] as a [[DateTimeLiteralV2]]. + * + * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not + * a [[DateTimeLiteralV2]]. + * @return a [[DateTimeLiteralV2]]. + */ + def asDateTimeLiteral(errorFun: => Nothing): DateTimeLiteralV2 = this match { case dateTimeLiteral: DateTimeLiteralV2 => dateTimeLiteral case _ => errorFun } - } } /** - * Represents a literal read from an ontology in the triplestore. - */ + * Represents a literal read from an ontology in the triplestore. + */ sealed trait OntologyLiteralV2 /** - * Represents an IRI literal. - * - * @param value the IRI. - */ + * Represents an IRI literal. + * + * @param value the IRI. + */ case class IriLiteralV2(value: IRI) extends LiteralV2 { override def toString: IRI = value } /** - * Represents an IRI literal as a [[SmartIri]]. - * - * @param value the IRI. - */ + * Represents an IRI literal as a [[SmartIri]]. + * + * @param value the IRI. + */ case class SmartIriLiteralV2(value: SmartIri) extends OntologyLiteralV2 { override def toString: IRI = value.toString @@ -566,20 +568,20 @@ case class SmartIriLiteralV2(value: SmartIri) extends OntologyLiteralV2 { } /** - * Represents a blank node identifier. - * - * @param value the identifier of the blank node. - */ + * Represents a blank node identifier. + * + * @param value the identifier of the blank node. + */ case class BlankNodeLiteralV2(value: String) extends LiteralV2 { override def toString: String = value } /** - * Represents a string with an optional language tag. Allows sorting inside collections by value. - * - * @param value the string value. - * @param language the optional language tag. - */ + * Represents a string with an optional language tag. Allows sorting inside collections by value. + * + * @param value the string value. + * @param language the optional language tag. + */ case class StringLiteralV2(value: String, language: Option[String] = None) extends LiteralV2 with OntologyLiteralV2 @@ -594,32 +596,31 @@ case class StringLiteralV2(value: String, language: Option[String] = None) } /** - * Represents a sequence of [[StringLiteralV2]]. - * - * @param stringLiterals a sequence of [[StringLiteralV2]]. - */ + * Represents a sequence of [[StringLiteralV2]]. + * + * @param stringLiterals a sequence of [[StringLiteralV2]]. + */ case class StringLiteralSequenceV2(stringLiterals: Vector[StringLiteralV2]) { /** - * Sort sequence of [[StringLiteralV2]] by their text value. - * - * @return a [[StringLiteralSequenceV2]] sorted by string value. - */ - def sortByStringValue: StringLiteralSequenceV2 = { + * Sort sequence of [[StringLiteralV2]] by their text value. + * + * @return a [[StringLiteralSequenceV2]] sorted by string value. + */ + def sortByStringValue: StringLiteralSequenceV2 = StringLiteralSequenceV2(stringLiterals.sortBy(_.value)) - } /** - * Gets the string value of the [[StringLiteralV2]] corresponding to the preferred language. - * If not available, returns the string value of the fallback language or any available language. - * - * @param preferredLang the preferred language. - * @param fallbackLang language to use if preferred language is not available. - */ + * Gets the string value of the [[StringLiteralV2]] corresponding to the preferred language. + * If not available, returns the string value of the fallback language or any available language. + * + * @param preferredLang the preferred language. + * @param fallbackLang language to use if preferred language is not available. + */ def getPreferredLanguage(preferredLang: String, fallbackLang: String): Option[String] = { - val stringLiteralMap: Map[Option[String], String] = stringLiterals.map { - case StringLiteralV2(str, lang) => lang -> str + val stringLiteralMap: Map[Option[String], String] = stringLiterals.map { case StringLiteralV2(str, lang) => + lang -> str }.toMap stringLiteralMap.get(Some(preferredLang)) match { @@ -646,14 +647,11 @@ case class StringLiteralSequenceV2(stringLiterals: Vector[StringLiteralV2]) { // available `StringLiteralV2` by language code to get a deterministic result, // and return the object in the language with the lowest sort // order. - stringLiteralMap.toVector - .sortBy { - case (lang, _) => lang - } - .headOption - .map { - case (_, obj) => obj - } + stringLiteralMap.toVector.sortBy { case (lang, _) => + lang + }.headOption.map { case (_, obj) => + obj + } } } @@ -662,37 +660,37 @@ case class StringLiteralSequenceV2(stringLiterals: Vector[StringLiteralV2]) { } /** - * Represents a boolean value. - * - * @param value the boolean value. - */ + * Represents a boolean value. + * + * @param value the boolean value. + */ case class BooleanLiteralV2(value: Boolean) extends LiteralV2 with OntologyLiteralV2 { override def toString: String = value.toString } /** - * Represents an integer value. - * - * @param value the integer value. - */ + * Represents an integer value. + * + * @param value the integer value. + */ case class IntLiteralV2(value: Int) extends LiteralV2 { override def toString: String = value.toString } /** - * Represents a decimal value. - * - * @param value the decimal value. - */ + * Represents a decimal value. + * + * @param value the decimal value. + */ case class DecimalLiteralV2(value: BigDecimal) extends LiteralV2 { override def toString: String = value.toString } /** - * Represents a timestamp. - * - * @param value the timestamp value. - */ + * Represents a timestamp. + * + * @param value the timestamp value. + */ case class DateTimeLiteralV2(value: Instant) extends LiteralV2 { override def toString: String = value.toString } @@ -701,14 +699,14 @@ case class DateTimeLiteralV2(value: Instant) extends LiteralV2 { // JSON formatting /** - * A spray-json protocol that parses JSON returned by a SPARQL endpoint. Empty values and empty rows are - * ignored. - */ + * A spray-json protocol that parses JSON returned by a SPARQL endpoint. Empty values and empty rows are + * ignored. + */ object SparqlResultProtocol extends DefaultJsonProtocol { /** - * Converts a [[JsValue]] to a [[VariableResultsRow]]. - */ + * Converts a [[JsValue]] to a [[VariableResultsRow]]. + */ implicit object VariableResultsJsonFormat extends JsonFormat[VariableResultsRow] { def read(jsonVal: JsValue): VariableResultsRow = { @@ -724,19 +722,24 @@ object SparqlResultProtocol extends DefaultJsonProtocol { // Wrap that Map in an ErrorHandlingMap that will gracefully report errors about missing values when they // are accessed later. - VariableResultsRow(new ErrorHandlingMap(mapToWrap, { key: String => - s"No value found for SPARQL query variable '$key' in query result row" - })) + VariableResultsRow( + new ErrorHandlingMap( + mapToWrap, + { key: String => + s"No value found for SPARQL query variable '$key' in query result row" + } + ) + ) } def write(variableResultsRow: VariableResultsRow): JsValue = ??? } /** - * Converts a [[JsValue]] to a [[SparqlSelectResultBody]]. - */ + * Converts a [[JsValue]] to a [[SparqlSelectResultBody]]. + */ implicit object SparqlSelectResponseBodyFormat extends JsonFormat[SparqlSelectResultBody] { - def read(jsonVal: JsValue): SparqlSelectResultBody = { + def read(jsonVal: JsValue): SparqlSelectResultBody = jsonVal.asJsObject.fields.get("bindings") match { case Some(bindingsJson: JsArray) => // Filter out empty rows. @@ -744,7 +747,6 @@ object SparqlResultProtocol extends DefaultJsonProtocol { case _ => SparqlSelectResultBody(Nil) } - } def write(sparqlSelectResponseBody: SparqlSelectResultBody): JsValue = ??? } @@ -754,20 +756,19 @@ object SparqlResultProtocol extends DefaultJsonProtocol { } /** - * A spray-json protocol for generating Knora API v1 JSON providing data about resources and their properties. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about resources and their properties. + */ trait TriplestoreJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { implicit object LiteralV2Format extends JsonFormat[StringLiteralV2] { /** - * Converts a [[StringLiteralV2]] to a [[JsValue]]. - * - * @param string a [[StringLiteralV2]]. - * @return a [[JsValue]]. - */ - def write(string: StringLiteralV2): JsValue = { - + * Converts a [[StringLiteralV2]] to a [[JsValue]]. + * + * @param string a [[StringLiteralV2]]. + * @return a [[JsValue]]. + */ + def write(string: StringLiteralV2): JsValue = if (string.language.isDefined) { // have language tag JsObject( @@ -784,14 +785,13 @@ trait TriplestoreJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol ) ) } - } /** - * Converts a [[JsValue]] to a [[StringLiteralV2]]. - * - * @param json a [[JsValue]]. - * @return a [[StringLiteralV2]]. - */ + * Converts a [[JsValue]] to a [[StringLiteralV2]]. + * + * @param json a [[JsValue]]. + * @return a [[StringLiteralV2]]. + */ def read(json: JsValue): StringLiteralV2 = json match { case stringWithLang: JsObject => stringWithLang.getFields("value", "language") match { @@ -815,6 +815,7 @@ trait TriplestoreJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol implicit val rdfDataObjectFormat: RootJsonFormat[RdfDataObject] = jsonFormat2(RdfDataObject) implicit val resetTriplestoreContentFormat: RootJsonFormat[ResetRepositoryContent] = jsonFormat2( - ResetRepositoryContent) + ResetRepositoryContent + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/traits/Jsonable.scala b/webapi/src/main/scala/org/knora/webapi/messages/traits/Jsonable.scala index c0d933444a..67e2c0cec6 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/traits/Jsonable.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/traits/Jsonable.scala @@ -22,14 +22,14 @@ package org.knora.webapi.messages.traits import spray.json.JsValue /** - * A trait for classes that can convert themselves into JSON using the spray-json library. - */ + * A trait for classes that can convert themselves into JSON using the spray-json library. + */ trait Jsonable { /** - * Converts this [[Jsonable]] into a [[JsValue]]. - * - * @return a [[JsValue]]. - */ + * Converts this [[Jsonable]] into a [[JsValue]]. + * + * @return a [[JsValue]]. + */ def toJsValue: JsValue } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/traits/RequestWithSender.scala b/webapi/src/main/scala/org/knora/webapi/messages/traits/RequestWithSender.scala index f5b2e49014..221398e7ba 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/traits/RequestWithSender.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/traits/RequestWithSender.scala @@ -20,8 +20,8 @@ package org.knora.webapi.messages.traits /** - * A request message that knows the name of the sender. - */ + * A request message that knows the name of the sender. + */ trait RequestWithSender { val senderName: String } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/twirl/Contributor.scala b/webapi/src/main/scala/org/knora/webapi/messages/twirl/Contributor.scala index 48e56989d9..1a18a61838 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/twirl/Contributor.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/twirl/Contributor.scala @@ -20,12 +20,12 @@ package org.knora.webapi.messages.twirl /** - * Represents a contributor to Knora. Used by `src/main/twirl/queries/util/generateContributorsMarkdown.scala.txt`. - * - * @param login the contributor's GitHub username. - * @param apiUrl the contributor's GitHub API URL. - * @param htmlUrl the contributor's GitHub HTML URL. - * @param contributions the number of contributions the contributor has maed. - * @param name the contributor's name. - */ + * Represents a contributor to Knora. Used by `src/main/twirl/queries/util/generateContributorsMarkdown.scala.txt`. + * + * @param login the contributor's GitHub username. + * @param apiUrl the contributor's GitHub API URL. + * @param htmlUrl the contributor's GitHub HTML URL. + * @param contributions the number of contributions the contributor has maed. + * @param name the contributor's name. + */ case class Contributor(login: String, apiUrl: String, htmlUrl: String, contributions: Int, name: Option[String] = None) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/twirl/Mapping.scala b/webapi/src/main/scala/org/knora/webapi/messages/twirl/Mapping.scala index 6f132770dd..7b20363ecb 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/twirl/Mapping.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/twirl/Mapping.scala @@ -22,45 +22,51 @@ package org.knora.webapi.messages.twirl import org.knora.webapi.IRI /** - * Represents a standoff datatype class of an XML tag. - * - * @param datatype the IRI of the standoff datatype class. - * @param attributeName the XML attribute that holds the typed value. - * @param mappingStandoffDataTypeClassElementIri the IRI of the standoff datatype element (to be used to create the element in the triplestore). - */ -case class MappingStandoffDatatypeClass(datatype: IRI, - attributeName: String, - mappingStandoffDataTypeClassElementIri: IRI) + * Represents a standoff datatype class of an XML tag. + * + * @param datatype the IRI of the standoff datatype class. + * @param attributeName the XML attribute that holds the typed value. + * @param mappingStandoffDataTypeClassElementIri the IRI of the standoff datatype element (to be used to create the element in the triplestore). + */ +case class MappingStandoffDatatypeClass( + datatype: IRI, + attributeName: String, + mappingStandoffDataTypeClassElementIri: IRI +) /** - * Represents an attribute of an XML tag. - * - * @param attributeName the name of the XML attribute. - * @param namespace the namespace of the XML attribute. - * @param standoffProperty the IRI of standoff property the XML attribute is mapped to. - * @param mappingXMLAttributeElementIri the IRI of the attribute element (to be used to create the element in the triplestore). - */ -case class MappingXMLAttribute(attributeName: String, - namespace: String, - standoffProperty: IRI, - mappingXMLAttributeElementIri: IRI) + * Represents an attribute of an XML tag. + * + * @param attributeName the name of the XML attribute. + * @param namespace the namespace of the XML attribute. + * @param standoffProperty the IRI of standoff property the XML attribute is mapped to. + * @param mappingXMLAttributeElementIri the IRI of the attribute element (to be used to create the element in the triplestore). + */ +case class MappingXMLAttribute( + attributeName: String, + namespace: String, + standoffProperty: IRI, + mappingXMLAttributeElementIri: IRI +) /** - * Represents an element of an XML to standoff mapping. - * - * @param tagName the name of the XML tag. - * @param namespace the namespace of the XML tag. - * @param className the classname of the XML tag. - * @param standoffClass the IRI of the standoff class the XML tag is mapped to. - * @param attributes the attributes of the XML tag. - * @param standoffDataTypeClass the standoff data type class of the xml tag. - * @param mappingElementIri the IRI of the mapping element (to be used to create the element in the triplestore). - */ -case class MappingElement(tagName: String, - namespace: String, - className: String, - standoffClass: IRI, - attributes: Seq[MappingXMLAttribute] = Seq.empty[MappingXMLAttribute], - standoffDataTypeClass: Option[MappingStandoffDatatypeClass] = None, - mappingElementIri: IRI, - separatorRequired: Boolean) + * Represents an element of an XML to standoff mapping. + * + * @param tagName the name of the XML tag. + * @param namespace the namespace of the XML tag. + * @param className the classname of the XML tag. + * @param standoffClass the IRI of the standoff class the XML tag is mapped to. + * @param attributes the attributes of the XML tag. + * @param standoffDataTypeClass the standoff data type class of the xml tag. + * @param mappingElementIri the IRI of the mapping element (to be used to create the element in the triplestore). + */ +case class MappingElement( + tagName: String, + namespace: String, + className: String, + standoffClass: IRI, + attributes: Seq[MappingXMLAttribute] = Seq.empty[MappingXMLAttribute], + standoffDataTypeClass: Option[MappingStandoffDatatypeClass] = None, + mappingElementIri: IRI, + separatorRequired: Boolean +) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/twirl/ResourceHtmlView.scala b/webapi/src/main/scala/org/knora/webapi/messages/twirl/ResourceHtmlView.scala index c5eb4d3c56..4f0b82fa5d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/twirl/ResourceHtmlView.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/twirl/ResourceHtmlView.scala @@ -34,53 +34,53 @@ import scala.concurrent.Await import scala.concurrent.duration.{Duration, SECONDS} /** - * Provides an HTML view of a resource. - */ + * Provides an HTML view of a resource. + */ object ResourceHtmlView { private implicit val timeout: Timeout = Duration(5, SECONDS) val log = Logger(LoggerFactory.getLogger("org.knora.webapi.viewhandlers.ResourceHtmlView")) /** - * A user representing the Knora API server, used in those cases where a user is required. - */ + * A user representing the Knora API server, used in those cases where a user is required. + */ private val systemUser = KnoraSystemInstances.Users.SystemUser.asUserProfileV1 def propertiesHtmlView(response: ResourceFullResponseV1, responderManager: ActorRef): String = { val properties = response.props.get.properties - val propMap = properties.foldLeft(Map.empty[String, String]) { - case (acc, propertyV1) => - log.debug(s"${propertyV1.toString}") + val propMap = properties.foldLeft(Map.empty[String, String]) { case (acc, propertyV1) => + log.debug(s"${propertyV1.toString}") - val label = propertyV1.label match { - case Some(value) => value - case None => propertyV1.pid - } + val label = propertyV1.label match { + case Some(value) => value + case None => propertyV1.pid + } - val values: Seq[String] = propertyV1.valuetype_id.get match { - case OntologyConstants.KnoraBase.TextValue => - propertyV1.values.map(literal => textValue2String(literal.asInstanceOf[TextValueV1])) + val values: Seq[String] = propertyV1.valuetype_id.get match { + case OntologyConstants.KnoraBase.TextValue => + propertyV1.values.map(literal => textValue2String(literal.asInstanceOf[TextValueV1])) - case OntologyConstants.KnoraBase.DateValue => - propertyV1.values.map(literal => dateValue2String(literal.asInstanceOf[DateValueV1])) + case OntologyConstants.KnoraBase.DateValue => + propertyV1.values.map(literal => dateValue2String(literal.asInstanceOf[DateValueV1])) - case OntologyConstants.KnoraBase.ListValue => - propertyV1.values.map(literal => - listValue2String(literal.asInstanceOf[HierarchicalListValueV1], responderManager)) + case OntologyConstants.KnoraBase.ListValue => + propertyV1.values.map(literal => + listValue2String(literal.asInstanceOf[HierarchicalListValueV1], responderManager) + ) - case OntologyConstants.KnoraBase.Resource => // TODO: This could actually be a subclass of knora-base:Resource. - propertyV1.values.map(literal => resourceValue2String(literal.asInstanceOf[LinkV1])) + case OntologyConstants.KnoraBase.Resource => // TODO: This could actually be a subclass of knora-base:Resource. + propertyV1.values.map(literal => resourceValue2String(literal.asInstanceOf[LinkV1])) - case _ => Vector() - } + case _ => Vector() + } - if (values.nonEmpty) { - acc + (label -> values.mkString(",")) - } else { - acc - } + if (values.nonEmpty) { + acc + (label -> values.mkString(",")) + } else { + acc + } } val imgpath = properties.find(_.locations.nonEmpty).map(_.locations.head.path).getOrElse("") @@ -91,18 +91,15 @@ object ResourceHtmlView { content.toString } - private def textValue2String(text: TextValueV1): String = { + private def textValue2String(text: TextValueV1): String = text.utf8str - } - - private def dateValue2String(date: DateValueV1): String = { + private def dateValue2String(date: DateValueV1): String = if (date.dateval1 == date.dateval2) { date.dateval1.toString + " " + date.era1 + ", " + date.calendar.toString + " " + date.era2 } else { date.dateval1.toString + " " + date.era1 + ", " + date.dateval2 + ", " + date.calendar.toString + " " + date.era2 } - } private def listValue2String(list: HierarchicalListValueV1, responderManager: ActorRef): String = { @@ -114,8 +111,7 @@ object ResourceHtmlView { } } - private def resourceValue2String(resource: LinkV1): String = { + private def resourceValue2String(resource: LinkV1): String = resource.valueLabel.get - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/twirl/SearchCriterion.scala b/webapi/src/main/scala/org/knora/webapi/messages/twirl/SearchCriterion.scala index 827779788a..ac2c700734 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/twirl/SearchCriterion.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/twirl/SearchCriterion.scala @@ -23,22 +23,24 @@ import org.knora.webapi._ import org.knora.webapi.messages.v1.responder.searchmessages.SearchComparisonOperatorV1 /** - * The extended search template's representation of an extended search criterion. - * - * @param propertyIri the IRI of the property to be searched. - * @param comparisonOperator the comparison operator. - * @param valueType the type of value to search for. - * @param searchValue the value to compare with, if we are comparing strings or numbers. - * @param dateStart the start of the date range to compare with, if we are comparing dates. - * @param dateEnd the end of the date range to compare with, if we are comparing dates. - * @param matchBooleanPositiveTerms the terms to include if we are using MATCH BOOLEAN. - * @param matchBooleanNegativeTerms the terms to exclude if we are using MATCH BOOLEAN. - */ -case class SearchCriterion(propertyIri: IRI, - comparisonOperator: SearchComparisonOperatorV1.Value, - valueType: IRI, - searchValue: Option[String] = None, - dateStart: Option[Int] = None, - dateEnd: Option[Int] = None, - matchBooleanPositiveTerms: Set[String] = Set.empty[String], - matchBooleanNegativeTerms: Set[String] = Set.empty[String]) + * The extended search template's representation of an extended search criterion. + * + * @param propertyIri the IRI of the property to be searched. + * @param comparisonOperator the comparison operator. + * @param valueType the type of value to search for. + * @param searchValue the value to compare with, if we are comparing strings or numbers. + * @param dateStart the start of the date range to compare with, if we are comparing dates. + * @param dateEnd the end of the date range to compare with, if we are comparing dates. + * @param matchBooleanPositiveTerms the terms to include if we are using MATCH BOOLEAN. + * @param matchBooleanNegativeTerms the terms to exclude if we are using MATCH BOOLEAN. + */ +case class SearchCriterion( + propertyIri: IRI, + comparisonOperator: SearchComparisonOperatorV1.Value, + valueType: IRI, + searchValue: Option[String] = None, + dateStart: Option[Int] = None, + dateEnd: Option[Int] = None, + matchBooleanPositiveTerms: Set[String] = Set.empty[String], + matchBooleanNegativeTerms: Set[String] = Set.empty[String] +) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateLinkUpdate.scala b/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateLinkUpdate.scala index 4f3db52b08..f77dffb751 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateLinkUpdate.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateLinkUpdate.scala @@ -23,38 +23,40 @@ import org.knora.webapi.IRI import org.knora.webapi.messages.SmartIri /** - * Contains instructions that can be given to a SPARQL template for updating direct links and `knora-base:LinkValue` - * objects representing references to resources. - * - * @param linkPropertyIri the IRI of the direct link property. - * @param directLinkExists `true` if a direct link already exists between the source and target resources. - * @param insertDirectLink `true` if the direct link should be inserted. - * @param deleteDirectLink `true` if the direct link should be deleted (because the reference count is being decremented - * to 0). - * @param linkValueExists `true` if a `LinkValue` already exists describing a direct link between the source - * and target resources. - * @param linkTargetExists `true` if the link target already exists, `false` if is being created in the same - * SPARQL update operation. - * @param newLinkValueIri the IRI of the new `LinkValue` to be created. - * @param linkTargetIri the IRI of the target resource. - * @param currentReferenceCount the current reference count of the existing `LinkValue`, if any. This will be - * 0 if (a) there was previously a direct link but it was deleted (`directLinkExists` is - * `false` and `linkValueExists` is `true`), or (b) there was never a direct link, and - * there is no `LinkValue` (`directLinkExists` and `linkValueExists` will then be `false`). - * @param newReferenceCount the new reference count of the `LinkValue`. - * @param newLinkValueCreator the creator of the new `LinkValue`. - * @param newLinkValuePermissions the literal that should be the object of the `hasPermissions` property of - * the new `LinkValue`. - */ -case class SparqlTemplateLinkUpdate(linkPropertyIri: SmartIri, - directLinkExists: Boolean, - insertDirectLink: Boolean, - deleteDirectLink: Boolean, - linkValueExists: Boolean, - linkTargetExists: Boolean, - newLinkValueIri: IRI, - linkTargetIri: IRI, - currentReferenceCount: Int, - newReferenceCount: Int, - newLinkValueCreator: IRI, - newLinkValuePermissions: String) + * Contains instructions that can be given to a SPARQL template for updating direct links and `knora-base:LinkValue` + * objects representing references to resources. + * + * @param linkPropertyIri the IRI of the direct link property. + * @param directLinkExists `true` if a direct link already exists between the source and target resources. + * @param insertDirectLink `true` if the direct link should be inserted. + * @param deleteDirectLink `true` if the direct link should be deleted (because the reference count is being decremented + * to 0). + * @param linkValueExists `true` if a `LinkValue` already exists describing a direct link between the source + * and target resources. + * @param linkTargetExists `true` if the link target already exists, `false` if is being created in the same + * SPARQL update operation. + * @param newLinkValueIri the IRI of the new `LinkValue` to be created. + * @param linkTargetIri the IRI of the target resource. + * @param currentReferenceCount the current reference count of the existing `LinkValue`, if any. This will be + * 0 if (a) there was previously a direct link but it was deleted (`directLinkExists` is + * `false` and `linkValueExists` is `true`), or (b) there was never a direct link, and + * there is no `LinkValue` (`directLinkExists` and `linkValueExists` will then be `false`). + * @param newReferenceCount the new reference count of the `LinkValue`. + * @param newLinkValueCreator the creator of the new `LinkValue`. + * @param newLinkValuePermissions the literal that should be the object of the `hasPermissions` property of + * the new `LinkValue`. + */ +case class SparqlTemplateLinkUpdate( + linkPropertyIri: SmartIri, + directLinkExists: Boolean, + insertDirectLink: Boolean, + deleteDirectLink: Boolean, + linkValueExists: Boolean, + linkTargetExists: Boolean, + newLinkValueIri: IRI, + linkTargetIri: IRI, + currentReferenceCount: Int, + newReferenceCount: Int, + newLinkValueCreator: IRI, + newLinkValuePermissions: String +) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateResourceToCreate.scala b/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateResourceToCreate.scala index cc00d31d3e..3b54903d92 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateResourceToCreate.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/twirl/SparqlTemplateResourceToCreate.scala @@ -24,18 +24,20 @@ import java.time.Instant import org.knora.webapi._ /** - * Represents a resource to be created with its index, label, IRI, permissions, and SPARQL for creating its values - * - * @param resourceIri the IRI of the resource to be created. - * @param permissions the permissions user has for creating the new resource. - * @param sparqlForValues the SPARQL for creating the values of the resource. - * @param resourceClassIri the type of the resource to be created. - * @param resourceLabel the label of the resource. - * @param resourceCreationDate the creation date that should be attached to the resource. - */ -case class SparqlTemplateResourceToCreate(resourceIri: IRI, - permissions: String, - sparqlForValues: String, - resourceClassIri: IRI, - resourceLabel: String, - resourceCreationDate: Instant) + * Represents a resource to be created with its index, label, IRI, permissions, and SPARQL for creating its values + * + * @param resourceIri the IRI of the resource to be created. + * @param permissions the permissions user has for creating the new resource. + * @param sparqlForValues the SPARQL for creating the values of the resource. + * @param resourceClassIri the type of the resource to be created. + * @param resourceLabel the label of the resource. + * @param resourceCreationDate the creation date that should be attached to the resource. + */ +case class SparqlTemplateResourceToCreate( + resourceIri: IRI, + permissions: String, + sparqlForValues: String, + resourceClassIri: IRI, + resourceLabel: String, + resourceCreationDate: Instant +) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/CalendarDateUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/CalendarDateUtilV2.scala index 54afcb37cd..12ef2bb1f0 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/CalendarDateUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/CalendarDateUtilV2.scala @@ -26,158 +26,157 @@ import org.knora.webapi.exceptions.{AssertionException, BadRequestException} import org.knora.webapi.messages.StringFormatter /** - * Indicates the era (CE or BCE) in Gregorian and Julian calendar dates. - */ + * Indicates the era (CE or BCE) in Gregorian and Julian calendar dates. + */ sealed trait DateEraV2 object DateEraV2 { /** - * Parses a calendar era. - * - * @param eraStr a string representing the era. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return a [[DateEraV2]] representing the era. - */ - def parse(eraStr: String, errorFun: => Nothing): DateEraV2 = { + * Parses a calendar era. + * + * @param eraStr a string representing the era. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return a [[DateEraV2]] representing the era. + */ + def parse(eraStr: String, errorFun: => Nothing): DateEraV2 = eraStr match { case StringFormatter.Era_AD | StringFormatter.Era_CE => DateEraCE case StringFormatter.Era_BC | StringFormatter.Era_BCE => DateEraBCE case _ => errorFun } - } } /** - * Represents the era BCE in a Gregorian or Julian calendar date. - */ + * Represents the era BCE in a Gregorian or Julian calendar date. + */ case object DateEraBCE extends DateEraV2 { override def toString: String = StringFormatter.Era_BCE } /** - * Represents the era CE in a Gregorian or Julian calendar date. - */ + * Represents the era CE in a Gregorian or Julian calendar date. + */ case object DateEraCE extends DateEraV2 { override def toString: String = StringFormatter.Era_CE } /** - * Represents the precision of a date. - */ + * Represents the precision of a date. + */ sealed trait DatePrecisionV2 object DatePrecisionV2 { /** - * Parses the name of a date precision. - * - * @param precisionStr the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return a [[DatePrecisionV2]] representing the date precision. - */ - def parse(precisionStr: String, errorFun: => Nothing): DatePrecisionV2 = { + * Parses the name of a date precision. + * + * @param precisionStr the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return a [[DatePrecisionV2]] representing the date precision. + */ + def parse(precisionStr: String, errorFun: => Nothing): DatePrecisionV2 = precisionStr match { case StringFormatter.PrecisionDay => DatePrecisionDay case StringFormatter.PrecisionMonth => DatePrecisionMonth case StringFormatter.PrecisionYear => DatePrecisionYear case _ => errorFun } - } } /** - * Indicates that a date has year precision. - */ + * Indicates that a date has year precision. + */ case object DatePrecisionYear extends DatePrecisionV2 { override def toString: String = StringFormatter.PrecisionYear } /** - * Indicates that a date has month precision. - */ + * Indicates that a date has month precision. + */ case object DatePrecisionMonth extends DatePrecisionV2 { override def toString: String = StringFormatter.PrecisionMonth } /** - * Indicates that a date has day precision. - */ + * Indicates that a date has day precision. + */ case object DatePrecisionDay extends DatePrecisionV2 { override def toString: String = StringFormatter.PrecisionDay } /** - * Represents the name of a calendar. - */ + * Represents the name of a calendar. + */ sealed trait CalendarNameV2 object CalendarNameV2 { /** - * Parses the name of a calendar. - * - * @param calendarNameStr a string representing the name of the calendar. - * @param errorFun a function that throws an exception. It will be called if the string cannot - * be parsed. - * @return a [[CalendarNameV2]] representing the name of the calendar. - */ - def parse(calendarNameStr: String, errorFun: => Nothing): CalendarNameV2 = { + * Parses the name of a calendar. + * + * @param calendarNameStr a string representing the name of the calendar. + * @param errorFun a function that throws an exception. It will be called if the string cannot + * be parsed. + * @return a [[CalendarNameV2]] representing the name of the calendar. + */ + def parse(calendarNameStr: String, errorFun: => Nothing): CalendarNameV2 = calendarNameStr match { case StringFormatter.CalendarGregorian => CalendarNameGregorian case StringFormatter.CalendarJulian => CalendarNameJulian case StringFormatter.CalendarIslamic => CalendarNameIslamic case _ => errorFun } - } } /** - * Represents the name of a Gregorian or Julian calendar. - */ + * Represents the name of a Gregorian or Julian calendar. + */ sealed trait CalendarNameGregorianOrJulian extends CalendarNameV2 /** - * Represents the name of a Islamic Civil - */ + * Represents the name of a Islamic Civil + */ sealed trait CalendarNameIslamicCivil extends CalendarNameV2 /** - * Represents the name of the Gregorian calendar. - */ + * Represents the name of the Gregorian calendar. + */ case object CalendarNameGregorian extends CalendarNameGregorianOrJulian { override def toString: String = StringFormatter.CalendarGregorian } /** - * Represents the name of the Julian calendar. - */ + * Represents the name of the Julian calendar. + */ case object CalendarNameJulian extends CalendarNameGregorianOrJulian { override def toString: String = StringFormatter.CalendarJulian } /** - * Represents the name of the Islamic calendar. - */ + * Represents the name of the Islamic calendar. + */ case object CalendarNameIslamic extends CalendarNameIslamicCivil { override def toString: String = StringFormatter.CalendarIslamic } /** - * Represents a date as values that are suitable for constructing human-readable representations. - * - * @param calendarName the name of the calendar. - * @param year the date's year. - * @param maybeMonth the date's month, if given. - * @param maybeDay the date's day, if given. - * @param maybeEra the date's era, if the calendar supports it. An era is required in Gregorian and - * Julian calendars. - */ -case class CalendarDateV2(calendarName: CalendarNameV2, - year: Int, - maybeMonth: Option[Int], - maybeDay: Option[Int], - maybeEra: Option[DateEraV2]) { + * Represents a date as values that are suitable for constructing human-readable representations. + * + * @param calendarName the name of the calendar. + * @param year the date's year. + * @param maybeMonth the date's month, if given. + * @param maybeDay the date's day, if given. + * @param maybeEra the date's era, if the calendar supports it. An era is required in Gregorian and + * Julian calendars. + */ +case class CalendarDateV2( + calendarName: CalendarNameV2, + year: Int, + maybeMonth: Option[Int], + maybeDay: Option[Int], + maybeEra: Option[DateEraV2] +) { if (maybeMonth.isEmpty && maybeDay.isDefined) { throw AssertionException(s"Invalid date: CalendarDateV2($calendarName, $year, $maybeMonth, $maybeDay, $maybeEra)") } @@ -192,8 +191,8 @@ case class CalendarDateV2(calendarName: CalendarNameV2, } /** - * The precision of this date. - */ + * The precision of this date. + */ lazy val precision: DatePrecisionV2 = { (maybeMonth, maybeDay) match { case (Some(_), Some(_)) => DatePrecisionDay @@ -204,8 +203,8 @@ case class CalendarDateV2(calendarName: CalendarNameV2, } /** - * Returns this date in Knora API v2 simple format, without the calendar. - */ + * Returns this date in Knora API v2 simple format, without the calendar. + */ override def toString: String = { val eraString = maybeEra match { case Some(era) => s"${StringFormatter.EraSeparator}$era" @@ -230,8 +229,8 @@ case class CalendarDateV2(calendarName: CalendarNameV2, } /** - * Constructs a [[Calendar]] based on the calendar name and era, to be used in subsequent date conversions. - */ + * Constructs a [[Calendar]] based on the calendar name and era, to be used in subsequent date conversions. + */ private def makeBaseCalendar: Calendar = { def calendarSetInitTime(calendar: Calendar): Calendar = { calendar.set(Calendar.HOUR, 0) @@ -263,18 +262,17 @@ case class CalendarDateV2(calendarName: CalendarNameV2, } /** - * Converts this [[CalendarDateV2]] to a pair of Julian Day Numbers representing a date range. If the date's - * precision is [[DatePrecisionDay]], the two Julian Day Numbers will be equal. This method validates the date - * using its calendar. - */ - def toJulianDayRange: (Int, Int) = { + * Converts this [[CalendarDateV2]] to a pair of Julian Day Numbers representing a date range. If the date's + * precision is [[DatePrecisionDay]], the two Julian Day Numbers will be equal. This method validates the date + * using its calendar. + */ + def toJulianDayRange: (Int, Int) = // Note: // // In com.ibm.icu.util.Calendar, JULIAN_DAY demarcates days at local zone midnight, rather than noon GMT: // http://icu-project.org/apiref/icu4j/com/ibm/icu/util/GregorianCalendar.html#setGregorianChange-java.util.Date- // // Month is 0-based. - try { precision match { case DatePrecisionYear => @@ -322,19 +320,18 @@ case class CalendarDateV2(calendarName: CalendarNameV2, case e: Exception => throw BadRequestException(s"The date '${this.toString}' could not be handled correctly: ${e.getMessage}") } - } } object CalendarDateV2 { /** - * Converts a Julian Day Number to a [[CalendarDateV2]]. - * - * @param julianDay the Julian Day Number. - * @param precision the desired precision. - * @param calendarName the name of the calendar to be used. - * @return a [[CalendarDateV2]] with the specified precision and calendar. - */ + * Converts a Julian Day Number to a [[CalendarDateV2]]. + * + * @param julianDay the Julian Day Number. + * @param precision the desired precision. + * @param calendarName the name of the calendar to be used. + * @return a [[CalendarDateV2]] with the specified precision and calendar. + */ def fromJulianDayNumber(julianDay: Int, precision: DatePrecisionV2, calendarName: CalendarNameV2): CalendarDateV2 = { // Convert the Julian Day Number to a com.ibm.icu.util.Calendar. val (calendar: Calendar, maybeEra: Option[DateEraV2]) = calendarName match { @@ -396,13 +393,13 @@ object CalendarDateV2 { } /** - * Parses a string representing a single date, without the calendar. This method does does not check that the - * date is valid in its calendar; to do that, call `toJulianDayRange` on it. - * - * @param dateStr the string to be parsed. - * @param calendarName the name of the calendar used. - * @return a [[CalendarDateV2]] representing the date. - */ + * Parses a string representing a single date, without the calendar. This method does does not check that the + * date is valid in its calendar; to do that, call `toJulianDayRange` on it. + * + * @param dateStr the string to be parsed. + * @param calendarName the name of the calendar used. + * @return a [[CalendarDateV2]] representing the date. + */ def parse(dateStr: String, calendarName: CalendarNameV2): CalendarDateV2 = { // Get the era, if provided. @@ -466,21 +463,21 @@ object CalendarDateV2 { } /** - * Represents a date range consisting of two instances of [[CalendarDateV2]]. Both instances must have the same - * calendar. - * - * @param startCalendarDate the start of the range. - * @param endCalendarDate the end of the range. - */ + * Represents a date range consisting of two instances of [[CalendarDateV2]]. Both instances must have the same + * calendar. + * + * @param startCalendarDate the start of the range. + * @param endCalendarDate the end of the range. + */ case class CalendarDateRangeV2(startCalendarDate: CalendarDateV2, endCalendarDate: CalendarDateV2) { if (startCalendarDate.calendarName != endCalendarDate.calendarName) { throw AssertionException("Both dates in a date range must have the same calendar") } /** - * Returns this date range in Knora API v2 simple format, with the calendar. If the start and end dates - * are equal, only one date is returned. - */ + * Returns this date range in Knora API v2 simple format, with the calendar. If the start and end dates + * are equal, only one date is returned. + */ override def toString: String = { // Concatenate the calendar name and the start date. val strBuilder = @@ -497,8 +494,8 @@ case class CalendarDateRangeV2(startCalendarDate: CalendarDateV2, endCalendarDat } /** - * Converts this [[CalendarDateRangeV2]] to a pair of Julian Day Numbers representing a date range. - */ + * Converts this [[CalendarDateRangeV2]] to a pair of Julian Day Numbers representing a date range. + */ def toJulianDayRange: (Int, Int) = { // Is this a date range or a single date? val (startJDN: Int, endJDN: Int) = if (startCalendarDate == endCalendarDate) { @@ -522,13 +519,13 @@ case class CalendarDateRangeV2(startCalendarDate: CalendarDateV2, endCalendarDat object CalendarDateRangeV2 { /** - * Parses a string representing a date range with a calendar. If the end date is not provided, it is assumed to be - * the same as the start date. This method does syntactic validation, but does not check that the date range is valid - * in its calendar; to do that, call `toJulianDayRange` on it. - * - * @param dateStr the string to be parsed. - * @return a [[CalendarDateRangeV2]] representing the date range. - */ + * Parses a string representing a date range with a calendar. If the end date is not provided, it is assumed to be + * the same as the start date. This method does syntactic validation, but does not check that the date range is valid + * in its calendar; to do that, call `toJulianDayRange` on it. + * + * @param dateStr the string to be parsed. + * @return a [[CalendarDateRangeV2]] representing the date range. + */ def parse(dateStr: String): CalendarDateRangeV2 = { // Validate the date string. val stringFormatter = StringFormatter.getGeneralInstance @@ -561,24 +558,23 @@ object CalendarDateRangeV2 { } /** - * Utility functions for working with calendar dates. - */ + * Utility functions for working with calendar dates. + */ object CalendarDateUtilV2 { /** - * Returns the date that must be passed to `com.ibm.icu.util.GregorianCalendar.setGregorianChange()` to select - * either the Gregorian or the Julian calendar. - * - * @param calendarName the name of the Gregorian or Julian calendar. - * @return a date that can be passed to `com.ibm.icu.util.GregorianCalendar.setGregorianChange()` to select - * the specified calendar. - */ - def getGregorianCalendarChangeDate(calendarName: CalendarNameGregorianOrJulian): Date = { + * Returns the date that must be passed to `com.ibm.icu.util.GregorianCalendar.setGregorianChange()` to select + * either the Gregorian or the Julian calendar. + * + * @param calendarName the name of the Gregorian or Julian calendar. + * @return a date that can be passed to `com.ibm.icu.util.GregorianCalendar.setGregorianChange()` to select + * the specified calendar. + */ + def getGregorianCalendarChangeDate(calendarName: CalendarNameGregorianOrJulian): Date = // http://icu-project.org/apiref/icu4j/com/ibm/icu/util/GregorianCalendar.html#setGregorianChange-java.util.Date- calendarName match { case CalendarNameJulian => new Date(java.lang.Long.MAX_VALUE) case CalendarNameGregorian => new Date(java.lang.Long.MIN_VALUE) case _ => throw AssertionException(s"Invalid calendar: $calendarName") } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala index 082da56969..f0e1326463 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala @@ -64,302 +64,307 @@ object ConstructResponseUtilV2 { ) /** - * A map of resource IRIs to resource RDF data. - */ + * A map of resource IRIs to resource RDF data. + */ type RdfResources = Map[IRI, ResourceWithValueRdfData] /** - * Makes an empty instance of [[RdfResources]]. - */ + * Makes an empty instance of [[RdfResources]]. + */ def emptyRdfResources: RdfResources = Map.empty /** - * A map of property IRIs to value RDF data. - */ + * A map of property IRIs to value RDF data. + */ type RdfPropertyValues = Map[SmartIri, Seq[ValueRdfData]] /** - * Makes an empty instance of [[RdfPropertyValues]]. - */ + * Makes an empty instance of [[RdfPropertyValues]]. + */ def emptyRdfPropertyValues: RdfPropertyValues = Map.empty /** - * A map of subject IRIs to [[ConstructPredicateObjects]] instances. - */ + * A map of subject IRIs to [[ConstructPredicateObjects]] instances. + */ type Statements = Map[IRI, ConstructPredicateObjects] /** - * A flattened map of predicates to objects. This assumes that each predicate has - * * only one object. - */ + * A flattened map of predicates to objects. This assumes that each predicate has + * * only one object. + */ type FlatPredicateObjects = Map[SmartIri, LiteralV2] /** - * A map of subject IRIs to flattened maps of predicates to objects. - */ + * A map of subject IRIs to flattened maps of predicates to objects. + */ type FlatStatements = Map[IRI, Map[SmartIri, LiteralV2]] /** - * Makes an empty instance of [[FlatStatements]]. - */ + * Makes an empty instance of [[FlatStatements]]. + */ def emptyFlatStatements: FlatStatements = Map.empty /** - * Represents assertions about an RDF subject. - */ + * Represents assertions about an RDF subject. + */ sealed trait RdfData { /** - * The IRI of the subject. - */ + * The IRI of the subject. + */ val subjectIri: IRI /** - * Assertions about the subject. - */ + * Assertions about the subject. + */ val assertions: FlatPredicateObjects /** - * Returns the optional string object of the specified predicate. Throws an exception if the object is not a string. - * - * @param predicateIri the predicate. - * @return the string object of the predicate. - */ - def maybeStringObject(predicateIri: SmartIri): Option[String] = { + * Returns the optional string object of the specified predicate. Throws an exception if the object is not a string. + * + * @param predicateIri the predicate. + * @return the string object of the predicate. + */ + def maybeStringObject(predicateIri: SmartIri): Option[String] = assertions.get(predicateIri).map { literal => literal .asStringLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal")) + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal") + ) .value } - } /** - * Returns the required string object of the specified predicate. Throws an exception if the object is not found or - * is not a string. - * - * @param predicateIri the predicate. - * @return the string object of the predicate. - */ - def requireStringObject(predicateIri: SmartIri): String = { + * Returns the required string object of the specified predicate. Throws an exception if the object is not found or + * is not a string. + * + * @param predicateIri the predicate. + * @return the string object of the predicate. + */ + def requireStringObject(predicateIri: SmartIri): String = maybeStringObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri")) - } + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri") + ) /** - * Returns the optional IRI object of the specified predicate. Throws an exception if the object is not an IRI. - * - * @param predicateIri the predicate. - * @return the IRI object of the predicate. - */ - def maybeIriObject(predicateIri: SmartIri): Option[IRI] = { + * Returns the optional IRI object of the specified predicate. Throws an exception if the object is not an IRI. + * + * @param predicateIri the predicate. + * @return the IRI object of the predicate. + */ + def maybeIriObject(predicateIri: SmartIri): Option[IRI] = assertions.get(predicateIri).map { literal => literal .asIriLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal")) + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal") + ) .value } - } /** - * Returns the required IRI object of the specified predicate. Throws an exception if the object is not found or - * is not an IRI. - * - * @param predicateIri the predicate. - * @return the IRI object of the predicate. - */ - def requireIriObject(predicateIri: SmartIri): IRI = { + * Returns the required IRI object of the specified predicate. Throws an exception if the object is not found or + * is not an IRI. + * + * @param predicateIri the predicate. + * @return the IRI object of the predicate. + */ + def requireIriObject(predicateIri: SmartIri): IRI = maybeIriObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri")) - } + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri") + ) /** - * Returns the optional integer object of the specified predicate. Throws an exception if the object is not an integer. - * - * @param predicateIri the predicate. - * @return the integer object of the predicate. - */ - def maybeIntObject(predicateIri: SmartIri): Option[Int] = { + * Returns the optional integer object of the specified predicate. Throws an exception if the object is not an integer. + * + * @param predicateIri the predicate. + * @return the integer object of the predicate. + */ + def maybeIntObject(predicateIri: SmartIri): Option[Int] = assertions.get(predicateIri).map { literal => literal .asIntLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal")) + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal") + ) .value } - } /** - * Returns the required integer object of the specified predicate. Throws an exception if the object is not found or - * is not an integer. - * - * @param predicateIri the predicate. - * @return the integer object of the predicate. - */ - def requireIntObject(predicateIri: SmartIri): Int = { + * Returns the required integer object of the specified predicate. Throws an exception if the object is not found or + * is not an integer. + * + * @param predicateIri the predicate. + * @return the integer object of the predicate. + */ + def requireIntObject(predicateIri: SmartIri): Int = maybeIntObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri")) - } + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri") + ) /** - * Returns the optional boolean object of the specified predicate. Throws an exception if the object is not a boolean. - * - * @param predicateIri the predicate. - * @return the boolean object of the predicate. - */ - def maybeBooleanObject(predicateIri: SmartIri): Option[Boolean] = { + * Returns the optional boolean object of the specified predicate. Throws an exception if the object is not a boolean. + * + * @param predicateIri the predicate. + * @return the boolean object of the predicate. + */ + def maybeBooleanObject(predicateIri: SmartIri): Option[Boolean] = assertions.get(predicateIri).map { literal => literal .asBooleanLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal")) + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal") + ) .value } - } /** - * Returns the required boolean object of the specified predicate. Throws an exception if the object is not found or - * is not an boolean value. - * - * @param predicateIri the predicate. - * @return the boolean object of the predicate. - */ - def requireBooleanObject(predicateIri: SmartIri): Boolean = { + * Returns the required boolean object of the specified predicate. Throws an exception if the object is not found or + * is not an boolean value. + * + * @param predicateIri the predicate. + * @return the boolean object of the predicate. + */ + def requireBooleanObject(predicateIri: SmartIri): Boolean = maybeBooleanObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri")) - } + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri") + ) /** - * Returns the optional decimal object of the specified predicate. Throws an exception if the object is not a decimal. - * - * @param predicateIri the predicate. - * @return the decimal object of the predicate. - */ - def maybeDecimalObject(predicateIri: SmartIri): Option[BigDecimal] = { + * Returns the optional decimal object of the specified predicate. Throws an exception if the object is not a decimal. + * + * @param predicateIri the predicate. + * @return the decimal object of the predicate. + */ + def maybeDecimalObject(predicateIri: SmartIri): Option[BigDecimal] = assertions.get(predicateIri).map { literal => literal .asDecimalLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal")) + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal") + ) .value } - } /** - * Returns the required decimal object of the specified predicate. Throws an exception if the object is not found or - * is not an decimal value. - * - * @param predicateIri the predicate. - * @return the decimal object of the predicate. - */ - def requireDecimalObject(predicateIri: SmartIri): BigDecimal = { + * Returns the required decimal object of the specified predicate. Throws an exception if the object is not found or + * is not an decimal value. + * + * @param predicateIri the predicate. + * @return the decimal object of the predicate. + */ + def requireDecimalObject(predicateIri: SmartIri): BigDecimal = maybeDecimalObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri")) - } + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri") + ) /** - * Returns the optional timestamp object of the specified predicate. Throws an exception if the object is not a timestamp. - * - * @param predicateIri the predicate. - * @return the timestamp object of the predicate. - */ - def maybeDateTimeObject(predicateIri: SmartIri): Option[Instant] = { + * Returns the optional timestamp object of the specified predicate. Throws an exception if the object is not a timestamp. + * + * @param predicateIri the predicate. + * @return the timestamp object of the predicate. + */ + def maybeDateTimeObject(predicateIri: SmartIri): Option[Instant] = assertions.get(predicateIri).map { literal => literal .asDateTimeLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal")) + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal") + ) .value } - } /** - * Returns the required timestamp object of the specified predicate. Throws an exception if the object is not found or - * is not an timestamp value. - * - * @param predicateIri the predicate. - * @return the timestamp object of the predicate. - */ - def requireDateTimeObject(predicateIri: SmartIri): Instant = { + * Returns the required timestamp object of the specified predicate. Throws an exception if the object is not found or + * is not an timestamp value. + * + * @param predicateIri the predicate. + * @return the timestamp object of the predicate. + */ + def requireDateTimeObject(predicateIri: SmartIri): Instant = maybeDateTimeObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri")) - } + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri") + ) } /** - * Represents the RDF data about a value, possibly including standoff. - * - * @param subjectIri the value object's IRI. - * @param valueObjectClass the type (class) of the value object. - * @param nestedResource the nested resource in case of a link value (either the source or the target of a link value, depending on [[isIncomingLink]]). - * @param isIncomingLink indicates if it is an incoming or outgoing link in case of a link value. - * @param userPermission the permission that the requesting user has on the value. - * @param assertions the value objects assertions. - * @param standoff standoff assertions, if any. - */ - case class ValueRdfData(subjectIri: IRI, - valueObjectClass: SmartIri, - nestedResource: Option[ResourceWithValueRdfData] = None, - isIncomingLink: Boolean = false, - userPermission: EntityPermission, - assertions: FlatPredicateObjects, - standoff: FlatStatements) - extends RdfData + * Represents the RDF data about a value, possibly including standoff. + * + * @param subjectIri the value object's IRI. + * @param valueObjectClass the type (class) of the value object. + * @param nestedResource the nested resource in case of a link value (either the source or the target of a link value, depending on [[isIncomingLink]]). + * @param isIncomingLink indicates if it is an incoming or outgoing link in case of a link value. + * @param userPermission the permission that the requesting user has on the value. + * @param assertions the value objects assertions. + * @param standoff standoff assertions, if any. + */ + case class ValueRdfData( + subjectIri: IRI, + valueObjectClass: SmartIri, + nestedResource: Option[ResourceWithValueRdfData] = None, + isIncomingLink: Boolean = false, + userPermission: EntityPermission, + assertions: FlatPredicateObjects, + standoff: FlatStatements + ) extends RdfData /** - * Represents a resource and its values. - * - * @param subjectIri the resource IRI. - * @param assertions assertions about the resource (direct statements). - * @param isMainResource indicates if this represents a top level resource or a referred resource (depending on the query). - * @param userPermission the permission that the requesting user has on the resource. - * @param valuePropertyAssertions assertions about value properties. - */ - case class ResourceWithValueRdfData(subjectIri: IRI, - assertions: FlatPredicateObjects, - isMainResource: Boolean, - userPermission: Option[EntityPermission], - valuePropertyAssertions: RdfPropertyValues) - extends RdfData + * Represents a resource and its values. + * + * @param subjectIri the resource IRI. + * @param assertions assertions about the resource (direct statements). + * @param isMainResource indicates if this represents a top level resource or a referred resource (depending on the query). + * @param userPermission the permission that the requesting user has on the resource. + * @param valuePropertyAssertions assertions about value properties. + */ + case class ResourceWithValueRdfData( + subjectIri: IRI, + assertions: FlatPredicateObjects, + isMainResource: Boolean, + userPermission: Option[EntityPermission], + valuePropertyAssertions: RdfPropertyValues + ) extends RdfData /** - * Represents a mapping including information about the standoff entities. - * May include a default XSL transformation. - * - * @param mapping the mapping from XML to standoff and vice versa. - * @param standoffEntities information about the standoff entities referred to in the mapping. - * @param XSLTransformation the default XSL transformation to convert the resulting XML (e.g., to HTML), if any. - */ - case class MappingAndXSLTransformation(mapping: MappingXMLtoStandoff, - standoffEntities: StandoffEntityInfoGetResponseV2, - XSLTransformation: Option[String]) + * Represents a mapping including information about the standoff entities. + * May include a default XSL transformation. + * + * @param mapping the mapping from XML to standoff and vice versa. + * @param standoffEntities information about the standoff entities referred to in the mapping. + * @param XSLTransformation the default XSL transformation to convert the resulting XML (e.g., to HTML), if any. + */ + case class MappingAndXSLTransformation( + mapping: MappingXMLtoStandoff, + standoffEntities: StandoffEntityInfoGetResponseV2, + XSLTransformation: Option[String] + ) /** - * Represents a tree structure of resources, values and dependent resources returned by a SPARQL CONSTRUCT query. - * - * @param resources a map of resource Iris to [[ResourceWithValueRdfData]]. The resource Iris represent main resources, dependent - * resources are contained in the link values as nested structures. - * @param hiddenResourceIris the IRIs of resources that were hidden because the user does not have permission - * to see them. - */ + * Represents a tree structure of resources, values and dependent resources returned by a SPARQL CONSTRUCT query. + * + * @param resources a map of resource Iris to [[ResourceWithValueRdfData]]. The resource Iris represent main resources, dependent + * resources are contained in the link values as nested structures. + * @param hiddenResourceIris the IRIs of resources that were hidden because the user does not have permission + * to see them. + */ case class MainResourcesAndValueRdfData(resources: RdfResources, hiddenResourceIris: Set[IRI] = Set.empty) /** - * An intermediate data structure containing RDF assertions about an entity and the user's permission on the entity. - * - * @param assertions RDF assertions about the entity. - * @param maybeUserPermission the user's permission on the entity, if any. - */ + * An intermediate data structure containing RDF assertions about an entity and the user's permission on the entity. + * + * @param assertions RDF assertions about the entity. + * @param maybeUserPermission the user's permission on the entity, if any. + */ case class RdfWithUserPermission(assertions: ConstructPredicateObjects, maybeUserPermission: Option[EntityPermission]) /** - * A [[SparqlConstructResponse]] may contain both resources and value RDF data objects as well as standoff. - * This method turns a graph (i.e. triples) into a structure organized by the principle of resources and their values, - * i.e. a map of resource Iris to [[ResourceWithValueRdfData]]. - * The resource Iris represent main resources, dependent resources are contained in the link values as nested structures. - * - * @param constructQueryResults the results of a SPARQL construct query representing resources and their values. - * @return an instance of [[MainResourcesAndValueRdfData]]. - */ + * A [[SparqlConstructResponse]] may contain both resources and value RDF data objects as well as standoff. + * This method turns a graph (i.e. triples) into a structure organized by the principle of resources and their values, + * i.e. a map of resource Iris to [[ResourceWithValueRdfData]]. + * The resource Iris represent main resources, dependent resources are contained in the link values as nested structures. + * + * @param constructQueryResults the results of a SPARQL construct query representing resources and their values. + * @return an instance of [[MainResourcesAndValueRdfData]]. + */ def splitMainResourcesAndValueRdfData( - constructQueryResults: SparqlExtendedConstructResponse, - requestingUser: UserADM)(implicit stringFormatter: StringFormatter): MainResourcesAndValueRdfData = { + constructQueryResults: SparqlExtendedConstructResponse, + requestingUser: UserADM + )(implicit stringFormatter: StringFormatter): MainResourcesAndValueRdfData = { // Make sure all the subjects are IRIs, because blank nodes are not used in resources. val resultsWithIriSubjects: Statements = constructQueryResults.statements.map { @@ -386,21 +391,18 @@ object ConstructResponseUtilV2 { // - every resource is a knora-base:Resource // - every value property is a subproperty of knora-base:hasValue // - every resource that's a main resource (not a dependent resource) in the query result has knora-base:isMainResource true - val assertionsExplicit: ConstructPredicateObjects = assertions - .filterNot { - case (pred: SmartIri, _) => InferredPredicates(pred.toString) - } - .map { - case (pred: SmartIri, objs: Seq[LiteralV2]) => - if (pred.toString == OntologyConstants.Rdf.Type) { - pred -> objs.filterNot { - case IriLiteralV2(OntologyConstants.KnoraBase.Resource) => true - case _ => false - } - } else { - pred -> objs - } + val assertionsExplicit: ConstructPredicateObjects = assertions.filterNot { case (pred: SmartIri, _) => + InferredPredicates(pred.toString) + }.map { case (pred: SmartIri, objs: Seq[LiteralV2]) => + if (pred.toString == OntologyConstants.Rdf.Type) { + pred -> objs.filterNot { + case IriLiteralV2(OntologyConstants.KnoraBase.Resource) => true + case _ => false + } + } else { + pred -> objs } + } // check for the knora-base:isMainResource flag created by the SPARQL CONSTRUCT query val isMainResource: Boolean = assertions.get(OntologyConstants.KnoraBase.IsMainResource.toSmartIri) match { @@ -409,18 +411,16 @@ object ConstructResponseUtilV2 { } // Make a set of all the value object IRIs, because we're going to associate them with their properties. - val valueObjectIris: Set[IRI] = assertions - .collect { - case (pred: SmartIri, objs: Seq[LiteralV2]) if pred.toString == OntologyConstants.KnoraBase.HasValue => - objs.map { - case IriLiteralV2(iri) => iri - case other => - throw InconsistentRepositoryDataException( - s"Unexpected object for $resourceIri knora-base:hasValue: $other") - } - } - .flatten - .toSet + val valueObjectIris: Set[IRI] = assertions.collect { + case (pred: SmartIri, objs: Seq[LiteralV2]) if pred.toString == OntologyConstants.KnoraBase.HasValue => + objs.map { + case IriLiteralV2(iri) => iri + case other => + throw InconsistentRepositoryDataException( + s"Unexpected object for $resourceIri knora-base:hasValue: $other" + ) + } + }.flatten.toSet // Make a map of property IRIs to sequences of value IRIs. val valuePropertyToObjectIris: Map[SmartIri, Seq[IRI]] = @@ -498,276 +498,264 @@ object ConstructResponseUtilV2 { } /** - * Converts a [[ConstructPredicateObjects]] to a map of property IRIs to sequences of value IRIs. - * - * @param assertionsExplicit all non-inferred statements. - * @param valueObjectIris a set of all value object IRIs. - * @return a map of property IRIs to sequences of value IRIs. - */ - private def mapPropertyIrisToValueIris(assertionsExplicit: ConstructPredicateObjects, - valueObjectIris: Set[IRI]): Map[SmartIri, Seq[IRI]] = { - assertionsExplicit - .map { - case (pred: SmartIri, objs: Seq[LiteralV2]) => - // Get only the assertions in which the object is a value object IRI. - val valueObjIris: Seq[IriLiteralV2] = objs.collect { - case iriObj: IriLiteralV2 if valueObjectIris(iriObj.value) => iriObj - } - - // create an entry using pred as a key and valueObjIris as the value - pred -> valueObjIris - } - .filter { - case (_: SmartIri, objs: Seq[IriLiteralV2]) => objs.nonEmpty - } - .groupBy { - case (pred: SmartIri, _: Seq[IriLiteralV2]) => - // Turn the sequence of assertions into a Map of predicate IRIs to assertions. - pred + * Converts a [[ConstructPredicateObjects]] to a map of property IRIs to sequences of value IRIs. + * + * @param assertionsExplicit all non-inferred statements. + * @param valueObjectIris a set of all value object IRIs. + * @return a map of property IRIs to sequences of value IRIs. + */ + private def mapPropertyIrisToValueIris( + assertionsExplicit: ConstructPredicateObjects, + valueObjectIris: Set[IRI] + ): Map[SmartIri, Seq[IRI]] = + assertionsExplicit.map { case (pred: SmartIri, objs: Seq[LiteralV2]) => + // Get only the assertions in which the object is a value object IRI. + val valueObjIris: Seq[IriLiteralV2] = objs.collect { + case iriObj: IriLiteralV2 if valueObjectIris(iriObj.value) => iriObj } - .map { - case (pred: SmartIri, valueAssertions: Map[SmartIri, Seq[IriLiteralV2]]) => - // Replace the assertions with their objects, i.e. the value object IRIs. - pred -> valueAssertions.values.flatten.map(_.value).toSeq - } - } + + // create an entry using pred as a key and valueObjIris as the value + pred -> valueObjIris + }.filter { case (_: SmartIri, objs: Seq[IriLiteralV2]) => + objs.nonEmpty + }.groupBy { case (pred: SmartIri, _: Seq[IriLiteralV2]) => + // Turn the sequence of assertions into a Map of predicate IRIs to assertions. + pred + }.map { case (pred: SmartIri, valueAssertions: Map[SmartIri, Seq[IriLiteralV2]]) => + // Replace the assertions with their objects, i.e. the value object IRIs. + pred -> valueAssertions.values.flatten.map(_.value).toSeq + } /** - * Given the assertions that describe a resource and its values, makes an [[RdfPropertyValues]] representing the values. - * - * @param valuePropertyToObjectIris a map of property IRIs to value IRIs. - * @param resourceIri the IRI of the resource. - * @param requestingUser the user making the request. - * @param assertionsExplicit all non-inferred statements. - * @param nonResourceStatements statements that are not about the containing resource. - * @return an [[RdfPropertyValues]] describing the values of the resource. - */ + * Given the assertions that describe a resource and its values, makes an [[RdfPropertyValues]] representing the values. + * + * @param valuePropertyToObjectIris a map of property IRIs to value IRIs. + * @param resourceIri the IRI of the resource. + * @param requestingUser the user making the request. + * @param assertionsExplicit all non-inferred statements. + * @param nonResourceStatements statements that are not about the containing resource. + * @return an [[RdfPropertyValues]] describing the values of the resource. + */ private def makeRdfPropertyValuesForResource( - valuePropertyToObjectIris: Map[SmartIri, Seq[IRI]], - resourceIri: IRI, - requestingUser: UserADM, - assertionsExplicit: ConstructPredicateObjects, - nonResourceStatements: Statements)(implicit stringFormatter: StringFormatter): RdfPropertyValues = { - valuePropertyToObjectIris - .map { - case (property: SmartIri, valObjIris: Seq[IRI]) => - // Make an RdfWithUserPermission for each value of the property. - val rdfWithUserPermissionsForValues: Seq[(IRI, RdfWithUserPermission)] = valObjIris.map { valObjIri: IRI => - val valueObjAssertions: ConstructPredicateObjects = nonResourceStatements(valObjIri) - - // get the resource's project - // value objects belong to the parent resource's project - - val resourceProjectLiteral: LiteralV2 = assertionsExplicit - .getOrElse( - OntologyConstants.KnoraBase.AttachedToProject.toSmartIri, - throw InconsistentRepositoryDataException(s"Resource $resourceIri has no knora-base:attachedToProject") - ) - .head - - // add the resource's project to the value's assertions, and get the user's permission on the value - val maybeUserPermission = PermissionUtilADM.getUserPermissionFromConstructAssertionsADM( - entityIri = valObjIri, - assertions = valueObjAssertions + (OntologyConstants.KnoraBase.AttachedToProject.toSmartIri -> Seq( - resourceProjectLiteral)), - requestingUser = requestingUser - ) + valuePropertyToObjectIris: Map[SmartIri, Seq[IRI]], + resourceIri: IRI, + requestingUser: UserADM, + assertionsExplicit: ConstructPredicateObjects, + nonResourceStatements: Statements + )(implicit stringFormatter: StringFormatter): RdfPropertyValues = { + valuePropertyToObjectIris.map { case (property: SmartIri, valObjIris: Seq[IRI]) => + // Make an RdfWithUserPermission for each value of the property. + val rdfWithUserPermissionsForValues: Seq[(IRI, RdfWithUserPermission)] = valObjIris.map { valObjIri: IRI => + val valueObjAssertions: ConstructPredicateObjects = nonResourceStatements(valObjIri) + + // get the resource's project + // value objects belong to the parent resource's project + + val resourceProjectLiteral: LiteralV2 = assertionsExplicit + .getOrElse( + OntologyConstants.KnoraBase.AttachedToProject.toSmartIri, + throw InconsistentRepositoryDataException(s"Resource $resourceIri has no knora-base:attachedToProject") + ) + .head + + // add the resource's project to the value's assertions, and get the user's permission on the value + val maybeUserPermission = PermissionUtilADM.getUserPermissionFromConstructAssertionsADM( + entityIri = valObjIri, + assertions = valueObjAssertions + (OntologyConstants.KnoraBase.AttachedToProject.toSmartIri -> Seq( + resourceProjectLiteral + )), + requestingUser = requestingUser + ) - valObjIri -> RdfWithUserPermission(valueObjAssertions, maybeUserPermission) - } + valObjIri -> RdfWithUserPermission(valueObjAssertions, maybeUserPermission) + } - // Filter out objects that the user doesn't have permission to see. - val visibleRdfWithUserPermissionsForValues: Seq[(IRI, RdfWithUserPermission)] = - rdfWithUserPermissionsForValues.filter { - // check if the user has sufficient permissions to see the value object - case (_: IRI, rdfWithUserPermission: RdfWithUserPermission) => - rdfWithUserPermission.maybeUserPermission.nonEmpty - } + // Filter out objects that the user doesn't have permission to see. + val visibleRdfWithUserPermissionsForValues: Seq[(IRI, RdfWithUserPermission)] = + rdfWithUserPermissionsForValues.filter { + // check if the user has sufficient permissions to see the value object + case (_: IRI, rdfWithUserPermission: RdfWithUserPermission) => + rdfWithUserPermission.maybeUserPermission.nonEmpty + } - // Make a ValueRdfData for each value object. - val valueRdfDataForProperty: Seq[ValueRdfData] = visibleRdfWithUserPermissionsForValues.flatMap { - case (valObjIri: IRI, valueRdfWithUserPermission: RdfWithUserPermission) => - // get all the standoff node IRIs possibly belonging to this value object - val standoffNodeIris: Set[IRI] = valueRdfWithUserPermission.assertions - .collect { - case (pred: SmartIri, objs: Seq[LiteralV2]) - if pred.toString == OntologyConstants.KnoraBase.ValueHasStandoff => - objs.map(_.toString) + // Make a ValueRdfData for each value object. + val valueRdfDataForProperty: Seq[ValueRdfData] = visibleRdfWithUserPermissionsForValues.flatMap { + case (valObjIri: IRI, valueRdfWithUserPermission: RdfWithUserPermission) => + // get all the standoff node IRIs possibly belonging to this value object + val standoffNodeIris: Set[IRI] = valueRdfWithUserPermission.assertions.collect { + case (pred: SmartIri, objs: Seq[LiteralV2]) + if pred.toString == OntologyConstants.KnoraBase.ValueHasStandoff => + objs.map(_.toString) + }.flatten.toSet + + // given the standoff node IRIs, get the standoff assertions + val standoffAssertions: FlatStatements = nonResourceStatements.collect { + case (subjIri: IRI, assertions: ConstructPredicateObjects) if standoffNodeIris(subjIri) => + subjIri -> assertions.flatMap { case (pred: SmartIri, objs: Seq[LiteralV2]) => + objs.map { obj => + pred -> obj } - .flatten - .toSet - - // given the standoff node IRIs, get the standoff assertions - val standoffAssertions: FlatStatements = nonResourceStatements.collect { - case (subjIri: IRI, assertions: ConstructPredicateObjects) if standoffNodeIris(subjIri) => - subjIri -> assertions.flatMap { - case (pred: SmartIri, objs: Seq[LiteralV2]) => - objs.map { obj => - pred -> obj - } - } } + } - // Flatten the value's statements. - val valueStatements: FlatPredicateObjects = valueRdfWithUserPermission.assertions.flatMap { - case (pred: SmartIri, objs: Seq[LiteralV2]) => - objs.map { obj => - pred -> obj - } + // Flatten the value's statements. + val valueStatements: FlatPredicateObjects = valueRdfWithUserPermission.assertions.flatMap { + case (pred: SmartIri, objs: Seq[LiteralV2]) => + objs.map { obj => + pred -> obj } + } - // Get the rdf:type of the value. - val rdfTypeLiteral: LiteralV2 = valueStatements.getOrElse( - OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"Value $valObjIri has no rdf:type")) + // Get the rdf:type of the value. + val rdfTypeLiteral: LiteralV2 = valueStatements.getOrElse( + OntologyConstants.Rdf.Type.toSmartIri, + throw InconsistentRepositoryDataException(s"Value $valObjIri has no rdf:type") + ) - val valueObjectClass: SmartIri = rdfTypeLiteral - .asIriLiteral( - throw InconsistentRepositoryDataException( - s"Unexpected object of $valObjIri rdf:type: $rdfTypeLiteral") - ) - .value - .toSmartIri - - // check if it is a link value - if (valueObjectClass.toString == OntologyConstants.KnoraBase.LinkValue) { - // create a link value object - Some( - ValueRdfData( - subjectIri = valObjIri, - valueObjectClass = valueObjectClass, - userPermission = valueRdfWithUserPermission.maybeUserPermission.get, - assertions = valueStatements, - standoff = emptyFlatStatements // link value does not contain standoff - ) - ) + val valueObjectClass: SmartIri = rdfTypeLiteral + .asIriLiteral( + throw InconsistentRepositoryDataException(s"Unexpected object of $valObjIri rdf:type: $rdfTypeLiteral") + ) + .value + .toSmartIri - } else { - // create a non-link value object - Some( - ValueRdfData( - subjectIri = valObjIri, - valueObjectClass = valueObjectClass, - userPermission = valueRdfWithUserPermission.maybeUserPermission.get, - assertions = valueStatements, - standoff = standoffAssertions - ) - ) - } - } + // check if it is a link value + if (valueObjectClass.toString == OntologyConstants.KnoraBase.LinkValue) { + // create a link value object + Some( + ValueRdfData( + subjectIri = valObjIri, + valueObjectClass = valueObjectClass, + userPermission = valueRdfWithUserPermission.maybeUserPermission.get, + assertions = valueStatements, + standoff = emptyFlatStatements // link value does not contain standoff + ) + ) - // Associate each property IRI with its Seq[ValueRdfData]. - property -> valueRdfDataForProperty - } - .filterNot { - // filter out those properties that do not have value objects (they may have been filtered out because the user does not have sufficient permissions to see them) - case (_, valObjs: Seq[ValueRdfData]) => - valObjs.isEmpty + } else { + // create a non-link value object + Some( + ValueRdfData( + subjectIri = valObjIri, + valueObjectClass = valueObjectClass, + userPermission = valueRdfWithUserPermission.maybeUserPermission.get, + assertions = valueStatements, + standoff = standoffAssertions + ) + ) + } } + + // Associate each property IRI with its Seq[ValueRdfData]. + property -> valueRdfDataForProperty + }.filterNot { + // filter out those properties that do not have value objects (they may have been filtered out because the user does not have sufficient permissions to see them) + case (_, valObjs: Seq[ValueRdfData]) => + valObjs.isEmpty + } } /** - * This method returns all the incoming link for each resource as a map of resource IRI to resources that link to it. - * - * @param visibleResources the resources that the user has permission to see - * @param flatResourcesWithValues the set of resources with their representing values, before permission filtering - * @return the incoming links as a map of resource IRIs - */ - private def getIncomingLink(visibleResources: RdfResources, flatResourcesWithValues: RdfResources)( - implicit stringFormatter: StringFormatter): Map[IRI, RdfResources] = { - visibleResources.map { - case (resourceIri: IRI, values: ResourceWithValueRdfData) => - // get all incoming links for resourceIri - val incomingLinksForRes: RdfResources = flatResourcesWithValues.foldLeft(emptyRdfResources) { - case (acc: RdfResources, (otherResourceIri: IRI, otherResource: ResourceWithValueRdfData)) => - // get all incoming links having assertions about value properties pointing to this resource - val incomingLinkPropertyAssertions: RdfPropertyValues = - otherResource.valuePropertyAssertions.foldLeft(emptyRdfPropertyValues) { - case (acc: RdfPropertyValues, (prop: SmartIri, otherResourceValues: Seq[ValueRdfData])) => - // collect all link values that point to resourceIri - val incomingLinkValues: Seq[ValueRdfData] = otherResourceValues.foldLeft(Seq.empty[ValueRdfData]) { - (acc, value: ValueRdfData) => - // check if it is a link value and points to this resource - if (value.valueObjectClass.toString == OntologyConstants.KnoraBase.LinkValue && value - .requireIriObject(OntologyConstants.Rdf.Object.toSmartIri) == resourceIri) { - acc :+ value - } else { - acc - } - } - - // check if the link value already exists - if (incomingLinkValues.nonEmpty) { - // add link value to the existing values - acc + (prop -> incomingLinkValues) - } else { - // it does not already exists therefore add the new oone - acc - } - } + * This method returns all the incoming link for each resource as a map of resource IRI to resources that link to it. + * + * @param visibleResources the resources that the user has permission to see + * @param flatResourcesWithValues the set of resources with their representing values, before permission filtering + * @return the incoming links as a map of resource IRIs + */ + private def getIncomingLink(visibleResources: RdfResources, flatResourcesWithValues: RdfResources)(implicit + stringFormatter: StringFormatter + ): Map[IRI, RdfResources] = + visibleResources.map { case (resourceIri: IRI, values: ResourceWithValueRdfData) => + // get all incoming links for resourceIri + val incomingLinksForRes: RdfResources = flatResourcesWithValues.foldLeft(emptyRdfResources) { + case (acc: RdfResources, (otherResourceIri: IRI, otherResource: ResourceWithValueRdfData)) => + // get all incoming links having assertions about value properties pointing to this resource + val incomingLinkPropertyAssertions: RdfPropertyValues = + otherResource.valuePropertyAssertions.foldLeft(emptyRdfPropertyValues) { + case (acc: RdfPropertyValues, (prop: SmartIri, otherResourceValues: Seq[ValueRdfData])) => + // collect all link values that point to resourceIri + val incomingLinkValues: Seq[ValueRdfData] = otherResourceValues.foldLeft(Seq.empty[ValueRdfData]) { + (acc, value: ValueRdfData) => + // check if it is a link value and points to this resource + if ( + value.valueObjectClass.toString == OntologyConstants.KnoraBase.LinkValue && value + .requireIriObject(OntologyConstants.Rdf.Object.toSmartIri) == resourceIri + ) { + acc :+ value + } else { + acc + } + } - // check if the property assertion already exists - if (incomingLinkPropertyAssertions.nonEmpty) { - // add resource values to the existing values - acc + (otherResourceIri -> values.copy( - valuePropertyAssertions = incomingLinkPropertyAssertions - )) - } else { - // it does not already exist therefore add the new one - acc + // check if the link value already exists + if (incomingLinkValues.nonEmpty) { + // add link value to the existing values + acc + (prop -> incomingLinkValues) + } else { + // it does not already exists therefore add the new oone + acc + } } - } + // check if the property assertion already exists + if (incomingLinkPropertyAssertions.nonEmpty) { + // add resource values to the existing values + acc + (otherResourceIri -> values.copy( + valuePropertyAssertions = incomingLinkPropertyAssertions + )) + } else { + // it does not already exist therefore add the new one + acc + } + + } - // create an entry using the resource's Iri as a key and its incoming links as the value - resourceIri -> incomingLinksForRes + // create an entry using the resource's Iri as a key and its incoming links as the value + resourceIri -> incomingLinksForRes } - } /** - * Given a resource IRI, finds any link values in the resource, and recursively embeds the target resource in each link value. - * - * @param resourceIri the IRI of the resource to start with. - * @param flatResourcesWithValues the complete set of resources with their values, before permission filtering. - * @param visibleResources the resources that the user has permission to see. - * @param dependentResourceIrisVisible the IRIs of dependent resources that the user has permission to see. - * @param dependentResourceIrisNotVisible the IRIs of dependent resources that the user does not have permission to see. - * @param incomingLinksForResource a map of resource IRIs to resources that link to each resource. - * @param alreadyTraversed a set (initially empty) of the IRIs of resources that this function has already - * traversed, to prevent an infinite loop if a cycle is encountered. - * @return the same resource, with any nested resources attached to it. - */ - private def nestResources(resourceIri: IRI, - flatResourcesWithValues: RdfResources, - visibleResources: RdfResources, - dependentResourceIrisVisible: Set[IRI], - dependentResourceIrisNotVisible: Set[IRI], - incomingLinksForResource: Map[IRI, RdfResources], - alreadyTraversed: Set[IRI] = Set.empty[IRI])( - implicit stringFormatter: StringFormatter): ResourceWithValueRdfData = { + * Given a resource IRI, finds any link values in the resource, and recursively embeds the target resource in each link value. + * + * @param resourceIri the IRI of the resource to start with. + * @param flatResourcesWithValues the complete set of resources with their values, before permission filtering. + * @param visibleResources the resources that the user has permission to see. + * @param dependentResourceIrisVisible the IRIs of dependent resources that the user has permission to see. + * @param dependentResourceIrisNotVisible the IRIs of dependent resources that the user does not have permission to see. + * @param incomingLinksForResource a map of resource IRIs to resources that link to each resource. + * @param alreadyTraversed a set (initially empty) of the IRIs of resources that this function has already + * traversed, to prevent an infinite loop if a cycle is encountered. + * @return the same resource, with any nested resources attached to it. + */ + private def nestResources( + resourceIri: IRI, + flatResourcesWithValues: RdfResources, + visibleResources: RdfResources, + dependentResourceIrisVisible: Set[IRI], + dependentResourceIrisNotVisible: Set[IRI], + incomingLinksForResource: Map[IRI, RdfResources], + alreadyTraversed: Set[IRI] = Set.empty[IRI] + )(implicit stringFormatter: StringFormatter): ResourceWithValueRdfData = { val resource = visibleResources(resourceIri) - val transformedValuePropertyAssertions: RdfPropertyValues = resource.valuePropertyAssertions - .map { - case (propIri: SmartIri, values: Seq[ValueRdfData]) => - val transformedValues: Seq[ValueRdfData] = transformValuesByNestingResources( - resourceIri = resourceIri, - values = values, - flatResourcesWithValues = flatResourcesWithValues, - visibleResources = visibleResources, - dependentResourceIrisVisible = dependentResourceIrisVisible, - dependentResourceIrisNotVisible = dependentResourceIrisNotVisible, - incomingLinksForResource = incomingLinksForResource, - alreadyTraversed = alreadyTraversed + resourceIri - ) + val transformedValuePropertyAssertions: RdfPropertyValues = resource.valuePropertyAssertions.map { + case (propIri: SmartIri, values: Seq[ValueRdfData]) => + val transformedValues: Seq[ValueRdfData] = transformValuesByNestingResources( + resourceIri = resourceIri, + values = values, + flatResourcesWithValues = flatResourcesWithValues, + visibleResources = visibleResources, + dependentResourceIrisVisible = dependentResourceIrisVisible, + dependentResourceIrisNotVisible = dependentResourceIrisNotVisible, + incomingLinksForResource = incomingLinksForResource, + alreadyTraversed = alreadyTraversed + resourceIri + ) - propIri -> transformedValues - } - .filter { - case (_: SmartIri, values: Seq[ValueRdfData]) => - // If we filtered out all the values for the property, filter out the property, too. - values.nonEmpty - } + propIri -> transformedValues + }.filter { case (_: SmartIri, values: Seq[ValueRdfData]) => + // If we filtered out all the values for the property, filter out the property, too. + values.nonEmpty + } // incomingLinksForResource contains incoming link values for each resource // flatResourcesWithValues contains the complete information @@ -804,27 +792,27 @@ object ConstructResponseUtilV2 { if (incomingLinkAssertions.nonEmpty) { // create a virtual property representing an incoming link - val incomingProps - : (SmartIri, Seq[ValueRdfData]) = OntologyConstants.KnoraBase.HasIncomingLinkValue.toSmartIri -> incomingLinkAssertions.values.toSeq.flatten - .map { linkValue: ValueRdfData => - // get the source of the link value (it points to the resource that is currently processed) - val sourceIri: IRI = linkValue.requireIriObject(OntologyConstants.Rdf.Subject.toSmartIri) - val source = Some( - nestResources( - resourceIri = sourceIri, - flatResourcesWithValues = flatResourcesWithValues, - visibleResources = visibleResources, - dependentResourceIrisVisible = dependentResourceIrisVisible, - dependentResourceIrisNotVisible = dependentResourceIrisNotVisible, - incomingLinksForResource = incomingLinksForResource, - alreadyTraversed = alreadyTraversed + resourceIri + val incomingProps: (SmartIri, Seq[ValueRdfData]) = + OntologyConstants.KnoraBase.HasIncomingLinkValue.toSmartIri -> incomingLinkAssertions.values.toSeq.flatten.map { + linkValue: ValueRdfData => + // get the source of the link value (it points to the resource that is currently processed) + val sourceIri: IRI = linkValue.requireIriObject(OntologyConstants.Rdf.Subject.toSmartIri) + val source = Some( + nestResources( + resourceIri = sourceIri, + flatResourcesWithValues = flatResourcesWithValues, + visibleResources = visibleResources, + dependentResourceIrisVisible = dependentResourceIrisVisible, + dependentResourceIrisNotVisible = dependentResourceIrisNotVisible, + incomingLinksForResource = incomingLinksForResource, + alreadyTraversed = alreadyTraversed + resourceIri + ) ) - ) - linkValue.copy( - nestedResource = source, - isIncomingLink = true - ) + linkValue.copy( + nestedResource = source, + isIncomingLink = true + ) } resource.copy( @@ -838,130 +826,128 @@ object ConstructResponseUtilV2 { } /** - * Transforms a resource's values by nesting dependent resources in link values. - * - * @param resourceIri the IRI of the resource. - * @param values the values of the resource. - * @param flatResourcesWithValues the complete set of resources with their values, before permission filtering. - * @param visibleResources the resources that the user has permission to see. - * @param dependentResourceIrisVisible the IRIs of dependent resources that the user has permission to see. - * @param dependentResourceIrisNotVisible the IRIs of dependent resources that the user does not have permission to see. - * @param incomingLinksForResource a map of resource IRIs to resources that link to each resource. - * @param alreadyTraversed a set (initially empty) of the IRIs of resources that this function has already - * traversed, to prevent an infinite loop if a cycle is encountered. - * @return the transformed values. - */ + * Transforms a resource's values by nesting dependent resources in link values. + * + * @param resourceIri the IRI of the resource. + * @param values the values of the resource. + * @param flatResourcesWithValues the complete set of resources with their values, before permission filtering. + * @param visibleResources the resources that the user has permission to see. + * @param dependentResourceIrisVisible the IRIs of dependent resources that the user has permission to see. + * @param dependentResourceIrisNotVisible the IRIs of dependent resources that the user does not have permission to see. + * @param incomingLinksForResource a map of resource IRIs to resources that link to each resource. + * @param alreadyTraversed a set (initially empty) of the IRIs of resources that this function has already + * traversed, to prevent an infinite loop if a cycle is encountered. + * @return the transformed values. + */ private def transformValuesByNestingResources( - resourceIri: IRI, - values: Seq[ValueRdfData], - flatResourcesWithValues: RdfResources, - visibleResources: RdfResources, - dependentResourceIrisVisible: Set[IRI], - dependentResourceIrisNotVisible: Set[IRI], - incomingLinksForResource: Map[IRI, RdfResources], - alreadyTraversed: Set[IRI])(implicit stringFormatter: StringFormatter): Seq[ValueRdfData] = { - values.foldLeft(Vector.empty[ValueRdfData]) { - case (acc: Vector[ValueRdfData], value: ValueRdfData) => - if (value.valueObjectClass.toString == OntologyConstants.KnoraBase.LinkValue) { - val dependentResourceIri: IRI = value.requireIriObject(OntologyConstants.Rdf.Object.toSmartIri) - - if (alreadyTraversed(dependentResourceIri)) { - acc :+ value - } else { - // Do we have the dependent resource? - if (dependentResourceIrisVisible.contains(dependentResourceIri)) { - // Yes. Nest it in the link value. - val dependentResource: ResourceWithValueRdfData = nestResources( - resourceIri = dependentResourceIri, - flatResourcesWithValues = flatResourcesWithValues, - visibleResources = visibleResources, - dependentResourceIrisVisible = dependentResourceIrisVisible, - dependentResourceIrisNotVisible = dependentResourceIrisNotVisible, - incomingLinksForResource = incomingLinksForResource, - alreadyTraversed = alreadyTraversed - ) + resourceIri: IRI, + values: Seq[ValueRdfData], + flatResourcesWithValues: RdfResources, + visibleResources: RdfResources, + dependentResourceIrisVisible: Set[IRI], + dependentResourceIrisNotVisible: Set[IRI], + incomingLinksForResource: Map[IRI, RdfResources], + alreadyTraversed: Set[IRI] + )(implicit stringFormatter: StringFormatter): Seq[ValueRdfData] = + values.foldLeft(Vector.empty[ValueRdfData]) { case (acc: Vector[ValueRdfData], value: ValueRdfData) => + if (value.valueObjectClass.toString == OntologyConstants.KnoraBase.LinkValue) { + val dependentResourceIri: IRI = value.requireIriObject(OntologyConstants.Rdf.Object.toSmartIri) + + if (alreadyTraversed(dependentResourceIri)) { + acc :+ value + } else { + // Do we have the dependent resource? + if (dependentResourceIrisVisible.contains(dependentResourceIri)) { + // Yes. Nest it in the link value. + val dependentResource: ResourceWithValueRdfData = nestResources( + resourceIri = dependentResourceIri, + flatResourcesWithValues = flatResourcesWithValues, + visibleResources = visibleResources, + dependentResourceIrisVisible = dependentResourceIrisVisible, + dependentResourceIrisNotVisible = dependentResourceIrisNotVisible, + incomingLinksForResource = incomingLinksForResource, + alreadyTraversed = alreadyTraversed + ) - acc :+ value.copy( - nestedResource = Some(dependentResource) - ) - } else if (dependentResourceIrisNotVisible.contains(dependentResourceIri)) { - // No, because the user doesn't have permission to see it. Skip the link value. - acc - } else { - // We don't have the dependent resource because it is marked as deleted. Just - // return the link value without a nested resource. - acc :+ value - } + acc :+ value.copy( + nestedResource = Some(dependentResource) + ) + } else if (dependentResourceIrisNotVisible.contains(dependentResourceIri)) { + // No, because the user doesn't have permission to see it. Skip the link value. + acc + } else { + // We don't have the dependent resource because it is marked as deleted. Just + // return the link value without a nested resource. + acc :+ value } - } else { - acc :+ value } + } else { + acc :+ value + } } - } /** - * Collect all mapping Iris referred to in the given value assertions. - * - * @param valuePropertyAssertions the given assertions (property -> value object). - * @return a set of mapping Iris. - */ - def getMappingIrisFromValuePropertyAssertions(valuePropertyAssertions: RdfPropertyValues)( - implicit stringFormatter: StringFormatter): Set[IRI] = { + * Collect all mapping Iris referred to in the given value assertions. + * + * @param valuePropertyAssertions the given assertions (property -> value object). + * @return a set of mapping Iris. + */ + def getMappingIrisFromValuePropertyAssertions( + valuePropertyAssertions: RdfPropertyValues + )(implicit stringFormatter: StringFormatter): Set[IRI] = valuePropertyAssertions.foldLeft(Set.empty[IRI]) { case (acc: Set[IRI], (_: SmartIri, valObjs: Seq[ValueRdfData])) => - val mappings: Seq[String] = valObjs - .filter { valObj: ValueRdfData => - valObj.valueObjectClass == OntologyConstants.KnoraBase.TextValue.toSmartIri && valObj.assertions.contains( - OntologyConstants.KnoraBase.ValueHasMapping.toSmartIri) - } - .map { textValObj: ValueRdfData => - textValObj.requireIriObject(OntologyConstants.KnoraBase.ValueHasMapping.toSmartIri) - } + val mappings: Seq[String] = valObjs.filter { valObj: ValueRdfData => + valObj.valueObjectClass == OntologyConstants.KnoraBase.TextValue.toSmartIri && valObj.assertions.contains( + OntologyConstants.KnoraBase.ValueHasMapping.toSmartIri + ) + }.map { textValObj: ValueRdfData => + textValObj.requireIriObject(OntologyConstants.KnoraBase.ValueHasMapping.toSmartIri) + } // get mappings from linked resources - val mappingsFromReferredResources: Set[IRI] = valObjs - .filter { valObj: ValueRdfData => - valObj.nestedResource.nonEmpty - } - .flatMap { valObj: ValueRdfData => - val referredRes: ResourceWithValueRdfData = valObj.nestedResource.get + val mappingsFromReferredResources: Set[IRI] = valObjs.filter { valObj: ValueRdfData => + valObj.nestedResource.nonEmpty + }.flatMap { valObj: ValueRdfData => + val referredRes: ResourceWithValueRdfData = valObj.nestedResource.get - // recurse on the nested resource's values - getMappingIrisFromValuePropertyAssertions(referredRes.valuePropertyAssertions) - } - .toSet + // recurse on the nested resource's values + getMappingIrisFromValuePropertyAssertions(referredRes.valuePropertyAssertions) + }.toSet acc ++ mappings ++ mappingsFromReferredResources } - } /** - * Given a [[ValueRdfData]], constructs a [[TextValueContentV2]]. This method is used to process a text value - * as returned in an API response, as well as to process a page of standoff markup that is being queried - * separately from its text value. - * - * @param valueObject the given [[ValueRdfData]]. - * @param valueObjectValueHasString the value's `knora-base:valueHasString`. - * @param valueCommentOption the value's comment, if any. - * @param mappings the mappings needed for standoff conversions and XSL transformations. - * @param queryStandoff if `true`, make separate queries to get the standoff for the text value. - * @param responderManager the Knora responder manager. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[TextValueContentV2]]. - */ - private def makeTextValueContentV2(resourceIri: IRI, - valueObject: ValueRdfData, - valueObjectValueHasString: Option[String], - valueCommentOption: Option[String], - mappings: Map[IRI, MappingAndXSLTransformation], - queryStandoff: Boolean, - responderManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM)( - implicit stringFormatter: StringFormatter, - timeout: Timeout, - executionContext: ExecutionContext): Future[TextValueContentV2] = { + * Given a [[ValueRdfData]], constructs a [[TextValueContentV2]]. This method is used to process a text value + * as returned in an API response, as well as to process a page of standoff markup that is being queried + * separately from its text value. + * + * @param valueObject the given [[ValueRdfData]]. + * @param valueObjectValueHasString the value's `knora-base:valueHasString`. + * @param valueCommentOption the value's comment, if any. + * @param mappings the mappings needed for standoff conversions and XSL transformations. + * @param queryStandoff if `true`, make separate queries to get the standoff for the text value. + * @param responderManager the Knora responder manager. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[TextValueContentV2]]. + */ + private def makeTextValueContentV2( + resourceIri: IRI, + valueObject: ValueRdfData, + valueObjectValueHasString: Option[String], + valueCommentOption: Option[String], + mappings: Map[IRI, MappingAndXSLTransformation], + queryStandoff: Boolean, + responderManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + )(implicit + stringFormatter: StringFormatter, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[TextValueContentV2] = { // Any knora-base:TextValue may have a language val valueLanguageOption: Option[String] = valueObject.maybeStringObject(OntologyConstants.KnoraBase.ValueHasLanguage.toSmartIri) @@ -983,39 +969,40 @@ object ConstructResponseUtilV2 { ) valueHasMaxStandoffStartIndex: Int = valueObject.requireIntObject( - OntologyConstants.KnoraBase.ValueHasMaxStandoffStartIndex.toSmartIri) + OntologyConstants.KnoraBase.ValueHasMaxStandoffStartIndex.toSmartIri + ) lastStartIndexQueried = standoff.last.startIndex // Should we get more the rest of the standoff for the same text value? - standoffToReturn <- if (queryStandoff && lastStartIndexQueried < valueHasMaxStandoffStartIndex) { - // We're supposed to get all the standoff for the text value. Ask the standoff responder for the rest of it. - // Each page of markup will be also be processed by this method. The resulting pages will be - // concatenated together and returned in a GetStandoffResponseV2. - - for { - standoffResponse <- (responderManager ? GetRemainingStandoffFromTextValueRequestV2( - resourceIri = resourceIri, - valueIri = valueObject.subjectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - )).mapTo[GetStandoffResponseV2] - } yield standoff ++ standoffResponse.standoff - } else { - // We're not supposed to get any more standoff here, either because we have all of it already, - // or because we're just supposed to return one page. - FastFuture.successful(standoff) - } - } yield - TextValueContentV2( - ontologySchema = InternalSchema, - maybeValueHasString = valueObjectValueHasString, - valueHasLanguage = valueLanguageOption, - standoff = standoffToReturn, - mappingIri = mappingIri, - mapping = mappingAndXsltTransformation.map(_.mapping), - xslt = mappingAndXsltTransformation.flatMap(_.XSLTransformation), - comment = valueCommentOption - ) + standoffToReturn <- + if (queryStandoff && lastStartIndexQueried < valueHasMaxStandoffStartIndex) { + // We're supposed to get all the standoff for the text value. Ask the standoff responder for the rest of it. + // Each page of markup will be also be processed by this method. The resulting pages will be + // concatenated together and returned in a GetStandoffResponseV2. + + for { + standoffResponse <- (responderManager ? GetRemainingStandoffFromTextValueRequestV2( + resourceIri = resourceIri, + valueIri = valueObject.subjectIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + )).mapTo[GetStandoffResponseV2] + } yield standoff ++ standoffResponse.standoff + } else { + // We're not supposed to get any more standoff here, either because we have all of it already, + // or because we're just supposed to return one page. + FastFuture.successful(standoff) + } + } yield TextValueContentV2( + ontologySchema = InternalSchema, + maybeValueHasString = valueObjectValueHasString, + valueHasLanguage = valueLanguageOption, + standoff = standoffToReturn, + mappingIri = mappingIri, + mapping = mappingAndXsltTransformation.map(_.mapping), + xslt = mappingAndXsltTransformation.flatMap(_.XSLTransformation), + comment = valueCommentOption + ) } else { // The query returned no standoff markup. @@ -1025,32 +1012,36 @@ object ConstructResponseUtilV2 { maybeValueHasString = valueObjectValueHasString, valueHasLanguage = valueLanguageOption, comment = valueCommentOption - )) + ) + ) } } /** - * Given a [[ValueRdfData]], constructs a [[FileValueContentV2]]. - * - * @param valueType the IRI of the file value type - * @param valueObject the given [[ValueRdfData]]. - * @param valueObjectValueHasString the value's `knora-base:valueHasString`. - * @param valueCommentOption the value's comment, if any. - * @param mappings the mappings needed for standoff conversions and XSL transformations. - * @param responderManager the Knora responder manager. - * @param requestingUser the user making the request. - * @return a [[FileValueContentV2]]. - */ - private def makeFileValueContentV2(valueType: IRI, - valueObject: ValueRdfData, - valueObjectValueHasString: String, - valueCommentOption: Option[String], - mappings: Map[IRI, MappingAndXSLTransformation], - responderManager: ActorRef, - requestingUser: UserADM)( - implicit stringFormatter: StringFormatter, - timeout: Timeout, - executionContext: ExecutionContext): Future[FileValueContentV2] = { + * Given a [[ValueRdfData]], constructs a [[FileValueContentV2]]. + * + * @param valueType the IRI of the file value type + * @param valueObject the given [[ValueRdfData]]. + * @param valueObjectValueHasString the value's `knora-base:valueHasString`. + * @param valueCommentOption the value's comment, if any. + * @param mappings the mappings needed for standoff conversions and XSL transformations. + * @param responderManager the Knora responder manager. + * @param requestingUser the user making the request. + * @return a [[FileValueContentV2]]. + */ + private def makeFileValueContentV2( + valueType: IRI, + valueObject: ValueRdfData, + valueObjectValueHasString: String, + valueCommentOption: Option[String], + mappings: Map[IRI, MappingAndXSLTransformation], + responderManager: ActorRef, + requestingUser: UserADM + )(implicit + stringFormatter: StringFormatter, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[FileValueContentV2] = { val fileValue = FileValueV2( internalMimeType = valueObject.requireStringObject(OntologyConstants.KnoraBase.InternalMimeType.toSmartIri), internalFilename = valueObject.requireStringObject(OntologyConstants.KnoraBase.InternalFilename.toSmartIri), @@ -1067,7 +1058,8 @@ object ConstructResponseUtilV2 { dimX = valueObject.requireIntObject(OntologyConstants.KnoraBase.DimX.toSmartIri), dimY = valueObject.requireIntObject(OntologyConstants.KnoraBase.DimY.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.DocumentFileValue => FastFuture.successful( @@ -1078,7 +1070,8 @@ object ConstructResponseUtilV2 { dimX = valueObject.maybeIntObject(OntologyConstants.KnoraBase.DimX.toSmartIri), dimY = valueObject.maybeIntObject(OntologyConstants.KnoraBase.DimY.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.TextFileValue => FastFuture.successful( @@ -1086,7 +1079,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, fileValue = fileValue, comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.AudioFileValue => FastFuture.successful( @@ -1097,7 +1091,8 @@ object ConstructResponseUtilV2 { .maybeStringObject(OntologyConstants.KnoraBase.Duration.toSmartIri) .map(definedDuration => BigDecimal(definedDuration)), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.MovingImageFileValue => FastFuture.successful( MovingImageFileValueContentV2( @@ -1120,35 +1115,38 @@ object ConstructResponseUtilV2 { } /** - * Given a [[ValueRdfData]], constructs a [[LinkValueContentV2]]. - * - * @param valueObject the given [[ValueRdfData]]. - * @param valueObjectValueHasString the value's `knora-base:valueHasString`. - * @param valueCommentOption the value's comment, if any. - * @param mappings the mappings needed for standoff conversions and XSL transformations. - * @param queryStandoff if `true`, make separate queries to get the standoff for text values. - * @param versionDate if defined, represents the requested time in the the resources' version history. - * @param responderManager the Knora responder manager. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application's settings. - * @param requestingUser the user making the request. - * @return a [[LinkValueContentV2]]. - */ - private def makeLinkValueContentV2(valueObject: ValueRdfData, - valueObjectValueHasString: String, - valueCommentOption: Option[String], - mappings: Map[IRI, MappingAndXSLTransformation], - queryStandoff: Boolean, - versionDate: Option[Instant], - responderManager: ActorRef, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - requestingUser: UserADM)( - implicit stringFormatter: StringFormatter, - timeout: Timeout, - executionContext: ExecutionContext): Future[LinkValueContentV2] = { + * Given a [[ValueRdfData]], constructs a [[LinkValueContentV2]]. + * + * @param valueObject the given [[ValueRdfData]]. + * @param valueObjectValueHasString the value's `knora-base:valueHasString`. + * @param valueCommentOption the value's comment, if any. + * @param mappings the mappings needed for standoff conversions and XSL transformations. + * @param queryStandoff if `true`, make separate queries to get the standoff for text values. + * @param versionDate if defined, represents the requested time in the the resources' version history. + * @param responderManager the Knora responder manager. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application's settings. + * @param requestingUser the user making the request. + * @return a [[LinkValueContentV2]]. + */ + private def makeLinkValueContentV2( + valueObject: ValueRdfData, + valueObjectValueHasString: String, + valueCommentOption: Option[String], + mappings: Map[IRI, MappingAndXSLTransformation], + queryStandoff: Boolean, + versionDate: Option[Instant], + responderManager: ActorRef, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + requestingUser: UserADM + )(implicit + stringFormatter: StringFormatter, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[LinkValueContentV2] = { val referredResourceIri: IRI = if (valueObject.isIncomingLink) { valueObject.requireIriObject(OntologyConstants.Rdf.Subject.toSmartIri) } else { @@ -1180,10 +1178,9 @@ object ConstructResponseUtilV2 { featureFactoryConfig = featureFactoryConfig, settings = settings ) - } yield - linkValue.copy( - nestedResource = Some(nestedResource) - ) + } yield linkValue.copy( + nestedResource = Some(nestedResource) + ) case None => // There is no nested resource. @@ -1192,32 +1189,35 @@ object ConstructResponseUtilV2 { } /** - * Given a [[ValueRdfData]], constructs a [[ValueContentV2]], considering the specific type of the given [[ValueRdfData]]. - * - * @param valueObject the given [[ValueRdfData]]. - * @param mappings the mappings needed for standoff conversions and XSL transformations. - * @param queryStandoff if `true`, make separate queries to get the standoff for text values. - * @param versionDate if defined, represents the requested time in the the resources' version history. - * @param responderManager the Knora responder manager. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application's settings. - * @param requestingUser the user making the request. - * @return a [[ValueContentV2]] representing a value. - */ - private def createValueContentV2FromValueRdfData(resourceIri: IRI, - valueObject: ValueRdfData, - mappings: Map[IRI, MappingAndXSLTransformation], - queryStandoff: Boolean, - versionDate: Option[Instant] = None, - responderManager: ActorRef, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - requestingUser: UserADM)( - implicit stringFormatter: StringFormatter, - timeout: Timeout, - executionContext: ExecutionContext): Future[ValueContentV2] = { + * Given a [[ValueRdfData]], constructs a [[ValueContentV2]], considering the specific type of the given [[ValueRdfData]]. + * + * @param valueObject the given [[ValueRdfData]]. + * @param mappings the mappings needed for standoff conversions and XSL transformations. + * @param queryStandoff if `true`, make separate queries to get the standoff for text values. + * @param versionDate if defined, represents the requested time in the the resources' version history. + * @param responderManager the Knora responder manager. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application's settings. + * @param requestingUser the user making the request. + * @return a [[ValueContentV2]] representing a value. + */ + private def createValueContentV2FromValueRdfData( + resourceIri: IRI, + valueObject: ValueRdfData, + mappings: Map[IRI, MappingAndXSLTransformation], + queryStandoff: Boolean, + versionDate: Option[Instant] = None, + responderManager: ActorRef, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + requestingUser: UserADM + )(implicit + stringFormatter: StringFormatter, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ValueContentV2] = { // every knora-base:Value (any of its subclasses) has a string representation, but it is not necessarily returned with text values. val valueObjectValueHasString: Option[String] = valueObject.maybeStringObject(OntologyConstants.KnoraBase.ValueHasString.toSmartIri) @@ -1256,15 +1256,19 @@ object ConstructResponseUtilV2 { valueHasEndJDN = valueObject.requireIntObject(OntologyConstants.KnoraBase.ValueHasEndJDN.toSmartIri), valueHasStartPrecision = DatePrecisionV2.parse( startPrecisionStr, - throw InconsistentRepositoryDataException(s"Invalid date precision: $startPrecisionStr")), + throw InconsistentRepositoryDataException(s"Invalid date precision: $startPrecisionStr") + ), valueHasEndPrecision = DatePrecisionV2.parse( endPrecisionStr, - throw InconsistentRepositoryDataException(s"Invalid date precision: $endPrecisionStr")), + throw InconsistentRepositoryDataException(s"Invalid date precision: $endPrecisionStr") + ), valueHasCalendar = CalendarNameV2.parse( calendarNameStr, - throw InconsistentRepositoryDataException(s"Invalid calendar name: $calendarNameStr")), + throw InconsistentRepositoryDataException(s"Invalid calendar name: $calendarNameStr") + ), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.IntValue => FastFuture.successful( @@ -1272,7 +1276,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, valueHasInteger = valueObject.requireIntObject(OntologyConstants.KnoraBase.ValueHasInteger.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.DecimalValue => FastFuture.successful( @@ -1280,7 +1285,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, valueHasDecimal = valueObject.requireDecimalObject(OntologyConstants.KnoraBase.ValueHasDecimal.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.BooleanValue => FastFuture.successful( @@ -1288,7 +1294,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, valueHasBoolean = valueObject.requireBooleanObject(OntologyConstants.KnoraBase.ValueHasBoolean.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.UriValue => FastFuture.successful( @@ -1296,7 +1303,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, valueHasUri = valueObject.requireIriObject(OntologyConstants.KnoraBase.ValueHasUri.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.ColorValue => FastFuture.successful( @@ -1304,7 +1312,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, valueHasColor = valueObject.requireStringObject(OntologyConstants.KnoraBase.ValueHasColor.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.GeomValue => FastFuture.successful( @@ -1312,7 +1321,8 @@ object ConstructResponseUtilV2 { ontologySchema = InternalSchema, valueHasGeometry = valueObject.requireStringObject(OntologyConstants.KnoraBase.ValueHasGeometry.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.GeonameValue => FastFuture.successful( @@ -1321,7 +1331,8 @@ object ConstructResponseUtilV2 { valueHasGeonameCode = valueObject.requireStringObject(OntologyConstants.KnoraBase.ValueHasGeonameCode.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.ListValue => val listNodeIri: IRI = valueObject.requireIriObject(OntologyConstants.KnoraBase.ValueHasListNode.toSmartIri) @@ -1344,11 +1355,10 @@ object ConstructResponseUtilV2 { featureFactoryConfig = featureFactoryConfig, requestingUser = requestingUser )).mapTo[NodeGetResponseV2] - } yield - listNode.copy( - listNodeLabel = nodeResponse.node.getLabelInPreferredLanguage(userLang = requestingUser.lang, - fallbackLang = settings.fallbackLanguage) - ) + } yield listNode.copy( + listNodeLabel = nodeResponse.node + .getLabelInPreferredLanguage(userLang = requestingUser.lang, fallbackLang = settings.fallbackLanguage) + ) case ApiV2Complex => FastFuture.successful(listNode) } @@ -1362,7 +1372,8 @@ object ConstructResponseUtilV2 { valueHasIntervalEnd = valueObject.requireDecimalObject(OntologyConstants.KnoraBase.ValueHasIntervalEnd.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.TimeValue => FastFuture.successful( @@ -1371,13 +1382,15 @@ object ConstructResponseUtilV2 { valueHasTimeStamp = valueObject.requireDateTimeObject(OntologyConstants.KnoraBase.ValueHasTimeStamp.toSmartIri), comment = valueCommentOption - )) + ) + ) case OntologyConstants.KnoraBase.LinkValue => makeLinkValueContentV2( valueObject = valueObject, valueObjectValueHasString = valueObjectValueHasString.getOrElse( - throw AssertionException(s"Value <${valueObject.subjectIri}> has no knora-base:valueHasString")), + throw AssertionException(s"Value <${valueObject.subjectIri}> has no knora-base:valueHasString") + ), valueCommentOption = valueCommentOption, mappings = mappings, queryStandoff = queryStandoff, @@ -1394,7 +1407,8 @@ object ConstructResponseUtilV2 { valueType = fileValueClass, valueObject = valueObject, valueObjectValueHasString = valueObjectValueHasString.getOrElse( - throw AssertionException(s"Value <${valueObject.subjectIri}> has no knora-base:valueHasString")), + throw AssertionException(s"Value <${valueObject.subjectIri}> has no knora-base:valueHasString") + ), valueCommentOption = valueCommentOption, mappings = mappings, responderManager = responderManager, @@ -1406,34 +1420,36 @@ object ConstructResponseUtilV2 { } /** - * - * Creates a [[ReadResourceV2]] from a [[ResourceWithValueRdfData]]. - * - * @param resourceIri the IRI of the resource. - * @param resourceWithValueRdfData the Rdf data belonging to the resource. - * @param mappings the mappings needed for standoff conversions and XSL transformations. - * @param queryStandoff if `true`, make separate queries to get the standoff for text values. - * @param versionDate if defined, represents the requested time in the the resources' version history. - * @param responderManager the Knora responder manager. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application's settings. - * @param requestingUser the user making the request. - * @return a [[ReadResourceV2]]. - */ - private def constructReadResourceV2(resourceIri: IRI, - resourceWithValueRdfData: ResourceWithValueRdfData, - mappings: Map[IRI, MappingAndXSLTransformation], - queryStandoff: Boolean, - versionDate: Option[Instant], - responderManager: ActorRef, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - requestingUser: UserADM)( - implicit stringFormatter: StringFormatter, - timeout: Timeout, - executionContext: ExecutionContext): Future[ReadResourceV2] = { + * Creates a [[ReadResourceV2]] from a [[ResourceWithValueRdfData]]. + * + * @param resourceIri the IRI of the resource. + * @param resourceWithValueRdfData the Rdf data belonging to the resource. + * @param mappings the mappings needed for standoff conversions and XSL transformations. + * @param queryStandoff if `true`, make separate queries to get the standoff for text values. + * @param versionDate if defined, represents the requested time in the the resources' version history. + * @param responderManager the Knora responder manager. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application's settings. + * @param requestingUser the user making the request. + * @return a [[ReadResourceV2]]. + */ + private def constructReadResourceV2( + resourceIri: IRI, + resourceWithValueRdfData: ResourceWithValueRdfData, + mappings: Map[IRI, MappingAndXSLTransformation], + queryStandoff: Boolean, + versionDate: Option[Instant], + responderManager: ActorRef, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + requestingUser: UserADM + )(implicit + stringFormatter: StringFormatter, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ReadResourceV2] = { def getDeletionInfo(rdfData: RdfData): Option[DeletionInfo] = { val mayHaveDeletedStatements: Option[Boolean] = rdfData.maybeBooleanObject(OntologyConstants.KnoraBase.IsDeleted.toSmartIri) @@ -1460,7 +1476,9 @@ object ConstructResponseUtilV2 { val resourceClassStr: IRI = resourceWithValueRdfData.requireIriObject(OntologyConstants.Rdf.Type.toSmartIri) val resourceClass = resourceClassStr.toSmartIriWithErr( throw InconsistentRepositoryDataException( - s"Couldn't parse rdf:type of resource <$resourceIri>: <$resourceClassStr>")) + s"Couldn't parse rdf:type of resource <$resourceIri>: <$resourceClassStr>" + ) + ) val resourceAttachedToUser: IRI = resourceWithValueRdfData.requireIriObject(OntologyConstants.KnoraBase.AttachedToUser.toSmartIri) val resourceAttachedToProject: IRI = @@ -1475,91 +1493,92 @@ object ConstructResponseUtilV2 { // get the resource's values val valueObjectFutures: Map[SmartIri, Seq[Future[ReadValueV2]]] = - resourceWithValueRdfData.valuePropertyAssertions.map { - case (property: SmartIri, valObjs: Seq[ValueRdfData]) => - val readValues: Seq[Future[ReadValueV2]] = valObjs - .sortBy(_.subjectIri) - .sortBy { // order values by value IRI, then by knora-base:valueHasOrder - valObj: ValueRdfData => - // set order to zero if not given - valObj.maybeIntObject(OntologyConstants.KnoraBase.ValueHasOrder.toSmartIri).getOrElse(0) - } - .map { valObj: ValueRdfData => - for { - valueContent: ValueContentV2 <- createValueContentV2FromValueRdfData( - resourceIri = resourceIri, - valueObject = valObj, - mappings = mappings, - queryStandoff = queryStandoff, - responderManager = responderManager, - requestingUser = requestingUser, - targetSchema = targetSchema, - featureFactoryConfig = featureFactoryConfig, - settings = settings + resourceWithValueRdfData.valuePropertyAssertions.map { case (property: SmartIri, valObjs: Seq[ValueRdfData]) => + val readValues: Seq[Future[ReadValueV2]] = valObjs + .sortBy(_.subjectIri) + .sortBy { // order values by value IRI, then by knora-base:valueHasOrder + valObj: ValueRdfData => + // set order to zero if not given + valObj.maybeIntObject(OntologyConstants.KnoraBase.ValueHasOrder.toSmartIri).getOrElse(0) + } + .map { valObj: ValueRdfData => + for { + valueContent: ValueContentV2 <- createValueContentV2FromValueRdfData( + resourceIri = resourceIri, + valueObject = valObj, + mappings = mappings, + queryStandoff = queryStandoff, + responderManager = responderManager, + requestingUser = requestingUser, + targetSchema = targetSchema, + featureFactoryConfig = featureFactoryConfig, + settings = settings + ) + + attachedToUser = valObj.requireIriObject(OntologyConstants.KnoraBase.AttachedToUser.toSmartIri) + permissions = valObj.requireStringObject(OntologyConstants.KnoraBase.HasPermissions.toSmartIri) + valueCreationDate: Instant = valObj.requireDateTimeObject( + OntologyConstants.KnoraBase.ValueCreationDate.toSmartIri + ) + valueDeletionInfo = getDeletionInfo(valObj) + valueHasUUID: UUID = stringFormatter.decodeUuid( + valObj.requireStringObject(OntologyConstants.KnoraBase.ValueHasUUID.toSmartIri) + ) + previousValueIri: Option[IRI] = valObj.maybeIriObject( + OntologyConstants.KnoraBase.PreviousValue.toSmartIri + ) + + } yield valueContent match { + case linkValueContentV2: LinkValueContentV2 => + val valueHasRefCount: Int = + valObj.requireIntObject(OntologyConstants.KnoraBase.ValueHasRefCount.toSmartIri) + + ReadLinkValueV2( + valueIri = valObj.subjectIri, + attachedToUser = attachedToUser, + permissions = permissions, + userPermission = valObj.userPermission, + valueCreationDate = valueCreationDate, + valueHasUUID = valueHasUUID, + valueContent = linkValueContentV2, + valueHasRefCount = valueHasRefCount, + previousValueIri = previousValueIri, + deletionInfo = valueDeletionInfo ) - attachedToUser = valObj.requireIriObject(OntologyConstants.KnoraBase.AttachedToUser.toSmartIri) - permissions = valObj.requireStringObject(OntologyConstants.KnoraBase.HasPermissions.toSmartIri) - valueCreationDate: Instant = valObj.requireDateTimeObject( - OntologyConstants.KnoraBase.ValueCreationDate.toSmartIri) - valueDeletionInfo = getDeletionInfo(valObj) - valueHasUUID: UUID = stringFormatter.decodeUuid( - valObj.requireStringObject(OntologyConstants.KnoraBase.ValueHasUUID.toSmartIri)) - previousValueIri: Option[IRI] = valObj.maybeIriObject( - OntologyConstants.KnoraBase.PreviousValue.toSmartIri) - - } yield - valueContent match { - case linkValueContentV2: LinkValueContentV2 => - val valueHasRefCount: Int = - valObj.requireIntObject(OntologyConstants.KnoraBase.ValueHasRefCount.toSmartIri) - - ReadLinkValueV2( - valueIri = valObj.subjectIri, - attachedToUser = attachedToUser, - permissions = permissions, - userPermission = valObj.userPermission, - valueCreationDate = valueCreationDate, - valueHasUUID = valueHasUUID, - valueContent = linkValueContentV2, - valueHasRefCount = valueHasRefCount, - previousValueIri = previousValueIri, - deletionInfo = valueDeletionInfo - ) - - case textValueContentV2: TextValueContentV2 => - val maybeValueHasMaxStandoffStartIndex: Option[Int] = - valObj.maybeIntObject(OntologyConstants.KnoraBase.ValueHasMaxStandoffStartIndex.toSmartIri) - - ReadTextValueV2( - valueIri = valObj.subjectIri, - attachedToUser = attachedToUser, - permissions = permissions, - userPermission = valObj.userPermission, - valueCreationDate = valueCreationDate, - valueHasUUID = valueHasUUID, - valueContent = textValueContentV2, - valueHasMaxStandoffStartIndex = maybeValueHasMaxStandoffStartIndex, - previousValueIri = previousValueIri, - deletionInfo = valueDeletionInfo - ) - - case otherValueContentV2: ValueContentV2 => - ReadOtherValueV2( - valueIri = valObj.subjectIri, - attachedToUser = attachedToUser, - permissions = permissions, - userPermission = valObj.userPermission, - valueCreationDate = valueCreationDate, - valueHasUUID = valueHasUUID, - valueContent = otherValueContentV2, - previousValueIri = previousValueIri, - deletionInfo = valueDeletionInfo - ) - } + case textValueContentV2: TextValueContentV2 => + val maybeValueHasMaxStandoffStartIndex: Option[Int] = + valObj.maybeIntObject(OntologyConstants.KnoraBase.ValueHasMaxStandoffStartIndex.toSmartIri) + + ReadTextValueV2( + valueIri = valObj.subjectIri, + attachedToUser = attachedToUser, + permissions = permissions, + userPermission = valObj.userPermission, + valueCreationDate = valueCreationDate, + valueHasUUID = valueHasUUID, + valueContent = textValueContentV2, + valueHasMaxStandoffStartIndex = maybeValueHasMaxStandoffStartIndex, + previousValueIri = previousValueIri, + deletionInfo = valueDeletionInfo + ) + + case otherValueContentV2: ValueContentV2 => + ReadOtherValueV2( + valueIri = valObj.subjectIri, + attachedToUser = attachedToUser, + permissions = permissions, + userPermission = valObj.userPermission, + valueCreationDate = valueCreationDate, + valueHasUUID = valueHasUUID, + valueContent = otherValueContentV2, + previousValueIri = previousValueIri, + deletionInfo = valueDeletionInfo + ) } + } - property -> readValues + property -> readValues } for { @@ -1570,57 +1589,59 @@ object ConstructResponseUtilV2 { )).mapTo[ProjectGetResponseADM] valueObjects <- ActorUtil.sequenceSeqFuturesInMap(valueObjectFutures) - } yield - ReadResourceV2( - resourceIri = resourceIri, - resourceClassIri = resourceClass, - label = resourceLabel, - attachedToUser = resourceAttachedToUser, - projectADM = projectResponse.project, - permissions = resourcePermissions, - userPermission = resourceWithValueRdfData.userPermission.get, - values = valueObjects, - creationDate = resourceCreationDate, - lastModificationDate = resourceLastModificationDate, - versionDate = versionDate, - deletionInfo = resourceDeletionInfo - ) + } yield ReadResourceV2( + resourceIri = resourceIri, + resourceClassIri = resourceClass, + label = resourceLabel, + attachedToUser = resourceAttachedToUser, + projectADM = projectResponse.project, + permissions = resourcePermissions, + userPermission = resourceWithValueRdfData.userPermission.get, + values = valueObjects, + creationDate = resourceCreationDate, + lastModificationDate = resourceLastModificationDate, + versionDate = versionDate, + deletionInfo = resourceDeletionInfo + ) } /** - * Creates an API response. - * - * @param mainResourcesAndValueRdfData the query results. - * @param orderByResourceIri the order in which the resources should be returned. This sequence - * contains the resource IRIs received from the triplestore before filtering - * for permissions, but after filtering for duplicates. - * @param pageSizeBeforeFiltering the number of resources returned before filtering for permissions and duplicates. - * @param mappings the mappings to convert standoff to XML, if any. - * @param queryStandoff if `true`, make separate queries to get the standoff for text values. - * @param calculateMayHaveMoreResults if `true`, calculate whether there may be more results for the query. - * @param versionDate if defined, represents the requested time in the the resources' version history. - * @param responderManager the Knora responder manager. - * @param targetSchema the schema of response. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application's settings. - * @param requestingUser the user making the request. - * @return a collection of [[ReadResourceV2]] representing the search results. - */ - def createApiResponse(mainResourcesAndValueRdfData: MainResourcesAndValueRdfData, - orderByResourceIri: Seq[IRI], - pageSizeBeforeFiltering: Int, - mappings: Map[IRI, MappingAndXSLTransformation] = Map.empty[IRI, MappingAndXSLTransformation], - queryStandoff: Boolean, - calculateMayHaveMoreResults: Boolean, - versionDate: Option[Instant], - responderManager: ActorRef, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - requestingUser: UserADM)( - implicit stringFormatter: StringFormatter, - timeout: Timeout, - executionContext: ExecutionContext): Future[ReadResourcesSequenceV2] = { + * Creates an API response. + * + * @param mainResourcesAndValueRdfData the query results. + * @param orderByResourceIri the order in which the resources should be returned. This sequence + * contains the resource IRIs received from the triplestore before filtering + * for permissions, but after filtering for duplicates. + * @param pageSizeBeforeFiltering the number of resources returned before filtering for permissions and duplicates. + * @param mappings the mappings to convert standoff to XML, if any. + * @param queryStandoff if `true`, make separate queries to get the standoff for text values. + * @param calculateMayHaveMoreResults if `true`, calculate whether there may be more results for the query. + * @param versionDate if defined, represents the requested time in the the resources' version history. + * @param responderManager the Knora responder manager. + * @param targetSchema the schema of response. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application's settings. + * @param requestingUser the user making the request. + * @return a collection of [[ReadResourceV2]] representing the search results. + */ + def createApiResponse( + mainResourcesAndValueRdfData: MainResourcesAndValueRdfData, + orderByResourceIri: Seq[IRI], + pageSizeBeforeFiltering: Int, + mappings: Map[IRI, MappingAndXSLTransformation] = Map.empty[IRI, MappingAndXSLTransformation], + queryStandoff: Boolean, + calculateMayHaveMoreResults: Boolean, + versionDate: Option[Instant], + responderManager: ActorRef, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + requestingUser: UserADM + )(implicit + stringFormatter: StringFormatter, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ReadResourcesSequenceV2] = { val visibleResourceIris: Seq[IRI] = orderByResourceIri.filter(resourceIri => mainResourcesAndValueRdfData.resources.keySet.contains(resourceIri)) @@ -1648,11 +1669,10 @@ object ConstructResponseUtilV2 { // If we got a full page of results from the triplestore (before filtering for permissions), there // might be at least one more page of results that the user could request. mayHaveMoreResults = calculateMayHaveMoreResults && pageSizeBeforeFiltering == settings.v2ResultsPerPage - } yield - ReadResourcesSequenceV2( - resources = resources, - hiddenResourceIris = mainResourcesAndValueRdfData.hiddenResourceIris, - mayHaveMoreResults = mayHaveMoreResults - ) + } yield ReadResourcesSequenceV2( + resources = resources, + hiddenResourceIris = mainResourcesAndValueRdfData.hiddenResourceIris, + mayHaveMoreResults = mayHaveMoreResults + ) } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/DateUtilV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/DateUtilV1.scala index c62ecd12e9..f6fb48169d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/DateUtilV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/DateUtilV1.scala @@ -32,25 +32,25 @@ import org.knora.webapi.messages.v1.responder.valuemessages.{ } /** - * Utility functions for converting dates. - */ + * Utility functions for converting dates. + */ object DateUtilV1 { /** - * Represents a date with a specified precision as a range of possible dates. - * - * @param start the earliest possible value for the date. - * @param end the latest possible value for the date. - * @param precision the precision that was used to calculate the date range. - */ + * Represents a date with a specified precision as a range of possible dates. + * + * @param start the earliest possible value for the date. + * @param end the latest possible value for the date. + * @param precision the precision that was used to calculate the date range. + */ case class DateRange(start: GregorianCalendar, end: GregorianCalendar, precision: KnoraPrecisionV1.Value) /** - * Converts a [[DateValueV1]] to a [[JulianDayNumberValueV1]]. - * - * @param dateValueV1 the [[DateValueV1]] to be converted. - * @return a [[JulianDayNumberValueV1]]. - */ + * Converts a [[DateValueV1]] to a [[JulianDayNumberValueV1]]. + * + * @param dateValueV1 the [[DateValueV1]] to be converted. + * @return a [[JulianDayNumberValueV1]]. + */ def dateValueV1ToJulianDayNumberValueV1(dateValueV1: DateValueV1): JulianDayNumberValueV1 = { // Get the start and end date ranges of the DateValueV1. @@ -69,18 +69,22 @@ object DateUtilV1 { } /** - * Converts a [[JulianDayNumberValueV1]] to a [[DateValueV1]]. - * - * @param julianDayNumberValueV1 the [[JulianDayNumberValueV1]] to be converted. - * @return a [[DateValueV1]]. - */ + * Converts a [[JulianDayNumberValueV1]] to a [[DateValueV1]]. + * + * @param julianDayNumberValueV1 the [[JulianDayNumberValueV1]] to be converted. + * @return a [[DateValueV1]]. + */ def julianDayNumberValueV1ToDateValueV1(julianDayNumberValueV1: JulianDayNumberValueV1): DateValueV1 = { - val dateval1 = julianDayNumber2DateString(julianDayNumberValueV1.dateval1, - julianDayNumberValueV1.calendar, - julianDayNumberValueV1.dateprecision1) - val dateval2 = julianDayNumber2DateString(julianDayNumberValueV1.dateval2, - julianDayNumberValueV1.calendar, - julianDayNumberValueV1.dateprecision2) + val dateval1 = julianDayNumber2DateString( + julianDayNumberValueV1.dateval1, + julianDayNumberValueV1.calendar, + julianDayNumberValueV1.dateprecision1 + ) + val dateval2 = julianDayNumber2DateString( + julianDayNumberValueV1.dateval2, + julianDayNumberValueV1.calendar, + julianDayNumberValueV1.dateprecision2 + ) val dateEra1 = dateval1.split(StringFormatter.EraSeparator) val dateEra2 = dateval2.split(StringFormatter.EraSeparator) @@ -97,21 +101,21 @@ object DateUtilV1 { } /** - * Converts a date string to a date interval and a precision depending on its precision: - * - * Possible date string formats: - * - * YYYY: (YYYY-01-01, YYYY-12-31) year precision - * YYYY-MM: (YYYY-MM-01, YYYY-MM-LAST-DAY-OF-MONTH) month precision - * YYYY-MM-DD: (YYYY-MM-DD, YYYY-MM-DD) day precision - * - * A date string can optionally end with a space and an era, which can be AD, BC, CE, or BCE. If no - * era is given, AD/CE is assumed. - * - * @param dateString A string representation of the given date conforming to the expected format. - * @param calendarType a [[KnoraCalendarV1.Value]] specifying the calendar. - * @return A tuple containing two calendar dates (interval) and a precision. - */ + * Converts a date string to a date interval and a precision depending on its precision: + * + * Possible date string formats: + * + * YYYY: (YYYY-01-01, YYYY-12-31) year precision + * YYYY-MM: (YYYY-MM-01, YYYY-MM-LAST-DAY-OF-MONTH) month precision + * YYYY-MM-DD: (YYYY-MM-DD, YYYY-MM-DD) day precision + * + * A date string can optionally end with a space and an era, which can be AD, BC, CE, or BCE. If no + * era is given, AD/CE is assumed. + * + * @param dateString A string representation of the given date conforming to the expected format. + * @param calendarType a [[KnoraCalendarV1.Value]] specifying the calendar. + * @return A tuple containing two calendar dates (interval) and a precision. + */ def dateString2DateRange(dateString: String, calendarType: KnoraCalendarV1.Value): DateRange = { val changeDate: Date = getGregorianCalendarChangeDate(calendarType) @@ -157,14 +161,28 @@ object DateUtilV1 { intervalStart.set(Calendar.ERA, era) intervalStart.setLenient(false) // set leniency to false in order to check for invalid dates intervalStart.setGregorianChange(changeDate) - intervalStart.set(dateSegments(0).toInt, 0, 1, 12, 0, 0) // January 1st of the given year. Attention: in java.util.Calendar, month count starts with 0 + intervalStart.set( + dateSegments(0).toInt, + 0, + 1, + 12, + 0, + 0 + ) // January 1st of the given year. Attention: in java.util.Calendar, month count starts with 0 intervalStart.get(0) // call method `get` in order to format the date; if it is invalid an exception is thrown val intervalEnd = new GregorianCalendar intervalEnd.set(Calendar.ERA, era) intervalEnd.setLenient(false) // set leniency to false in order to check for invalid dates intervalEnd.setGregorianChange(changeDate) - intervalEnd.set(dateSegments(0).toInt, 11, 31, 12, 0, 0) // December 31st of the given year. Attention: in java.util.Calendar, month count starts with 0 + intervalEnd.set( + dateSegments(0).toInt, + 11, + 31, + 12, + 0, + 0 + ) // December 31st of the given year. Attention: in java.util.Calendar, month count starts with 0 intervalEnd.get(0) // call method `get` in order to format the date; if it is invalid an exception is thrown DateRange(intervalStart, intervalEnd, KnoraPrecisionV1.YEAR) @@ -184,7 +202,14 @@ object DateUtilV1 { intervalStart.set(Calendar.ERA, era) intervalStart.setLenient(false) // set leniency to false in order to check for invalid dates intervalStart.setGregorianChange(changeDate) - intervalStart.set(dateSegments(0).toInt, dateSegments(1).toInt - 1, 1, 12, 0, 0) // Attention: in java.util.Calendar, month count starts with 0; first day of the given month in the given year + intervalStart.set( + dateSegments(0).toInt, + dateSegments(1).toInt - 1, + 1, + 12, + 0, + 0 + ) // Attention: in java.util.Calendar, month count starts with 0; first day of the given month in the given year intervalStart.get(0) // call method `get` in order to format the date; if it is invalid an exception is thrown val intervalEnd = new GregorianCalendar @@ -197,7 +222,8 @@ object DateUtilV1 { intervalStart.getActualMaximum(daysInMonth), 12, 0, - 0) // Attention: in java.util.Calendar, month count starts with 0; last day of the given month in the given year + 0 + ) // Attention: in java.util.Calendar, month count starts with 0; last day of the given month in the given year intervalEnd.get(0) // call method `get` in order to format the date; if it is invalid an exception is thrown DateRange(intervalStart, intervalEnd, KnoraPrecisionV1.MONTH) @@ -216,7 +242,11 @@ object DateUtilV1 { exactDate.set(Calendar.ERA, era) exactDate.setLenient(false) // set leniency to false in order to check for invalid dates exactDate.setGregorianChange(changeDate) - exactDate.set(dateSegments(0).toInt, dateSegments(1).toInt - 1, dateSegments(2).toInt) // Attention: in java.util.Calendar, month count starts with 0 + exactDate.set( + dateSegments(0).toInt, + dateSegments(1).toInt - 1, + dateSegments(2).toInt + ) // Attention: in java.util.Calendar, month count starts with 0 exactDate.get(0) // call method `get` in order to format the date; if it is invalid an exception is thrown DateRange(exactDate, exactDate, KnoraPrecisionV1.DAY) @@ -229,18 +259,19 @@ object DateUtilV1 { } case _ => - throw BadRequestException(s"Invalid date format: $dateString") // should never be fulfilled due to previous regex checking + throw BadRequestException( + s"Invalid date format: $dateString" + ) // should never be fulfilled due to previous regex checking } } /** - * Converts era property of java.calendar to a string format. - * - * @param era java.calendar era property. - * @return string format of era. - */ - def eraToString(era: Int): String = { - + * Converts era property of java.calendar to a string format. + * + * @param era java.calendar era property. + * @return string format of era. + */ + def eraToString(era: Int): String = era match { case 1 => StringFormatter.Era_CE @@ -248,19 +279,20 @@ object DateUtilV1 { case other => throw AssertionException(s"A valid era should be 0 or 1, but $other given") } - } /** - * Converts a Julian Day Number to a string in `YYYY[-MM[-DD] ]` format. - * - * @param julianDay a Julian Day Number. - * @param calendarType the type of calendar to be used. - * @param precision the desired precision of the resulting string. - * @return a string in `YYYY[-MM[-DD] ]` format. - */ - def julianDayNumber2DateString(julianDay: Int, - calendarType: KnoraCalendarV1.Value, - precision: KnoraPrecisionV1.Value): String = { + * Converts a Julian Day Number to a string in `YYYY[-MM[-DD] ]` format. + * + * @param julianDay a Julian Day Number. + * @param calendarType the type of calendar to be used. + * @param precision the desired precision of the resulting string. + * @return a string in `YYYY[-MM[-DD] ]` format. + */ + def julianDayNumber2DateString( + julianDay: Int, + calendarType: KnoraCalendarV1.Value, + precision: KnoraPrecisionV1.Value + ): String = { val gregorianCalendar = convertJulianDayNumberToJavaGregorianCalendar(julianDay, calendarType) val year = gregorianCalendar.get(Calendar.YEAR) val month = gregorianCalendar.get(Calendar.MONTH) + 1 @@ -284,11 +316,11 @@ object DateUtilV1 { } /** - * Converts a [[GregorianCalendar]] to a Julian Day Number. - * - * @param date a [[GregorianCalendar]]. - * @return a Julian Day Number. - */ + * Converts a [[GregorianCalendar]] to a Julian Day Number. + * + * @param date a [[GregorianCalendar]]. + * @return a Julian Day Number. + */ def convertDateToJulianDayNumber(date: GregorianCalendar): Int = { val conv = new JDateTime conv.loadFrom(date) @@ -296,14 +328,16 @@ object DateUtilV1 { } /** - * Converts a Julian Day Number to a [[GregorianCalendar]]. - * - * @param julianDay a Julian Day Number. - * @param calendarType the type of calendar to be used to configure the [[GregorianCalendar]]. - * @return a [[GregorianCalendar]]. - */ - def convertJulianDayNumberToJavaGregorianCalendar(julianDay: Int, - calendarType: KnoraCalendarV1.Value): GregorianCalendar = { + * Converts a Julian Day Number to a [[GregorianCalendar]]. + * + * @param julianDay a Julian Day Number. + * @param calendarType the type of calendar to be used to configure the [[GregorianCalendar]]. + * @return a [[GregorianCalendar]]. + */ + def convertJulianDayNumberToJavaGregorianCalendar( + julianDay: Int, + calendarType: KnoraCalendarV1.Value + ): GregorianCalendar = { val conv = new JDateTime(julianDay.toDouble) val gregorianCalendar = new GregorianCalendar @@ -314,7 +348,7 @@ object DateUtilV1 { gregorianCalendar } - private def getGregorianCalendarChangeDate(calendarType: KnoraCalendarV1.Value): Date = { + private def getGregorianCalendarChangeDate(calendarType: KnoraCalendarV1.Value): Date = // https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#setGregorianChange%28java.util.Date%29 calendarType match { case KnoraCalendarV1.JULIAN => new Date(java.lang.Long.MAX_VALUE) // for Julian: if calendar given in Julian cal @@ -322,14 +356,13 @@ object DateUtilV1 { new Date(java.lang.Long.MIN_VALUE) //for Gregorian: if calendar given in Gregorian cal case _ => throw BadRequestException(s"Invalid calendar name: $calendarType") } - } /** - * Creates a [[JulianDayNumberValueV1]] from a date String (e.g. "GREGORIAN:2015-12-03"). - * - * @param dateStr the date String to be processed. - * @return a [[JulianDayNumberValueV1]] representing the date. - */ + * Creates a [[JulianDayNumberValueV1]] from a date String (e.g. "GREGORIAN:2015-12-03"). + * + * @param dateStr the date String to be processed. + * @return a [[JulianDayNumberValueV1]] representing the date. + */ def createJDNValueV1FromDateString(dateStr: String): JulianDayNumberValueV1 = { val stringFormatter = StringFormatter.getGeneralInstance val datestring = stringFormatter.validateDate(dateStr, throw BadRequestException(s"Invalid date format: $dateStr")) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ErrorHandlingMap.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ErrorHandlingMap.scala index a2292c1472..3bb9012576 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ErrorHandlingMap.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ErrorHandlingMap.scala @@ -22,24 +22,25 @@ package org.knora.webapi.messages.util import org.knora.webapi.exceptions.InconsistentRepositoryDataException /** - * A [[Map]] that facilitates error-handling, by wrapping an ordinary [[Map]] and overriding the `default` - * method to provide custom behaviour (by default, throwing an [[InconsistentRepositoryDataException]]) if a required - * value is missing. - * - * @param toWrap the [[Map]] to wrap. - * @param errorTemplateFun a function that generates an appropriate error message if a required value is missing. The function's - * argument is the key that was not found. - * @param errorFun an optional function that is called if a required value is missing. The function's argument is the - * error message generated by `errorTemplateFun`. - * @tparam A the type of keys in the map. - * @tparam B the type of values in the map. - */ -class ErrorHandlingMap[A, B](toWrap: Map[A, B], - private val errorTemplateFun: A => String, - private val errorFun: String => B = { errorMessage: String => - throw InconsistentRepositoryDataException(errorMessage) - }) - extends Map[A, B] { + * A [[Map]] that facilitates error-handling, by wrapping an ordinary [[Map]] and overriding the `default` + * method to provide custom behaviour (by default, throwing an [[InconsistentRepositoryDataException]]) if a required + * value is missing. + * + * @param toWrap the [[Map]] to wrap. + * @param errorTemplateFun a function that generates an appropriate error message if a required value is missing. The function's + * argument is the key that was not found. + * @param errorFun an optional function that is called if a required value is missing. The function's argument is the + * error message generated by `errorTemplateFun`. + * @tparam A the type of keys in the map. + * @tparam B the type of values in the map. + */ +class ErrorHandlingMap[A, B]( + toWrap: Map[A, B], + private val errorTemplateFun: A => String, + private val errorFun: String => B = { errorMessage: String => + throw InconsistentRepositoryDataException(errorMessage) + } +) extends Map[A, B] { // As an optimization, if the Map we're supposed to wrap is another ErrorHandlingMap, wrap its underlying wrapped Map instead. private val wrapped: Map[A, B] = toWrap match { @@ -47,35 +48,29 @@ class ErrorHandlingMap[A, B](toWrap: Map[A, B], case otherMap => otherMap } - override def empty: ErrorHandlingMap[A, B] = { + override def empty: ErrorHandlingMap[A, B] = new ErrorHandlingMap(Map.empty[A, B], errorTemplateFun, errorFun) - } - override def get(key: A): Option[B] = { + override def get(key: A): Option[B] = wrapped.get(key) - } - override def iterator: Iterator[(A, B)] = { + override def iterator: Iterator[(A, B)] = wrapped.iterator - } - override def foreach[U](f: ((A, B)) => U): Unit = { + override def foreach[U](f: ((A, B)) => U): Unit = wrapped.foreach(f) - } - override def size: Int = { + override def size: Int = wrapped.size - } - override def ++[V1 >: B](xs: IterableOnce[(A, V1)]): ErrorHandlingMap[A, V1] = { + override def ++[V1 >: B](xs: IterableOnce[(A, V1)]): ErrorHandlingMap[A, V1] = new ErrorHandlingMap(wrapped ++ xs, errorTemplateFun, errorFun) - } /** - * Called when a key is not found. - * - * @param key the given key value for which a binding is missing. - */ + * Called when a key is not found. + * + * @param key the given key value for which a binding is missing. + */ override def default(key: A): B = errorFun(errorTemplateFun(key)) override def removed(key: A): Map[A, B] = new ErrorHandlingMap(wrapped - key, errorTemplateFun, errorFun) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/FakeTriplestore.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/FakeTriplestore.scala index 1aa8ac41bd..a454b79612 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/FakeTriplestore.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/FakeTriplestore.scala @@ -28,8 +28,8 @@ import org.knora.webapi.util.FileUtil import scala.jdk.CollectionConverters._ /** - * A fake triplestore for use in performance testing. This feature is activated in `application.conf`. - */ + * A fake triplestore for use in performance testing. This feature is activated in `application.conf`. + */ object FakeTriplestore { private var fakeTriplestoreDir: Option[Path] = None @@ -38,9 +38,8 @@ object FakeTriplestore { var data = Map.empty[String, String] - def init(dataDir: Path): Unit = { + def init(dataDir: Path): Unit = fakeTriplestoreDir = Some(dataDir) - } def clear(): Unit = { FileUtils.deleteDirectory(fakeTriplestoreDir.get.toFile) @@ -58,23 +57,28 @@ object FakeTriplestore { .newDirectoryStream(queryDir) .asScala .filter(_.getFileName.toString.endsWith(".rq")) - .head) + .head + ) val result = FileUtil.readTextFile( Files .newDirectoryStream(queryDir) .asScala .filter(_.getFileName.toString.endsWith(".json")) - .head) + .head + ) sparql -> result } .toMap - data = new ErrorHandlingMap(dataToWrap, { key: String => - s"No result has been stored in the fake triplestore for this query: $key" - }) + data = new ErrorHandlingMap( + dataToWrap, + { key: String => + s"No result has been stored in the fake triplestore for this query: $key" + } + ) } - def add(sparql: String, result: String, log: LoggingAdapter): Unit = { + def add(sparql: String, result: String, log: LoggingAdapter): Unit = this.synchronized { log.info("Collecting data for fake triplestore") val paddedQueryNum = f"$queryNum%04d" @@ -86,5 +90,4 @@ object FakeTriplestore { FileUtil.writeTextFile(resultFile, result) queryNum += 1 } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/KnoraSystemInstances.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/KnoraSystemInstances.scala index 0426901999..a5143c234b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/KnoraSystemInstances.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/KnoraSystemInstances.scala @@ -26,15 +26,15 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.admin.responder.usersmessages.UserADM /** - * This object represents built-in User and Project instances. - */ + * This object represents built-in User and Project instances. + */ object KnoraSystemInstances { object Users { /** - * Represents the anonymous user. - */ + * Represents the anonymous user. + */ val AnonymousUser = UserADM( id = OntologyConstants.KnoraAdmin.AnonymousUser, username = "anonymous", @@ -52,8 +52,8 @@ object KnoraSystemInstances { ) /** - * Represents the system user used internally. - */ + * Represents the system user used internally. + */ val SystemUser = UserADM( id = OntologyConstants.KnoraAdmin.SystemUser, username = "system", diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/MessageUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/MessageUtil.scala index 7f7d3df62d..fb3ac33d60 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/MessageUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/MessageUtil.scala @@ -29,28 +29,30 @@ import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality import scala.reflect.runtime.{universe => ru} /** - * Utility functions for working with Akka messages. - */ + * Utility functions for working with Akka messages. + */ object MessageUtil { // Set of case class field names to skip. private val fieldsToSkip = - Set("stringFormatter", - "base64Decoder", - "knoraIdUtil", - "standoffLinkTagTargetResourceIris", - "knoraSettings", - "featureFactoryConfig") + Set( + "stringFormatter", + "base64Decoder", + "knoraIdUtil", + "standoffLinkTagTargetResourceIris", + "knoraSettings", + "featureFactoryConfig" + ) /** - * Recursively converts a Scala object to Scala source code for constructing the object (with named parameters). This is useful - * for writing tests containing hard-coded Akka messages. It works with case classes, collections ([[Seq]], [[Set]], - * and [[Map]]), [[Option]], enumerations (as long as the enumeration value's `toString` representation is the same - * as its identifier), and primitive types. It doesn't work with classes defined inside methods. - * - * @param obj the object to convert. - * @return a string that can be pasted into Scala source code to construct the object. - */ + * Recursively converts a Scala object to Scala source code for constructing the object (with named parameters). This is useful + * for writing tests containing hard-coded Akka messages. It works with case classes, collections ([[Seq]], [[Set]], + * and [[Map]]), [[Option]], enumerations (as long as the enumeration value's `toString` representation is the same + * as its identifier), and primitive types. It doesn't work with classes defined inside methods. + * + * @param obj the object to convert. + * @return a string that can be pasted into Scala source code to construct the object. + */ def toSource(obj: Any): String = { def maybeMakeNewLine(elemCount: Int): String = if (elemCount > 1) "\n" else "" @@ -102,10 +104,9 @@ object MessageUtil { case map: Map[Any @unchecked, Any @unchecked] => val maybeNewLine = maybeMakeNewLine(map.size) - map - .map { - case (key, value) => toSource(key) + " -> " + toSource(value) - } + map.map { case (key, value) => + toSource(key) + " -> " + toSource(value) + } .mkString("Map(" + maybeNewLine, ", " + maybeNewLine, maybeNewLine + ")") // Handle case classes. @@ -125,10 +126,9 @@ object MessageUtil { } } - val members: Iterable[String] = fieldMap.map { - case (fieldName, fieldValue) => - val fieldValueString = toSource(fieldValue) - s"$fieldName = $fieldValueString" + val members: Iterable[String] = fieldMap.map { case (fieldName, fieldValue) => + val fieldValueString = toSource(fieldValue) + s"$fieldName = $fieldValueString" } val maybeNewLine = maybeMakeNewLine(members.size) @@ -149,11 +149,12 @@ object MessageUtil { val memberName = member.name.toString.trim if (!(memberName.contains("$") || memberName.endsWith("Format") || fieldsToSkip.contains(memberName))) { - val fieldMirror = try { - instanceMirror.reflectField(member.asTerm) - } catch { - case e: Exception => throw new Exception(s"Can't format member $memberName in class $objClassName", e) - } + val fieldMirror = + try { + instanceMirror.reflectField(member.asTerm) + } catch { + case e: Exception => throw new Exception(s"Can't format member $memberName in class $objClassName", e) + } val memberValue = fieldMirror.get val memberValueString = toSource(memberValue) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/OntologyUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/OntologyUtil.scala index 6dbfbbcf8f..b98e8bb99c 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/OntologyUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/OntologyUtil.scala @@ -23,27 +23,28 @@ import org.knora.webapi.exceptions.InconsistentRepositoryDataException import org.knora.webapi.messages.SmartIri /** - * Utility functions for working with ontology entities. - */ + * Utility functions for working with ontology entities. + */ object OntologyUtil { /** - * Recursively walks up an entity hierarchy read from the triplestore, collecting the IRIs of all base entities in - * an ordered sequence. - * - * @param iri the IRI of an entity. - * @param directRelations a map of entities to their direct base entities. - * @return all the base entities of the specified entity hierarchically ordered. - */ + * Recursively walks up an entity hierarchy read from the triplestore, collecting the IRIs of all base entities in + * an ordered sequence. + * + * @param iri the IRI of an entity. + * @param directRelations a map of entities to their direct base entities. + * @return all the base entities of the specified entity hierarchically ordered. + */ def getAllBaseDefs(iri: SmartIri, directRelations: Map[SmartIri, Set[SmartIri]]): Seq[SmartIri] = { - def getAllBaseDefsRec(initialIri: SmartIri, currentIri: SmartIri): Seq[SmartIri] = { + def getAllBaseDefsRec(initialIri: SmartIri, currentIri: SmartIri): Seq[SmartIri] = directRelations.get(currentIri) match { case Some(baseDefs) => val baseDefsSequence = baseDefs.toSeq baseDefsSequence ++ baseDefsSequence.flatMap { baseDef => if (baseDef == initialIri) { throw InconsistentRepositoryDataException( - s"Entity $initialIri has an inheritance cycle with entity $baseDef") + s"Entity $initialIri has an inheritance cycle with entity $baseDef" + ) } else { getAllBaseDefsRec(initialIri, baseDef) } @@ -51,7 +52,6 @@ object OntologyUtil { case None => Seq.empty[SmartIri] } - } getAllBaseDefsRec(initialIri = iri, currentIri = iri) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/PermissionUtilADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/PermissionUtilADM.scala index 3f99d9634f..79cdc39d3b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/PermissionUtilADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/PermissionUtilADM.scala @@ -39,21 +39,21 @@ import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} import scala.concurrent.{ExecutionContext, Future} /** - * A utility that responder actors use to determine a user's permissions on an RDF entity in the triplestore. - */ + * A utility that responder actors use to determine a user's permissions on an RDF entity in the triplestore. + */ object PermissionUtilADM extends LazyLogging { // TODO: unify EntityPermission with PermissionADM. /** - * Represents a permission granted to a group on an entity. The `toString` method of an `EntityPermission` - * returns one of the codes in [[OntologyConstants.KnoraBase.EntityPermissionAbbreviations]]. - */ + * Represents a permission granted to a group on an entity. The `toString` method of an `EntityPermission` + * returns one of the codes in [[OntologyConstants.KnoraBase.EntityPermissionAbbreviations]]. + */ sealed trait EntityPermission extends Ordered[EntityPermission] { /** - * Represents this [[EntityPermission]] as an integer, as required by Knora API v1. - */ + * Represents this [[EntityPermission]] as an integer, as required by Knora API v1. + */ def toInt: Int override def compare(that: EntityPermission): Int = this.toInt - that.toInt @@ -64,8 +64,8 @@ object PermissionUtilADM extends LazyLogging { } /** - * Represents restricted view permission on an entity. - */ + * Represents restricted view permission on an entity. + */ case object RestrictedViewPermission extends EntityPermission { override def toInt: Int = 1 @@ -73,14 +73,13 @@ object PermissionUtilADM extends LazyLogging { override val getName: String = "restricted view permission" - override def toPermissionADM(groupIri: IRI): PermissionADM = { + override def toPermissionADM(groupIri: IRI): PermissionADM = PermissionADM.restrictedViewPermission(groupIri) - } } /** - * Represents unrestricted view permission on an entity. - */ + * Represents unrestricted view permission on an entity. + */ case object ViewPermission extends EntityPermission { override def toInt: Int = 2 @@ -88,14 +87,13 @@ object PermissionUtilADM extends LazyLogging { override val getName: String = "view permission" - override def toPermissionADM(groupIri: IRI): PermissionADM = { + override def toPermissionADM(groupIri: IRI): PermissionADM = PermissionADM.viewPermission(groupIri) - } } /** - * Represents modify permission on an entity. - */ + * Represents modify permission on an entity. + */ case object ModifyPermission extends EntityPermission { override def toInt: Int = 6 @@ -103,14 +101,13 @@ object PermissionUtilADM extends LazyLogging { override val getName: String = "modify permission" - override def toPermissionADM(groupIri: IRI): PermissionADM = { + override def toPermissionADM(groupIri: IRI): PermissionADM = PermissionADM.modifyPermission(groupIri) - } } /** - * Represents delete permission on an entity. - */ + * Represents delete permission on an entity. + */ case object DeletePermission extends EntityPermission { override def toInt: Int = 7 @@ -118,14 +115,13 @@ object PermissionUtilADM extends LazyLogging { override val getName: String = "delete permission" - override def toPermissionADM(groupIri: IRI): PermissionADM = { + override def toPermissionADM(groupIri: IRI): PermissionADM = PermissionADM.deletePermission(groupIri) - } } /** - * Represents permission to change the permissions on an entity. - */ + * Represents permission to change the permissions on an entity. + */ case object ChangeRightsPermission extends EntityPermission { override def toInt: Int = 8 @@ -133,14 +129,13 @@ object PermissionUtilADM extends LazyLogging { override val getName: String = "change rights permission" - override def toPermissionADM(groupIri: IRI): PermissionADM = { + override def toPermissionADM(groupIri: IRI): PermissionADM = PermissionADM.changeRightsPermission(groupIri) - } } /** - * The highest permission, i.e. the one that is least restrictive. - */ + * The highest permission, i.e. the one that is least restrictive. + */ private val MaxPermissionLevel: EntityPermission = ChangeRightsPermission private val permissionStringsToPermissionLevels: Map[String, EntityPermission] = Set( @@ -154,8 +149,8 @@ object PermissionUtilADM extends LazyLogging { }.toMap /** - * A set of assertions that are relevant for calculating permissions. - */ + * A set of assertions that are relevant for calculating permissions. + */ private val permissionRelevantAssertions = Set( OntologyConstants.KnoraBase.AttachedToUser, OntologyConstants.KnoraBase.AttachedToProject, @@ -163,58 +158,57 @@ object PermissionUtilADM extends LazyLogging { ) /** - * Given the IRI of an RDF property, returns `true` if the property is relevant to calculating permissions. This - * is the case if the property is [[OntologyConstants.KnoraBase.AttachedToUser]], - * [[OntologyConstants.KnoraBase.AttachedToProject]], or - * or [[OntologyConstants.KnoraBase.HasPermissions]]. - * - * @param p the IRI of the property. - * @return `true` if the property is relevant to calculating permissions. - */ + * Given the IRI of an RDF property, returns `true` if the property is relevant to calculating permissions. This + * is the case if the property is [[OntologyConstants.KnoraBase.AttachedToUser]], + * [[OntologyConstants.KnoraBase.AttachedToProject]], or + * or [[OntologyConstants.KnoraBase.HasPermissions]]. + * + * @param p the IRI of the property. + * @return `true` if the property is relevant to calculating permissions. + */ def isPermissionRelevant(p: IRI): Boolean = permissionRelevantAssertions.contains(p) /** - * Given a list of predicates and objects pertaining to a entity, returns only the ones that are relevant to - * permissions (i.e. the permissions themselves, plus the creator and project). - * - * @param assertions a list containing the permission-relevant predicates and objects - * pertaining to the entity. Other predicates will be filtered out. - * @return a list of permission-relevant predicates and objects. - */ - def filterPermissionRelevantAssertions(assertions: Seq[(IRI, IRI)]): Vector[(IRI, IRI)] = { - assertions.filter { - case (p, o) => isPermissionRelevant(p) + * Given a list of predicates and objects pertaining to a entity, returns only the ones that are relevant to + * permissions (i.e. the permissions themselves, plus the creator and project). + * + * @param assertions a list containing the permission-relevant predicates and objects + * pertaining to the entity. Other predicates will be filtered out. + * @return a list of permission-relevant predicates and objects. + */ + def filterPermissionRelevantAssertions(assertions: Seq[(IRI, IRI)]): Vector[(IRI, IRI)] = + assertions.filter { case (p, o) => + isPermissionRelevant(p) }.toVector - } /** - * Given a [[ValueProps]] describing a `knora-base:Value`, returns the permission-relevant assertions contained - * in the [[ValueProps]] (i.e. permission assertion, plus assertions about the entity's creator and project). - * - * @param valueProps a [[ValueProps]] describing a `knora-base:Value`. - * @return a list of permission-relevant predicates and objects. - */ - def filterPermissionRelevantAssertionsFromValueProps(valueProps: ValueProps): Vector[(IRI, IRI)] = { - valueProps.literalData.foldLeft(Vector.empty[(IRI, IRI)]) { - case (acc, (predicate: IRI, ValueLiterals(literals))) => - if (isPermissionRelevant(predicate)) { - acc ++ literals.map(literal => (predicate, literal)) - } else { - acc - } + * Given a [[ValueProps]] describing a `knora-base:Value`, returns the permission-relevant assertions contained + * in the [[ValueProps]] (i.e. permission assertion, plus assertions about the entity's creator and project). + * + * @param valueProps a [[ValueProps]] describing a `knora-base:Value`. + * @return a list of permission-relevant predicates and objects. + */ + def filterPermissionRelevantAssertionsFromValueProps(valueProps: ValueProps): Vector[(IRI, IRI)] = + valueProps.literalData.foldLeft(Vector.empty[(IRI, IRI)]) { case (acc, (predicate: IRI, ValueLiterals(literals))) => + if (isPermissionRelevant(predicate)) { + acc ++ literals.map(literal => (predicate, literal)) + } else { + acc + } } - } /** - * Calculates the highest permission level a user can be granted on a entity. - * - * @param entityPermissions a map of permissions on a entity to the groups they are granted to. - * @param userGroups the groups that the user belongs to. - * @return the code of the highest permission the user has on the entity, or `None` if the user has no permissions - * on the entity. - */ - private def calculateHighestGrantedPermissionLevel(entityPermissions: Map[EntityPermission, Set[IRI]], - userGroups: Set[IRI]): Option[EntityPermission] = { + * Calculates the highest permission level a user can be granted on a entity. + * + * @param entityPermissions a map of permissions on a entity to the groups they are granted to. + * @param userGroups the groups that the user belongs to. + * @return the code of the highest permission the user has on the entity, or `None` if the user has no permissions + * on the entity. + */ + private def calculateHighestGrantedPermissionLevel( + entityPermissions: Map[EntityPermission, Set[IRI]], + userGroups: Set[IRI] + ): Option[EntityPermission] = { // Make a set of all the permissions the user can obtain for this entity. val permissionLevels: Set[EntityPermission] = entityPermissions.foldLeft(Set.empty[EntityPermission]) { case (acc, (permissionLevel, grantedToGroups)) => @@ -235,22 +229,26 @@ object PermissionUtilADM extends LazyLogging { } /** - * Determines the permissions that a user has on a entity, and returns an [[EntityPermission]]. - * - * @param entityCreator the IRI of the user that created the entity. - * @param entityProject the IRI of the entity's project. - * @param entityPermissionLiteral the literal that is the object of the entity's `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return an [[EntityPermission]] representing the user's permission level for the entity, or `None` if the user - * has no permissions on the entity. - */ - def getUserPermissionADM(entityCreator: IRI, - entityProject: IRI, - entityPermissionLiteral: String, - requestingUser: UserADM): Option[EntityPermission] = { + * Determines the permissions that a user has on a entity, and returns an [[EntityPermission]]. + * + * @param entityCreator the IRI of the user that created the entity. + * @param entityProject the IRI of the entity's project. + * @param entityPermissionLiteral the literal that is the object of the entity's `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return an [[EntityPermission]] representing the user's permission level for the entity, or `None` if the user + * has no permissions on the entity. + */ + def getUserPermissionADM( + entityCreator: IRI, + entityProject: IRI, + entityPermissionLiteral: String, + requestingUser: UserADM + ): Option[EntityPermission] = { val maybePermissionLevel = - if (requestingUser.isSystemUser || requestingUser.isSystemAdmin || requestingUser.permissions - .hasProjectAdminAllPermissionFor(entityProject)) { + if ( + requestingUser.isSystemUser || requestingUser.isSystemAdmin || requestingUser.permissions + .hasProjectAdminAllPermissionFor(entityProject) + ) { // If the user is the system user, is in the SystemAdmin group, or has ProjectAdminAllPermission, just give them the maximum permission. Some(MaxPermissionLevel) } else { @@ -295,45 +293,47 @@ object PermissionUtilADM extends LazyLogging { } /** - * A trait representing a result returned by [[comparePermissionsADM]]. - */ + * A trait representing a result returned by [[comparePermissionsADM]]. + */ sealed trait PermissionComparisonResult /** - * Indicates that the user would have a lower permission with permission string A. - */ + * Indicates that the user would have a lower permission with permission string A. + */ case object ALessThanB extends PermissionComparisonResult /** - * Indicates that permission strings A and B would give the user the same permission. - */ + * Indicates that permission strings A and B would give the user the same permission. + */ case object AEqualToB extends PermissionComparisonResult /** - * Indicates that the user would have a higher permission with permission string A. - */ + * Indicates that the user would have a higher permission with permission string A. + */ case object AGreaterThanB extends PermissionComparisonResult /** - * Calculates the permissions that the specified user would have on an entity with two permission strings, - * and returns: - * - * - [[ALessThanB]] if the user would have a lower permission with `permissionLiteralA`. - * - [[AEqualToB]] if `permissionLiteralA` and `permissionLiteralB` would give the user the same permission. - * - [[AGreaterThanB]] if the user would have a higher permission with `permissionLiteralA`. - * - * @param entityCreator the IRI of the user that created the entity. - * @param entityProject the IRI of the entity's project. - * @param permissionLiteralA the first permission string. - * @param permissionLiteralB the second permission string. - * @param requestingUser the user making the request. - * @return a [[PermissionComparisonResult]]. - */ - def comparePermissionsADM(entityCreator: IRI, - entityProject: IRI, - permissionLiteralA: String, - permissionLiteralB: String, - requestingUser: UserADM): PermissionComparisonResult = { + * Calculates the permissions that the specified user would have on an entity with two permission strings, + * and returns: + * + * - [[ALessThanB]] if the user would have a lower permission with `permissionLiteralA`. + * - [[AEqualToB]] if `permissionLiteralA` and `permissionLiteralB` would give the user the same permission. + * - [[AGreaterThanB]] if the user would have a higher permission with `permissionLiteralA`. + * + * @param entityCreator the IRI of the user that created the entity. + * @param entityProject the IRI of the entity's project. + * @param permissionLiteralA the first permission string. + * @param permissionLiteralB the second permission string. + * @param requestingUser the user making the request. + * @return a [[PermissionComparisonResult]]. + */ + def comparePermissionsADM( + entityCreator: IRI, + entityProject: IRI, + permissionLiteralA: String, + permissionLiteralB: String, + requestingUser: UserADM + ): PermissionComparisonResult = { val maybePermissionA: Option[EntityPermission] = getUserPermissionADM( entityCreator = requestingUser.id, entityProject = entityProject, @@ -365,23 +365,25 @@ object PermissionUtilADM extends LazyLogging { } /** - * Given data from a [[SparqlExtendedConstructResponse]], determines the permissions that a user has on a entity, - * and returns an [[EntityPermission]]. - * - * @param entityIri the IRI of the entity. - * @param assertions a [[Seq]] containing all the permission-relevant predicates and objects - * pertaining to the entity. The predicates must include - * [[OntologyConstants.KnoraBase.AttachedToUser]] and - * [[OntologyConstants.KnoraBase.AttachedToProject]], and should include - * [[OntologyConstants.KnoraBase.HasPermissions]]. - * Other predicates may be included, but they will be ignored, so there is no need to filter - * them before passing them to this function. - * @param requestingUser the profile of the user making the request. - * @return a code representing the user's permission level for the entity. - */ - def getUserPermissionFromConstructAssertionsADM(entityIri: IRI, - assertions: ConstructPredicateObjects, - requestingUser: UserADM): Option[EntityPermission] = { + * Given data from a [[SparqlExtendedConstructResponse]], determines the permissions that a user has on a entity, + * and returns an [[EntityPermission]]. + * + * @param entityIri the IRI of the entity. + * @param assertions a [[Seq]] containing all the permission-relevant predicates and objects + * pertaining to the entity. The predicates must include + * [[OntologyConstants.KnoraBase.AttachedToUser]] and + * [[OntologyConstants.KnoraBase.AttachedToProject]], and should include + * [[OntologyConstants.KnoraBase.HasPermissions]]. + * Other predicates may be included, but they will be ignored, so there is no need to filter + * them before passing them to this function. + * @param requestingUser the profile of the user making the request. + * @return a code representing the user's permission level for the entity. + */ + def getUserPermissionFromConstructAssertionsADM( + entityIri: IRI, + assertions: ConstructPredicateObjects, + requestingUser: UserADM + ): Option[EntityPermission] = { val assertionsAsStrings: Seq[(IRI, String)] = assertions.toSeq.flatMap { case (pred: SmartIri, objs: Seq[LiteralV2]) => objs.map { obj => @@ -397,35 +399,40 @@ object PermissionUtilADM extends LazyLogging { } /** - * Determines the permissions that a user has on a entity, and returns an [[EntityPermission]]. - * - * @param entityIri the IRI of the entity. - * @param assertions a [[Seq]] containing all the permission-relevant predicates and objects - * pertaining to the entity. The predicates must include - * [[OntologyConstants.KnoraBase.AttachedToUser]] and - * [[OntologyConstants.KnoraBase.AttachedToProject]], and should include - * [[OntologyConstants.KnoraBase.HasPermissions]]. - * Other predicates may be included, but they will be ignored, so there is no need to filter - * them before passing them to this function. - * @param requestingUser the profile of the user making the request. - * @return a code representing the user's permission level for the entity. - */ - def getUserPermissionFromAssertionsADM(entityIri: IRI, - assertions: Seq[(IRI, String)], - requestingUser: UserADM): Option[EntityPermission] = { + * Determines the permissions that a user has on a entity, and returns an [[EntityPermission]]. + * + * @param entityIri the IRI of the entity. + * @param assertions a [[Seq]] containing all the permission-relevant predicates and objects + * pertaining to the entity. The predicates must include + * [[OntologyConstants.KnoraBase.AttachedToUser]] and + * [[OntologyConstants.KnoraBase.AttachedToProject]], and should include + * [[OntologyConstants.KnoraBase.HasPermissions]]. + * Other predicates may be included, but they will be ignored, so there is no need to filter + * them before passing them to this function. + * @param requestingUser the profile of the user making the request. + * @return a code representing the user's permission level for the entity. + */ + def getUserPermissionFromAssertionsADM( + entityIri: IRI, + assertions: Seq[(IRI, String)], + requestingUser: UserADM + ): Option[EntityPermission] = { // Get the entity's creator, project, and permissions. val assertionMap: Map[IRI, String] = assertions.toMap // Anything with permissions must have an creator and a project. val entityCreator: IRI = assertionMap.getOrElse( OntologyConstants.KnoraBase.AttachedToUser, - throw InconsistentRepositoryDataException(s"Entity $entityIri has no creator")) + throw InconsistentRepositoryDataException(s"Entity $entityIri has no creator") + ) val entityProject: IRI = assertionMap.getOrElse( OntologyConstants.KnoraBase.AttachedToProject, - throw InconsistentRepositoryDataException(s"Entity $entityIri has no project")) + throw InconsistentRepositoryDataException(s"Entity $entityIri has no project") + ) val entityPermissionLiteral: String = assertionMap.getOrElse( OntologyConstants.KnoraBase.HasPermissions, - throw InconsistentRepositoryDataException(s"Entity $entityIri has no knora-base:hasPermissions predicate")) + throw InconsistentRepositoryDataException(s"Entity $entityIri has no knora-base:hasPermissions predicate") + ) getUserPermissionADM( entityCreator = entityCreator, @@ -436,16 +443,19 @@ object PermissionUtilADM extends LazyLogging { } /** - * Parses the literal object of the predicate `knora-base:hasPermissions`. - * - * @param permissionLiteral the literal to parse. - * @return a [[Map]] in which the keys are permission abbreviations in - * [[OntologyConstants.KnoraBase.EntityPermissionAbbreviations]], and the values are sets of - * user group IRIs. - */ - def parsePermissions(permissionLiteral: String, errorFun: String => Nothing = { permissionLiteral: String => - throw InconsistentRepositoryDataException(s"invalid permission literal: $permissionLiteral") - }): Map[EntityPermission, Set[IRI]] = { + * Parses the literal object of the predicate `knora-base:hasPermissions`. + * + * @param permissionLiteral the literal to parse. + * @return a [[Map]] in which the keys are permission abbreviations in + * [[OntologyConstants.KnoraBase.EntityPermissionAbbreviations]], and the values are sets of + * user group IRIs. + */ + def parsePermissions( + permissionLiteral: String, + errorFun: String => Nothing = { permissionLiteral: String => + throw InconsistentRepositoryDataException(s"invalid permission literal: $permissionLiteral") + } + ): Map[EntityPermission, Set[IRI]] = { val permissions: Seq[String] = permissionLiteral.split(OntologyConstants.KnoraBase.PermissionListDelimiter) permissions.map { permission => @@ -463,77 +473,86 @@ object PermissionUtilADM extends LazyLogging { val shortGroups: Set[String] = splitPermission(1).split(OntologyConstants.KnoraBase.GroupListDelimiter).toSet val groups = shortGroups.map( - _.replace(OntologyConstants.KnoraAdmin.KnoraAdminPrefix, - OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion)) + _.replace(OntologyConstants.KnoraAdmin.KnoraAdminPrefix, OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion) + ) (permissionStringsToPermissionLevels(abbreviation), groups) }.toMap } /** - * Parses the literal object of the predicate `knora-base:hasPermissions`. - * - * @param maybePermissionListStr the literal to parse. - * @return a [[Map]] in which the keys are permission abbreviations in - * [[OntologyConstants.KnoraBase.EntityPermissionAbbreviations]], and the values are sets of - * user group IRIs. - */ - def parsePermissionsWithType(maybePermissionListStr: Option[String], - permissionType: PermissionType): Set[PermissionADM] = { + * Parses the literal object of the predicate `knora-base:hasPermissions`. + * + * @param maybePermissionListStr the literal to parse. + * @return a [[Map]] in which the keys are permission abbreviations in + * [[OntologyConstants.KnoraBase.EntityPermissionAbbreviations]], and the values are sets of + * user group IRIs. + */ + def parsePermissionsWithType( + maybePermissionListStr: Option[String], + permissionType: PermissionType + ): Set[PermissionADM] = maybePermissionListStr match { - case Some(permissionListStr) => { - val cleanedPermissionListStr = permissionListStr.replaceAll("[<>]", "") - val permissions: Seq[String] = - cleanedPermissionListStr.split(OntologyConstants.KnoraBase.PermissionListDelimiter) - logger.debug(s"PermissionUtil.parsePermissionsWithType - split permissions: $permissions") - permissions.flatMap { permission => - val splitPermission = permission.split(' ') - val abbreviation = splitPermission(0) - - permissionType match { - case PermissionType.AP => - if (!OntologyConstants.KnoraAdmin.AdministrativePermissionAbbreviations.contains(abbreviation)) { - throw InconsistentRepositoryDataException(s"Unrecognized permission abbreviation '$abbreviation'") - } - - if (splitPermission.length > 1) { + case Some(permissionListStr) => + { + val cleanedPermissionListStr = permissionListStr.replaceAll("[<>]", "") + val permissions: Seq[String] = + cleanedPermissionListStr.split(OntologyConstants.KnoraBase.PermissionListDelimiter) + logger.debug(s"PermissionUtil.parsePermissionsWithType - split permissions: $permissions") + permissions.flatMap { permission => + val splitPermission = permission.split(' ') + val abbreviation = splitPermission(0) + + permissionType match { + case PermissionType.AP => + if (!OntologyConstants.KnoraAdmin.AdministrativePermissionAbbreviations.contains(abbreviation)) { + throw InconsistentRepositoryDataException(s"Unrecognized permission abbreviation '$abbreviation'") + } + + if (splitPermission.length > 1) { + val shortGroups: Array[String] = + splitPermission(1).split(OntologyConstants.KnoraBase.GroupListDelimiter) + val groups: Set[IRI] = shortGroups + .map( + _.replace( + OntologyConstants.KnoraAdmin.KnoraAdminPrefix, + OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion + ) + ) + .toSet + buildPermissionObject(abbreviation, groups) + } else { + buildPermissionObject(abbreviation, Set.empty[IRI]) + } + + case PermissionType.OAP => + if (!OntologyConstants.KnoraBase.EntityPermissionAbbreviations.contains(abbreviation)) { + throw InconsistentRepositoryDataException(s"Unrecognized permission abbreviation '$abbreviation'") + } val shortGroups: Array[String] = splitPermission(1).split(OntologyConstants.KnoraBase.GroupListDelimiter) val groups: Set[IRI] = shortGroups .map( - _.replace(OntologyConstants.KnoraAdmin.KnoraAdminPrefix, - OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion)) + _.replace( + OntologyConstants.KnoraAdmin.KnoraAdminPrefix, + OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion + ) + ) .toSet buildPermissionObject(abbreviation, groups) - } else { - buildPermissionObject(abbreviation, Set.empty[IRI]) - } - - case PermissionType.OAP => - if (!OntologyConstants.KnoraBase.EntityPermissionAbbreviations.contains(abbreviation)) { - throw InconsistentRepositoryDataException(s"Unrecognized permission abbreviation '$abbreviation'") - } - val shortGroups: Array[String] = splitPermission(1).split(OntologyConstants.KnoraBase.GroupListDelimiter) - val groups: Set[IRI] = shortGroups - .map( - _.replace(OntologyConstants.KnoraAdmin.KnoraAdminPrefix, - OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion)) - .toSet - buildPermissionObject(abbreviation, groups) + } } - } - }.toSet + }.toSet case None => Set.empty[PermissionADM] } - } /** - * Helper method used to convert the permission string stored inside the triplestore to a permission object. - * - * @param name the name of the permission. - * @param iris the optional set of additional information (e.g., group IRIs, resource class IRIs). - * @return a sequence of permission objects. - */ - def buildPermissionObject(name: String, iris: Set[IRI]): Set[PermissionADM] = { + * Helper method used to convert the permission string stored inside the triplestore to a permission object. + * + * @param name the name of the permission. + * @param iris the optional set of additional information (e.g., group IRIs, resource class IRIs). + * @return a sequence of permission objects. + */ + def buildPermissionObject(name: String, iris: Set[IRI]): Set[PermissionADM] = name match { case OntologyConstants.KnoraAdmin.ProjectResourceCreateAllPermission => Set(PermissionADM.ProjectResourceCreateAllPermission) @@ -597,14 +616,12 @@ object PermissionUtilADM extends LazyLogging { } } - } - /** - * Helper method used to remove remove duplicate permissions. - * - * @param permissions the sequence of permissions with possible duplicates. - * @return a set containing only unique permission. - */ + * Helper method used to remove remove duplicate permissions. + * + * @param permissions the sequence of permissions with possible duplicates. + * @return a set containing only unique permission. + */ def removeDuplicatePermissions(permissions: Seq[PermissionADM]): Set[PermissionADM] = { val result = permissions.groupBy(perm => perm.name + perm.additionalInformation).map { case (k, v) => v.head }.toSet @@ -613,24 +630,23 @@ object PermissionUtilADM extends LazyLogging { } /** - * Helper method used to remove lesser permissions, i.e. permissions which are already given by - * the highest permission. - * - * @param permissions a set of permissions possibly containing lesser permissions. - * @param permissionType the type of permissions. - * @return a set of permissions without possible lesser permissions. - */ - def removeLesserPermissions(permissions: Set[PermissionADM], permissionType: PermissionType): Set[PermissionADM] = { + * Helper method used to remove lesser permissions, i.e. permissions which are already given by + * the highest permission. + * + * @param permissions a set of permissions possibly containing lesser permissions. + * @param permissionType the type of permissions. + * @return a set of permissions without possible lesser permissions. + */ + def removeLesserPermissions(permissions: Set[PermissionADM], permissionType: PermissionType): Set[PermissionADM] = permissionType match { case PermissionType.OAP => if (permissions.nonEmpty) { /* Handling object access permissions which always have 'additionalInformation' and 'permissionCode' set */ permissions .groupBy(_.additionalInformation) - .map { - case (groupIri, perms) => - // sort in descending order and then take the first one (the highest permission) - perms.toArray.sortWith(_.permissionCode.get > _.permissionCode.get).head + .map { case (groupIri, perms) => + // sort in descending order and then take the first one (the highest permission) + perms.toArray.sortWith(_.permissionCode.get > _.permissionCode.get).head } .toSet } else { @@ -639,37 +655,39 @@ object PermissionUtilADM extends LazyLogging { case PermissionType.AP => ??? } - } /** - * Helper method used to transform a set of permissions into a permissions string ready to be written into the - * triplestore as the value for the 'knora-base:hasPermissions' property. - * - * @param permissions the permissions to be formatted. - * @param permissionType a [[PermissionType]] indicating the type of permissions to be formatted. - * @return - */ - def formatPermissionADMs(permissions: Set[PermissionADM], permissionType: PermissionType): String = { + * Helper method used to transform a set of permissions into a permissions string ready to be written into the + * triplestore as the value for the 'knora-base:hasPermissions' property. + * + * @param permissions the permissions to be formatted. + * @param permissionType a [[PermissionType]] indicating the type of permissions to be formatted. + * @return + */ + def formatPermissionADMs(permissions: Set[PermissionADM], permissionType: PermissionType): String = permissionType match { case PermissionType.OAP => if (permissions.nonEmpty) { /* a map with permission names, shortened groups, and full group names. */ - val groupedPermissions: Map[String, String] = permissions.groupBy(_.name).map { - case (name: String, perms: Set[PermissionADM]) => + val groupedPermissions: Map[String, String] = + permissions.groupBy(_.name).map { case (name: String, perms: Set[PermissionADM]) => val shortGroupsString = perms.toVector.sortBy(_.additionalInformation.get).foldLeft("") { case (acc: String, perm: PermissionADM) => if (acc.isEmpty) { - acc + perm.additionalInformation.get.replace(OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion, - OntologyConstants.KnoraAdmin.KnoraAdminPrefix) + acc + perm.additionalInformation.get.replace( + OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion, + OntologyConstants.KnoraAdmin.KnoraAdminPrefix + ) } else { acc + OntologyConstants.KnoraBase.GroupListDelimiter + perm.additionalInformation.get.replace( OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion, - OntologyConstants.KnoraAdmin.KnoraAdminPrefix) + OntologyConstants.KnoraAdmin.KnoraAdminPrefix + ) } } (name, shortGroupsString) - } + } /* Sort permissions in descending order */ val sortedPermissions: Array[(String, String)] = groupedPermissions.toArray.sortWith { (left, right) => @@ -705,36 +723,42 @@ object PermissionUtilADM extends LazyLogging { throw InconsistentRepositoryDataException("Permissions cannot be empty") } } - } /** - * Given a permission literal, checks that it refers to valid permissions and groups. - * - * @param permissionLiteral the permission literal. - * @param featureFactoryConfig the feature factory configuration. - * @param responderManager a reference to the responder manager. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return the validated permission literal, normalised and reformatted. - */ + * Given a permission literal, checks that it refers to valid permissions and groups. + * + * @param permissionLiteral the permission literal. + * @param featureFactoryConfig the feature factory configuration. + * @param responderManager a reference to the responder manager. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return the validated permission literal, normalised and reformatted. + */ def validatePermissions( - permissionLiteral: String, - featureFactoryConfig: FeatureFactoryConfig, - responderManager: ActorRef)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[String] = { + permissionLiteral: String, + featureFactoryConfig: FeatureFactoryConfig, + responderManager: ActorRef + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[String] = { val stringFormatter = StringFormatter.getGeneralInstance for { // Parse the permission literal. parsedPermissions: Map[PermissionUtilADM.EntityPermission, Set[IRI]] <- Future( - parsePermissions(permissionLiteral = permissionLiteral, errorFun = { literal => - throw BadRequestException(s"Invalid permission literal: $literal") - })) + parsePermissions( + permissionLiteral = permissionLiteral, + errorFun = { literal => + throw BadRequestException(s"Invalid permission literal: $literal") + } + ) + ) // Get the group IRIs that are mentioned, minus the built-in groups. - projectSpecificGroupIris: Set[IRI] = parsedPermissions.values.flatten.toSet -- OntologyConstants.KnoraAdmin.BuiltInGroups + projectSpecificGroupIris: Set[IRI] = + parsedPermissions.values.flatten.toSet -- OntologyConstants.KnoraAdmin.BuiltInGroups validatedProjectSpecificGroupIris: Set[IRI] = projectSpecificGroupIris.map(iri => - stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid group IRI: $iri"))) + stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid group IRI: $iri")) + ) // Check that those groups exist. _ <- (responderManager ? MultipleGroupsGetRequestADM( @@ -744,11 +768,10 @@ object PermissionUtilADM extends LazyLogging { )).mapTo[Set[GroupGetResponseADM]] // Reformat the permission literal. - permissionADMs: Set[PermissionADM] = parsedPermissions.flatMap { - case (entityPermission, groupIris) => - groupIris.map { groupIri => - entityPermission.toPermissionADM(groupIri) - } + permissionADMs: Set[PermissionADM] = parsedPermissions.flatMap { case (entityPermission, groupIris) => + groupIris.map { groupIri => + entityPermission.toPermissionADM(groupIri) + } }.toSet } yield formatPermissionADMs(permissions = permissionADMs, permissionType = PermissionType.OAP) } @@ -757,78 +780,86 @@ object PermissionUtilADM extends LazyLogging { // API v1 methods /** - * Determines the permissions that a user has on a entity, and returns an integer permissions code. - * - * @param entityIri the IRI of the entity. - * @param assertions a [[Seq]] containing all the permission-relevant predicates and objects - * pertaining to the entity. The predicates must include - * [[OntologyConstants.KnoraBase.AttachedToUser]] and - * [[OntologyConstants.KnoraBase.AttachedToProject]], and should include - * [[OntologyConstants.KnoraBase.HasPermissions]]. - * Other predicates may be included, but they will be ignored, so there is no need to filter - * them before passing them to this function. - * @param userProfile the profile of the user making the request. - * @return a code representing the user's permission level for the entity. - */ - def getUserPermissionFromAssertionsV1(entityIri: IRI, - assertions: Seq[(IRI, String)], - userProfile: UserProfileV1): Option[Int] = { + * Determines the permissions that a user has on a entity, and returns an integer permissions code. + * + * @param entityIri the IRI of the entity. + * @param assertions a [[Seq]] containing all the permission-relevant predicates and objects + * pertaining to the entity. The predicates must include + * [[OntologyConstants.KnoraBase.AttachedToUser]] and + * [[OntologyConstants.KnoraBase.AttachedToProject]], and should include + * [[OntologyConstants.KnoraBase.HasPermissions]]. + * Other predicates may be included, but they will be ignored, so there is no need to filter + * them before passing them to this function. + * @param userProfile the profile of the user making the request. + * @return a code representing the user's permission level for the entity. + */ + def getUserPermissionFromAssertionsV1( + entityIri: IRI, + assertions: Seq[(IRI, String)], + userProfile: UserProfileV1 + ): Option[Int] = { // Get the entity's creator, project, and permissions. val assertionMap: Map[IRI, String] = assertions.toMap // Anything with permissions must have an creator and a project. val entityCreator: IRI = assertionMap.getOrElse( OntologyConstants.KnoraBase.AttachedToUser, - throw InconsistentRepositoryDataException(s"entity $entityIri has no creator")) + throw InconsistentRepositoryDataException(s"entity $entityIri has no creator") + ) val entityProject: IRI = assertionMap.getOrElse( OntologyConstants.KnoraBase.AttachedToProject, - throw InconsistentRepositoryDataException(s"entity $entityIri has no project")) + throw InconsistentRepositoryDataException(s"entity $entityIri has no project") + ) val entityPermissionLiteral: String = assertionMap.getOrElse( OntologyConstants.KnoraBase.HasPermissions, - throw InconsistentRepositoryDataException(s"entity $entityIri has no knora-base:hasPermissions predicate")) + throw InconsistentRepositoryDataException(s"entity $entityIri has no knora-base:hasPermissions predicate") + ) - getUserPermissionV1(entityIri = entityIri, - entityCreator = entityCreator, - entityProject = entityProject, - entityPermissionLiteral = entityPermissionLiteral, - userProfile = userProfile) + getUserPermissionV1( + entityIri = entityIri, + entityCreator = entityCreator, + entityProject = entityProject, + entityPermissionLiteral = entityPermissionLiteral, + userProfile = userProfile + ) } /** - * Checks whether an integer permission code implies a particular permission property. - * - * @param userHasPermissionCode the integer permission code that the user has, or [[None]] if the user has no permissions - * (in which case this method returns `false`). - * @param userNeedsPermission the abbreviation of the permission that the user needs. - * @return `true` if the user has the needed permission. - */ - def impliesPermissionCodeV1(userHasPermissionCode: Option[Int], userNeedsPermission: String): Boolean = { + * Checks whether an integer permission code implies a particular permission property. + * + * @param userHasPermissionCode the integer permission code that the user has, or [[None]] if the user has no permissions + * (in which case this method returns `false`). + * @param userNeedsPermission the abbreviation of the permission that the user needs. + * @return `true` if the user has the needed permission. + */ + def impliesPermissionCodeV1(userHasPermissionCode: Option[Int], userNeedsPermission: String): Boolean = userHasPermissionCode match { case Some(permissionCode) => permissionCode >= permissionStringsToPermissionLevels(userNeedsPermission).toInt case None => false } - } /** - * Determines the permissions that a user has on a `knora-base:Value`, and returns an integer permission code. - * - * @param valueIri the IRI of the `knora-base:Value`. - * @param valueProps a [[ValueProps]] containing the permission-relevant predicates and objects - * pertaining to the value, grouped by predicate. The predicates must include - * [[OntologyConstants.KnoraBase.AttachedToUser]], and should include - * [[OntologyConstants.KnoraBase.AttachedToProject]] - * and [[OntologyConstants.KnoraBase.HasPermissions]]. Other predicates may be - * included, but they will be ignored, so there is no need to filter them before passing them to - * this function. - * @param entityProject if provided, the `knora-base:attachedToProject` of the resource containing the value. Otherwise, - * this predicate must be in `valueProps`. - * @param userProfile the profile of the user making the request. - * @return a code representing the user's permission level on the value. - */ - def getUserPermissionWithValuePropsV1(valueIri: IRI, - valueProps: ValueProps, - entityProject: Option[IRI], - userProfile: UserProfileV1): Option[Int] = { + * Determines the permissions that a user has on a `knora-base:Value`, and returns an integer permission code. + * + * @param valueIri the IRI of the `knora-base:Value`. + * @param valueProps a [[ValueProps]] containing the permission-relevant predicates and objects + * pertaining to the value, grouped by predicate. The predicates must include + * [[OntologyConstants.KnoraBase.AttachedToUser]], and should include + * [[OntologyConstants.KnoraBase.AttachedToProject]] + * and [[OntologyConstants.KnoraBase.HasPermissions]]. Other predicates may be + * included, but they will be ignored, so there is no need to filter them before passing them to + * this function. + * @param entityProject if provided, the `knora-base:attachedToProject` of the resource containing the value. Otherwise, + * this predicate must be in `valueProps`. + * @param userProfile the profile of the user making the request. + * @return a code representing the user's permission level on the value. + */ + def getUserPermissionWithValuePropsV1( + valueIri: IRI, + valueProps: ValueProps, + entityProject: Option[IRI], + userProfile: UserProfileV1 + ): Option[Int] = { // Either entityProject must be provided, or there must be a knora-base:attachedToProject in valueProps. @@ -843,7 +874,8 @@ object PermissionUtilADM extends LazyLogging { if (providedProjects.size > 1) { throw InconsistentRepositoryDataException( - s"Two different values of knora-base:attachedToProject were provided for entity $valueIri: ${valuePropsProject.get} and ${entityProject.get}") + s"Two different values of knora-base:attachedToProject were provided for entity $valueIri: ${valuePropsProject.get} and ${entityProject.get}" + ) } val valuePropsAssertionsWithoutProject: Vector[(IRI, IRI)] = @@ -858,24 +890,28 @@ object PermissionUtilADM extends LazyLogging { } /** - * Determines the permissions that a user has on a entity, and returns an integer permission code. - * - * @param entityIri the IRI of the entity. - * @param entityCreator the IRI of the user that created the entity. - * @param entityProject the IRI of the entity's project. - * @param entityPermissionLiteral the literal that is the object of the entity's `knora-base:hasPermissions` predicate. - * @param userProfile the profile of the user making the request. - * @return a code representing the user's permission level for the entity. - */ - def getUserPermissionV1(entityIri: IRI, - entityCreator: IRI, - entityProject: IRI, - entityPermissionLiteral: String, - userProfile: UserProfileV1): Option[Int] = { + * Determines the permissions that a user has on a entity, and returns an integer permission code. + * + * @param entityIri the IRI of the entity. + * @param entityCreator the IRI of the user that created the entity. + * @param entityProject the IRI of the entity's project. + * @param entityPermissionLiteral the literal that is the object of the entity's `knora-base:hasPermissions` predicate. + * @param userProfile the profile of the user making the request. + * @return a code representing the user's permission level for the entity. + */ + def getUserPermissionV1( + entityIri: IRI, + entityCreator: IRI, + entityProject: IRI, + entityPermissionLiteral: String, + userProfile: UserProfileV1 + ): Option[Int] = { val maybePermissionLevel = - if (userProfile.isSystemUser || userProfile.permissionData.isSystemAdmin || userProfile.permissionData - .hasProjectAdminAllPermissionFor(entityProject)) { + if ( + userProfile.isSystemUser || userProfile.permissionData.isSystemAdmin || userProfile.permissionData + .hasProjectAdminAllPermissionFor(entityProject) + ) { // If the user is the system user, is in the SystemAdmin group or has ProjectAdminAllPermission, just give them the maximum permission. Some(MaxPermissionLevel) } else { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala index 3ed7b16d45..c12d850207 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala @@ -24,12 +24,14 @@ import org.knora.webapi.settings.KnoraSettingsImpl import org.knora.webapi.store.cacheservice.settings.CacheServiceSettings /** - * Data needed to be passed to each responder. - * - * @param system the actor system. - * @param appActor the main application actor. - */ -case class ResponderData(system: ActorSystem, - appActor: ActorRef, - knoraSettings: KnoraSettingsImpl, - cacheServiceSettings: CacheServiceSettings) + * Data needed to be passed to each responder. + * + * @param system the actor system. + * @param appActor the main application actor. + */ +case class ResponderData( + system: ActorSystem, + appActor: ActorRef, + knoraSettings: KnoraSettingsImpl, + cacheServiceSettings: CacheServiceSettings +) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/UserUtilADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/UserUtilADM.scala index 567fb437b4..29466fd970 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/UserUtilADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/UserUtilADM.scala @@ -32,35 +32,39 @@ import org.knora.webapi.messages.admin.responder.usersmessages._ import scala.concurrent.{ExecutionContext, Future} /** - * Utility functions for working with users. - */ + * Utility functions for working with users. + */ object UserUtilADM { /** - * Allows a system admin or project admin to perform an operation as another user in a specified project. - * Checks whether the requesting user is a system admin or a project admin in the project, and if so, - * returns a [[UserADM]] representing the requested user. Otherwise, returns a failed future containing - * [[ForbiddenException]]. - * - * @param requestingUser the requesting user. - * @param requestedUserIri the IRI of the requested user. - * @param projectIri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[UserADM]] representing the requested user. - */ + * Allows a system admin or project admin to perform an operation as another user in a specified project. + * Checks whether the requesting user is a system admin or a project admin in the project, and if so, + * returns a [[UserADM]] representing the requested user. Otherwise, returns a failed future containing + * [[ForbiddenException]]. + * + * @param requestingUser the requesting user. + * @param requestedUserIri the IRI of the requested user. + * @param projectIri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[UserADM]] representing the requested user. + */ def switchToUser( - requestingUser: UserADM, - requestedUserIri: IRI, - projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - responderManager: ActorRef)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[UserADM] = { + requestingUser: UserADM, + requestedUserIri: IRI, + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + responderManager: ActorRef + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[UserADM] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance if (requestingUser.id == requestedUserIri) { FastFuture.successful(requestingUser) } else if (!(requestingUser.permissions.isSystemAdmin || requestingUser.permissions.isProjectAdmin(projectIri))) { - Future.failed(ForbiddenException( - s"You are logged in as ${requestingUser.username}, but only a system administrator or project administrator can perform an operation as another user")) + Future.failed( + ForbiddenException( + s"You are logged in as ${requestingUser.username}, but only a system administrator or project administrator can perform an operation as another user" + ) + ) } else { for { userResponse: UserResponseADM <- (responderManager ? UserGetRequestADM( diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ValueUtilV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ValueUtilV1.scala index 15a6aa58dc..b76242da93 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ValueUtilV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ValueUtilV1.scala @@ -48,26 +48,27 @@ import org.knora.webapi.settings.KnoraSettingsImpl import scala.concurrent.{ExecutionContext, Future} /** - * Converts data from SPARQL query results into [[ApiValueV1]] objects. - */ + * Converts data from SPARQL query results into [[ApiValueV1]] objects. + */ class ValueUtilV1(private val settings: KnoraSettingsImpl) { private val stringFormatter = StringFormatter.getGeneralInstance /** - * Given a [[ValueProps]] containing details of a `knora-base:Value` object, creates a [[ApiValueV1]]. - * - * @param valueProps a [[GroupedProps.ValueProps]] resulting from querying the `Value`, in which the keys are RDF predicates, - * and the values are lists of the objects of each predicate. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[ApiValueV1]] representing the `Value`. - */ + * Given a [[ValueProps]] containing details of a `knora-base:Value` object, creates a [[ApiValueV1]]. + * + * @param valueProps a [[GroupedProps.ValueProps]] resulting from querying the `Value`, in which the keys are RDF predicates, + * and the values are lists of the objects of each predicate. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[ApiValueV1]] representing the `Value`. + */ def makeValueV1( - valueProps: ValueProps, - projectShortcode: String, - responderManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + projectShortcode: String, + responderManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val valueTypeIri = valueProps.literalData(OntologyConstants.Rdf.Type).literals.head valueTypeIri match { @@ -98,59 +99,53 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { } } - def makeSipiImagePreviewGetUrlFromFilename(projectShortcode: String, filename: String): String = { + def makeSipiImagePreviewGetUrlFromFilename(projectShortcode: String, filename: String): String = s"${settings.externalSipiIIIFGetUrl}/$projectShortcode/$filename/full/!128,128/0/default.jpg" - } /** - * Creates a IIIF URL for accessing an image file via Sipi. - * - * @param imageFileValueV1 the image file value representing the image. - * @return a Sipi IIIF URL. - */ - def makeSipiImageGetUrlFromFilename(imageFileValueV1: StillImageFileValueV1): String = { + * Creates a IIIF URL for accessing an image file via Sipi. + * + * @param imageFileValueV1 the image file value representing the image. + * @return a Sipi IIIF URL. + */ + def makeSipiImageGetUrlFromFilename(imageFileValueV1: StillImageFileValueV1): String = s"${settings.externalSipiIIIFGetUrl}/${imageFileValueV1.projectShortcode}/${imageFileValueV1.internalFilename}/full/${imageFileValueV1.dimX},${imageFileValueV1.dimY}/0/default.jpg" - } /** - * Creates a URL for accessing a document file via Sipi. - * - * @param documentFileValueV1 the document file value. - * @return a Sipi URL. - */ - def makeSipiDocumentGetUrlFromFilename(documentFileValueV1: DocumentFileValueV1): String = { + * Creates a URL for accessing a document file via Sipi. + * + * @param documentFileValueV1 the document file value. + * @return a Sipi URL. + */ + def makeSipiDocumentGetUrlFromFilename(documentFileValueV1: DocumentFileValueV1): String = s"${settings.externalSipiIIIFGetUrl}/${documentFileValueV1.projectShortcode}/${documentFileValueV1.internalFilename}/file" - } /** - * Creates a URL for accessing a text file via Sipi. - * - * @param textFileValue the text file value representing the text file. - * @return a Sipi URL. - */ - def makeSipiTextFileGetUrlFromFilename(textFileValue: TextFileValueV1): String = { + * Creates a URL for accessing a text file via Sipi. + * + * @param textFileValue the text file value representing the text file. + * @return a Sipi URL. + */ + def makeSipiTextFileGetUrlFromFilename(textFileValue: TextFileValueV1): String = s"${settings.externalSipiBaseUrl}/${textFileValue.projectShortcode}/${textFileValue.internalFilename}" - } /** - * Creates a URL for accessing an audio file via Sipi. - * - * @param audioFileValue the file value representing the audio file. - * @return a Sipi URL. - */ - def makeSipiAudioFileGetUrlFromFilename(audioFileValue: AudioFileValueV1): String = { + * Creates a URL for accessing an audio file via Sipi. + * + * @param audioFileValue the file value representing the audio file. + * @return a Sipi URL. + */ + def makeSipiAudioFileGetUrlFromFilename(audioFileValue: AudioFileValueV1): String = s"${settings.externalSipiIIIFGetUrl}/${audioFileValue.projectShortcode}/${audioFileValue.internalFilename}/file" - } /** - * Creates a URL for accessing a video file via Sipi. - * - * @param videoFileValue the file value representing the video file. - * @return a Sipi URL. - */ - def makeSipiVideoFileGetUrlFromFilename(videoFileValue: MovingImageFileValueV1): String = { + * Creates a URL for accessing a video file via Sipi. + * + * @param videoFileValue the file value representing the video file. + * @return a Sipi URL. + */ + def makeSipiVideoFileGetUrlFromFilename(videoFileValue: MovingImageFileValueV1): String = s"${settings.externalSipiIIIFGetUrl}/${videoFileValue.projectShortcode}/${videoFileValue.internalFilename}/file" - } // A Map of MIME types to Knora API v1 binary format name. private val mimeType2V1Format = new ErrorHandlingMap( @@ -183,19 +178,20 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { "audio/x-wav" -> "AUDIO", "audio/vnd.wave" -> "AUDIO", "video/mp4" -> "VIDEO" - ), { key: String => + ), + { key: String => s"Unknown MIME type: $key" } ) /** - * Converts a [[FileValueV1]] (which is used internally by the Knora API server) to a [[LocationV1]] (which is - * used in certain API responses). - * - * @param fileValueV1 a [[FileValueV1]]. - * @return a [[LocationV1]]. - */ - def fileValueV12LocationV1(fileValueV1: FileValueV1): LocationV1 = { + * Converts a [[FileValueV1]] (which is used internally by the Knora API server) to a [[LocationV1]] (which is + * used in certain API responses). + * + * @param fileValueV1 a [[FileValueV1]]. + * @return a [[LocationV1]]. + */ + def fileValueV12LocationV1(fileValueV1: FileValueV1): LocationV1 = fileValueV1 match { case stillImageFileValueV1: StillImageFileValueV1 => LocationV1( @@ -238,19 +234,18 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { case otherType => throw NotImplementedException(s"Type not yet implemented: ${otherType.valueTypeIri}") } - } /** - * Creates a URL pointing to the given resource class icon. From the resource class IRI it gets the ontology specific path, i.e. the ontology name. - * If the resource class IRI is "http://www.knora.org/ontology/knora-base#Region", the ontology name would be "knora-base". - * To the base path, the icon name is appended. In case of a region with the icon name "region.gif", - * "http://salsahapp:port/project-icons-basepath/knora-base/region.gif" is returned. - * - * This method requires the IRI segment before the last slash to be a unique identifier for all the ontologies used with Knora.. - * - * @param resourceClassIri the IRI of the resource class in question. - * @param iconsSrc the name of the icon file. - */ + * Creates a URL pointing to the given resource class icon. From the resource class IRI it gets the ontology specific path, i.e. the ontology name. + * If the resource class IRI is "http://www.knora.org/ontology/knora-base#Region", the ontology name would be "knora-base". + * To the base path, the icon name is appended. In case of a region with the icon name "region.gif", + * "http://salsahapp:port/project-icons-basepath/knora-base/region.gif" is returned. + * + * This method requires the IRI segment before the last slash to be a unique identifier for all the ontologies used with Knora.. + * + * @param resourceClassIri the IRI of the resource class in question. + * @param iconsSrc the name of the icon file. + */ def makeResourceClassIconURL(resourceClassIri: IRI, iconsSrc: String): IRI = { // get ontology name, e.g. "knora-base" from "http://www.knora.org/ontology/knora-base#Region" // add +1 to ignore the slash @@ -262,77 +257,86 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { } /** - * Creates [[ValueProps]] from a List of [[VariableResultsRow]] representing a value object - * (the triples where the given value object is the subject in). - * - * A [[VariableResultsRow]] is expected to have the following members (SPARQL variable names): - * - * - objPred: the object predicate (e.g. http://www.knora.org/ontology/knora-base#valueHasString). - * - objObj: The string representation of the value assigned to objPred. - * - * In one given row, objPred **must** indicate the type of the given value object using rdfs:type (e.g. http://www.knora.org/ontology/knora-base#TextValue) - * - * In case the given value object contains standoff (objPred is http://www.knora.org/ontology/knora-base#valueHasStandoff), - * it has the following additional members compared those mentioned above: - * - * - predStandoff: the standoff predicate (e.g. http://www.knora.org/ontology/knora-base#standoffHasStart) - * - objStandoff: the string representation of the value assigned to predStandoff - * - * @param valueIri the IRI of the value that was queried. - * @param objRows SPARQL results. - * @return a [[ValueProps]] representing the SPARQL results. - */ + * Creates [[ValueProps]] from a List of [[VariableResultsRow]] representing a value object + * (the triples where the given value object is the subject in). + * + * A [[VariableResultsRow]] is expected to have the following members (SPARQL variable names): + * + * - objPred: the object predicate (e.g. http://www.knora.org/ontology/knora-base#valueHasString). + * - objObj: The string representation of the value assigned to objPred. + * + * In one given row, objPred **must** indicate the type of the given value object using rdfs:type (e.g. http://www.knora.org/ontology/knora-base#TextValue) + * + * In case the given value object contains standoff (objPred is http://www.knora.org/ontology/knora-base#valueHasStandoff), + * it has the following additional members compared those mentioned above: + * + * - predStandoff: the standoff predicate (e.g. http://www.knora.org/ontology/knora-base#standoffHasStart) + * - objStandoff: the string representation of the value assigned to predStandoff + * + * @param valueIri the IRI of the value that was queried. + * @param objRows SPARQL results. + * @return a [[ValueProps]] representing the SPARQL results. + */ def createValueProps(valueIri: IRI, objRows: Seq[VariableResultsRow]): ValueProps = { val groupedValueObject = groupKnoraValueObjectPredicateRows(objRows.map(_.rowMap)) - ValueProps(valueIri, new ErrorHandlingMap(groupedValueObject.valuesLiterals, { key: IRI => - s"Predicate $key not found in value $valueIri" - }), groupedValueObject.standoff) + ValueProps( + valueIri, + new ErrorHandlingMap( + groupedValueObject.valuesLiterals, + { key: IRI => + s"Predicate $key not found in value $valueIri" + } + ), + groupedValueObject.standoff + ) } /** - * Converts three lists of SPARQL query results representing all the properties of a resource into a [[GroupedPropertiesByType]]. - * - * Each [[VariableResultsRow]] is expected to have the following SPARQL variables: - * - * - prop: the IRI of the resource property (e.g. http://www.knora.org/ontology/knora-base#hasComment) - * - obj: the IRI of the object that the property points to, which may be either a value object (an ordinary value or a reification) or another resource - * - objPred: the IRI of each predicate of `obj` (e.g. for its literal contents, or for its permissions) - * - objObj: the object of each `objPred` - * - * The remaining members are identical to those documented in [[createValueProps]]. - * - * @param rowsWithOrdinaryValues SPARQL result rows describing properties that point to ordinary values (not link values). - * @param rowsWithLinkValues SPARQL result rows describing properties that point link values (reifications of links to resources). - * @param rowsWithLinks SPARQL result rows describing properties that point to resources. - * @return a [[GroupedPropertiesByType]] representing the SPARQL results. - */ - def createGroupedPropsByType(rowsWithOrdinaryValues: Seq[VariableResultsRow], - rowsWithLinkValues: Seq[VariableResultsRow], - rowsWithLinks: Seq[VariableResultsRow]): GroupedPropertiesByType = { + * Converts three lists of SPARQL query results representing all the properties of a resource into a [[GroupedPropertiesByType]]. + * + * Each [[VariableResultsRow]] is expected to have the following SPARQL variables: + * + * - prop: the IRI of the resource property (e.g. http://www.knora.org/ontology/knora-base#hasComment) + * - obj: the IRI of the object that the property points to, which may be either a value object (an ordinary value or a reification) or another resource + * - objPred: the IRI of each predicate of `obj` (e.g. for its literal contents, or for its permissions) + * - objObj: the object of each `objPred` + * + * The remaining members are identical to those documented in [[createValueProps]]. + * + * @param rowsWithOrdinaryValues SPARQL result rows describing properties that point to ordinary values (not link values). + * @param rowsWithLinkValues SPARQL result rows describing properties that point link values (reifications of links to resources). + * @param rowsWithLinks SPARQL result rows describing properties that point to resources. + * @return a [[GroupedPropertiesByType]] representing the SPARQL results. + */ + def createGroupedPropsByType( + rowsWithOrdinaryValues: Seq[VariableResultsRow], + rowsWithLinkValues: Seq[VariableResultsRow], + rowsWithLinks: Seq[VariableResultsRow] + ): GroupedPropertiesByType = GroupedPropertiesByType( groupedOrdinaryValueProperties = groupKnoraPropertyRows(rowsWithOrdinaryValues), groupedLinkValueProperties = groupKnoraPropertyRows(rowsWithLinkValues), groupedLinkProperties = groupKnoraPropertyRows(rowsWithLinks) ) - } /** - * Checks that a value type is valid for the `knora-base:objectClassConstraint` of a property. - * - * @param propertyIri the IRI of the property. - * @param valueType the IRI of the value type. - * @param propertyObjectClassConstraint the IRI of the property's `knora-base:objectClassConstraint`. - * @param responderManager a reference to the Knora API Server responder manager. - * @return A future containing Unit on success, or a failed future if the value type is not valid for the property's range. - */ + * Checks that a value type is valid for the `knora-base:objectClassConstraint` of a property. + * + * @param propertyIri the IRI of the property. + * @param valueType the IRI of the value type. + * @param propertyObjectClassConstraint the IRI of the property's `knora-base:objectClassConstraint`. + * @param responderManager a reference to the Knora API Server responder manager. + * @return A future containing Unit on success, or a failed future if the value type is not valid for the property's range. + */ def checkValueTypeForPropertyObjectClassConstraint( - propertyIri: IRI, - valueType: IRI, - propertyObjectClassConstraint: IRI, - responderManager: ActorRef, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Unit] = { + propertyIri: IRI, + valueType: IRI, + propertyObjectClassConstraint: IRI, + responderManager: ActorRef, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Unit] = if (propertyObjectClassConstraint == valueType) { Future.successful(()) } else { @@ -345,28 +349,29 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { _ = if (!checkSubClassResponse.isSubClass) { throw OntologyConstraintException( - s"Property $propertyIri requires a value of type $propertyObjectClassConstraint") + s"Property $propertyIri requires a value of type $propertyObjectClassConstraint" + ) } } yield () } - } /** - * Converts a [[CreateValueResponseV1]] returned by the values responder on value creation - * to the expected format for the resources responder [[ResourceCreateValueResponseV1]], which describes a value - * added to a new resource. - * - * @param resourceIri the IRI of the created resource. - * @param creatorIri the creator of the resource. - * @param propertyIri the property the valueResponse belongs to. - * @param valueResponse the value that has been attached to the resource. - * @return a [[ResourceCreateValueResponseV1]] representing the created value. - */ + * Converts a [[CreateValueResponseV1]] returned by the values responder on value creation + * to the expected format for the resources responder [[ResourceCreateValueResponseV1]], which describes a value + * added to a new resource. + * + * @param resourceIri the IRI of the created resource. + * @param creatorIri the creator of the resource. + * @param propertyIri the property the valueResponse belongs to. + * @param valueResponse the value that has been attached to the resource. + * @return a [[ResourceCreateValueResponseV1]] representing the created value. + */ def convertCreateValueResponseV1ToResourceCreateValueResponseV1( - resourceIri: IRI, - creatorIri: IRI, - propertyIri: IRI, - valueResponse: CreateValueResponseV1): ResourceCreateValueResponseV1 = { + resourceIri: IRI, + creatorIri: IRI, + propertyIri: IRI, + valueResponse: CreateValueResponseV1 + ): ResourceCreateValueResponseV1 = { val basicObjectResponse = ResourceCreateValueObjectResponseV1( textval = Map(LiteralValueType.StringValue -> valueResponse.value.toString), @@ -443,110 +448,116 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { } /** - * Creates a tuple that can be turned into a [[ValueProps]] representing both literal values and standoff. - * - * It expects the members documented in [[createValueProps]]. - * - * @param objRows a value object's predicates. - * @return a [[GroupedValueObject]] containing the values (literal or linking) and standoff nodes if given. - */ + * Creates a tuple that can be turned into a [[ValueProps]] representing both literal values and standoff. + * + * It expects the members documented in [[createValueProps]]. + * + * @param objRows a value object's predicates. + * @return a [[GroupedValueObject]] containing the values (literal or linking) and standoff nodes if given. + */ private def groupKnoraValueObjectPredicateRows(objRows: Seq[Map[String, String]]): GroupedValueObject = { // get rid of the value object IRI `obj` and group by predicate IRI `objPred` (e.g. `valueHasString`) val valuesGroupedByPredicate = objRows.map(_ - "obj").groupBy(_("objPred")) valuesGroupedByPredicate.foldLeft( - GroupedValueObject(valuesLiterals = Map.empty[String, ValueLiterals], - standoff = Map.empty[IRI, Map[IRI, String]])) { - case (acc: GroupedValueObject, (objPredIri: IRI, values: Seq[Map[String, String]])) => - if (objPredIri == OntologyConstants.KnoraBase.ValueHasStandoff) { - // standoff information - - val groupedByStandoffNodeIri: Map[IRI, Seq[Map[String, String]]] = values.groupBy(_("objObj")) - - val standoffNodeAssertions: Map[IRI, Map[String, String]] = groupedByStandoffNodeIri.map { - case (standoffNodeIri: IRI, values: Seq[Map[String, String]]) => - val valuesMap: Map[String, String] = values - .map { - // make a Map with the standoffPred as the key and the objStandoff as the value - value: Map[String, String] => - Map(value("predStandoff") -> value("objStandoff")) - } - .foldLeft(Map.empty[String, String]) { - // for each standoff node, we want to have just one Map - // this foldLeft turns a Sequence of Maps into one Map (a predicate can only occur once) - case (nodeValues: Map[String, String], value: Map[String, String]) => - nodeValues ++ value - } - - standoffNodeIri -> valuesMap + GroupedValueObject(valuesLiterals = Map.empty[String, ValueLiterals], standoff = Map.empty[IRI, Map[IRI, String]]) + ) { case (acc: GroupedValueObject, (objPredIri: IRI, values: Seq[Map[String, String]])) => + if (objPredIri == OntologyConstants.KnoraBase.ValueHasStandoff) { + // standoff information + + val groupedByStandoffNodeIri: Map[IRI, Seq[Map[String, String]]] = values.groupBy(_("objObj")) + + val standoffNodeAssertions: Map[IRI, Map[String, String]] = groupedByStandoffNodeIri.map { + case (standoffNodeIri: IRI, values: Seq[Map[String, String]]) => + val valuesMap: Map[String, String] = values.map { + // make a Map with the standoffPred as the key and the objStandoff as the value + value: Map[String, String] => + Map(value("predStandoff") -> value("objStandoff")) + } + .foldLeft(Map.empty[String, String]) { + // for each standoff node, we want to have just one Map + // this foldLeft turns a Sequence of Maps into one Map (a predicate can only occur once) + case (nodeValues: Map[String, String], value: Map[String, String]) => + nodeValues ++ value + } + + standoffNodeIri -> valuesMap - } + } - acc.copy( - standoff = acc.standoff ++ standoffNodeAssertions - ) + acc.copy( + standoff = acc.standoff ++ standoffNodeAssertions + ) - } else { - // non standoff value + } else { + // non standoff value - val value: (String, ValueLiterals) = (objPredIri, ValueLiterals(values.map { value: Map[String, String] => + val value: (String, ValueLiterals) = ( + objPredIri, + ValueLiterals(values.map { value: Map[String, String] => value("objObj") - })) + }) + ) - acc.copy( - valuesLiterals = acc.valuesLiterals + value - ) + acc.copy( + valuesLiterals = acc.valuesLiterals + value + ) - } + } } } /** - * - * Given a list of result rows from the `get-resource-properties-and-values` SPARQL query, groups the rows first by property, - * then by property object, and finally by property object predicate. In case the results contain standoff information, the standoff nodes are grouped - * according to their blank node IRI. If the first row of results has a `linkValue` column, this is taken to mean that the property - * is a link property and that the value of `linkValue` is the IRI of the corresponding `knora-base:LinkValue`; that IRI is then - * added to the literals in the results, with the key [[OntologyConstants.KnoraBase.LinkValue]]. - * - * For example, suppose we have the following rows for a property that points to Knora values. - * - * {{{ - * prop obj objPred objObj - * --------------------------------------------------------------------------------------------------------------------------------------------------- - * incunabula:pagenum http://rdfh.ch/8a0b1e75/values/61cb927602 knora-base:valueHasString a1r, Titelblatt - * incunabula:pagenum http://rdfh.ch/8a0b1e75/values/61cb927602 knora-base:hasViewPermission knora-base:KnownUser - * incunabula:pagenum http://rdfh.ch/8a0b1e75/values/61cb927602 knora-base:hasViewPermission knora-base:UnknownUser - * }}} - * - * The result will be a [[GroupedProperties]] containing a [[ValueProps]] with two keys, `valueHasString` and `hasPermission`. - * - * @param rows the SPARQL query result rows to group, which are expected to contain the columns given in the description of the `createGroupedPropsByType` - * method. - * @return a [[GroupedProperties]] representing the SPARQL results. - */ + * Given a list of result rows from the `get-resource-properties-and-values` SPARQL query, groups the rows first by property, + * then by property object, and finally by property object predicate. In case the results contain standoff information, the standoff nodes are grouped + * according to their blank node IRI. If the first row of results has a `linkValue` column, this is taken to mean that the property + * is a link property and that the value of `linkValue` is the IRI of the corresponding `knora-base:LinkValue`; that IRI is then + * added to the literals in the results, with the key [[OntologyConstants.KnoraBase.LinkValue]]. + * + * For example, suppose we have the following rows for a property that points to Knora values. + * + * {{{ + * prop obj objPred objObj + * --------------------------------------------------------------------------------------------------------------------------------------------------- + * incunabula:pagenum http://rdfh.ch/8a0b1e75/values/61cb927602 knora-base:valueHasString a1r, Titelblatt + * incunabula:pagenum http://rdfh.ch/8a0b1e75/values/61cb927602 knora-base:hasViewPermission knora-base:KnownUser + * incunabula:pagenum http://rdfh.ch/8a0b1e75/values/61cb927602 knora-base:hasViewPermission knora-base:UnknownUser + * }}} + * + * The result will be a [[GroupedProperties]] containing a [[ValueProps]] with two keys, `valueHasString` and `hasPermission`. + * + * @param rows the SPARQL query result rows to group, which are expected to contain the columns given in the description of the `createGroupedPropsByType` + * method. + * @return a [[GroupedProperties]] representing the SPARQL results. + */ private def groupKnoraPropertyRows(rows: Seq[VariableResultsRow]): GroupedProperties = { val gp: Map[String, ValueObjects] = rows.groupBy(_.rowMap("prop")).map { // grouped by resource property (e.g. hasComment) case (resProp: String, rows: Seq[VariableResultsRow]) => - val vo = (resProp, rows.map(_.rowMap - "prop").groupBy(_("obj")).map { - // grouped by value object IRI - case (objIri: IRI, objRows: Seq[Map[String, String]]) => - val groupedValueObject = groupKnoraValueObjectPredicateRows(objRows) - - val vp: ValueProps = ValueProps( - valueIri = objIri, - new ErrorHandlingMap(groupedValueObject.valuesLiterals, { key: IRI => - s"Predicate $key not found for property object $objIri" - }), - groupedValueObject.standoff - ) - - (objIri, vp) - }) + val vo = ( + resProp, + rows.map(_.rowMap - "prop").groupBy(_("obj")).map { + // grouped by value object IRI + case (objIri: IRI, objRows: Seq[Map[String, String]]) => + val groupedValueObject = groupKnoraValueObjectPredicateRows(objRows) + + val vp: ValueProps = ValueProps( + valueIri = objIri, + new ErrorHandlingMap( + groupedValueObject.valuesLiterals, + { key: IRI => + s"Predicate $key not found for property object $objIri" + } + ), + groupedValueObject.standoff + ) + + (objIri, vp) + } + ) (resProp, GroupedProps.ValueObjects(vo._2)) } @@ -555,70 +566,75 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { } /** - * Converts a [[ValueProps]] into an [[IntegerValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return an [[IntegerValueV1]]. - */ - private def makeIntValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into an [[IntegerValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return an [[IntegerValueV1]]. + */ + private def makeIntValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(IntegerValueV1(predicates(OntologyConstants.KnoraBase.ValueHasInteger).literals.head.toInt)) } /** - * Converts a [[ValueProps]] into a [[DecimalValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[DecimalValueV1]]. - */ - private def makeDecimalValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[DecimalValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[DecimalValueV1]]. + */ + private def makeDecimalValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(DecimalValueV1(BigDecimal(predicates(OntologyConstants.KnoraBase.ValueHasDecimal).literals.head))) } /** - * Converts a [[ValueProps]] into a [[BooleanValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[BooleanValueV1]]. - */ - private def makeBooleanValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[BooleanValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[BooleanValueV1]]. + */ + private def makeBooleanValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(BooleanValueV1(predicates(OntologyConstants.KnoraBase.ValueHasBoolean).literals.head.toBoolean)) } /** - * Converts a [[ValueProps]] into a [[UriValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[UriValueV1]]. - */ - private def makeUriValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[UriValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[UriValueV1]]. + */ + private def makeUriValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(UriValueV1(predicates(OntologyConstants.KnoraBase.ValueHasUri).literals.head)) } /** - * Converts a [[ValueProps]] into a [[DateValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[DateValueV1]]. - */ - private def makeDateValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[DateValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[DateValueV1]]. + */ + private def makeDateValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData val julianDayNumberValueV1 = JulianDayNumberValueV1( @@ -635,32 +651,35 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { } /** - * Converts a [[ValueProps]] into an [[IntervalValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return an [[IntervalValueV1]]. - */ - private def makeIntervalValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into an [[IntervalValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return an [[IntervalValueV1]]. + */ + private def makeIntervalValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( IntervalValueV1( timeval1 = BigDecimal(predicates(OntologyConstants.KnoraBase.ValueHasIntervalStart).literals.head), timeval2 = BigDecimal(predicates(OntologyConstants.KnoraBase.ValueHasIntervalEnd).literals.head) - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[TimeValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[TimeValueV1]]. - */ - private def makeTimeValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[TimeValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[TimeValueV1]]. + */ + private def makeTimeValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData val timeStampStr = predicates(OntologyConstants.KnoraBase.ValueHasTimeStamp).literals.head @@ -668,35 +687,38 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { TimeValueV1( timeStamp = stringFormatter.xsdDateTimeStampToInstant( timeStampStr, - throw InconsistentRepositoryDataException(s"Can't parse timestamp: $timeStampStr")) - )) + throw InconsistentRepositoryDataException(s"Can't parse timestamp: $timeStampStr") + ) + ) + ) } /** - * Creates a [[TextValueWithStandoffV1]] from the given string and the standoff nodes. - * - * @param utf8str the string representation. - * @param valueProps the properties of the TextValue with standoff. - * @param responderManager the responder manager. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the client that is making the request. - * @return a [[TextValueWithStandoffV1]]. - */ - private def makeTextValueWithStandoff(utf8str: String, - language: Option[String] = None, - valueProps: ValueProps, - responderManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TextValueWithStandoffV1] = { + * Creates a [[TextValueWithStandoffV1]] from the given string and the standoff nodes. + * + * @param utf8str the string representation. + * @param valueProps the properties of the TextValue with standoff. + * @param responderManager the responder manager. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the client that is making the request. + * @return a [[TextValueWithStandoffV1]]. + */ + private def makeTextValueWithStandoff( + utf8str: String, + language: Option[String] = None, + valueProps: ValueProps, + responderManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TextValueWithStandoffV1] = { // get the IRI of the mapping val mappingIri = valueProps.literalData .getOrElse( OntologyConstants.KnoraBase.ValueHasMapping, throw InconsistentRepositoryDataException( - s"no mapping IRI associated with standoff belonging to textValue ${valueProps.valueIri}") + s"no mapping IRI associated with standoff belonging to textValue ${valueProps.valueIri}" + ) ) .literals .head @@ -717,52 +739,54 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { requestingUser = userProfile ) - } yield - TextValueWithStandoffV1( - utf8str = utf8str, - language = language, - standoff = standoffTags, - mappingIri = mappingIri, - mapping = mappingResponse.mapping, - resource_reference = stringFormatter.getResourceIrisFromStandoffTags(standoffTags) - ) + } yield TextValueWithStandoffV1( + utf8str = utf8str, + language = language, + standoff = standoffTags, + mappingIri = mappingIri, + mapping = mappingResponse.mapping, + resource_reference = stringFormatter.getResourceIrisFromStandoffTags(standoffTags) + ) } /** - * Creates a [[TextValueSimpleV1]] from the given string. - * - * @param utf8str the string representation of the TextValue. - * @return a [[TextValueSimpleV1]]. - */ - private def makeTextValueSimple(utf8str: String, language: Option[String] = None)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TextValueSimpleV1] = { + * Creates a [[TextValueSimpleV1]] from the given string. + * + * @param utf8str the string representation of the TextValue. + * @return a [[TextValueSimpleV1]]. + */ + private def makeTextValueSimple(utf8str: String, language: Option[String] = None)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[TextValueSimpleV1] = Future( TextValueSimpleV1( utf8str = utf8str, language = language - )) - } + ) + ) /** - * Converts a [[ValueProps]] into a [[TextValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[TextValueV1]]. - */ + * Converts a [[ValueProps]] into a [[TextValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[TextValueV1]]. + */ private def makeTextValue( - valueProps: ValueProps, - responderManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + responderManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val valueHasString: String = valueProps.literalData .get(OntologyConstants.KnoraBase.ValueHasString) .map(_.literals.head) .getOrElse( - throw InconsistentRepositoryDataException(s"Value ${valueProps.valueIri} has no knora-base:valueHasString")) + throw InconsistentRepositoryDataException(s"Value ${valueProps.valueIri} has no knora-base:valueHasString") + ) val valueHasLanguage: Option[String] = valueProps.literalData.get(OntologyConstants.KnoraBase.ValueHasLanguage).map(_.literals.head) @@ -785,58 +809,62 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { } /** - * Converts a [[ValueProps]] into a [[ColorValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[ColorValueV1]]. - */ - private def makeColorValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[ColorValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[ColorValueV1]]. + */ + private def makeColorValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(ColorValueV1(predicates(OntologyConstants.KnoraBase.ValueHasColor).literals.head)) } /** - * Converts a [[ValueProps]] into a [[GeomValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[GeomValueV1]]. - */ - private def makeGeomValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[GeomValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[GeomValueV1]]. + */ + private def makeGeomValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(GeomValueV1(predicates(OntologyConstants.KnoraBase.ValueHasGeometry).literals.head)) } /** - * Converts a [[ValueProps]] into a [[HierarchicalListValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[HierarchicalListValueV1]]. - */ - private def makeListValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[HierarchicalListValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[HierarchicalListValueV1]]. + */ + private def makeListValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(HierarchicalListValueV1(predicates(OntologyConstants.KnoraBase.ValueHasListNode).literals.head)) } /** - * Converts a [[ValueProps]] into a [[StillImageFileValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[StillImageFileValueV1]]. - */ + * Converts a [[ValueProps]] into a [[StillImageFileValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[StillImageFileValueV1]]. + */ private def makeStillImageValue( - valueProps: ValueProps, - projectShortcode: String, - responderManager: ActorRef, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + projectShortcode: String, + responderManager: ActorRef, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( @@ -847,20 +875,22 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { projectShortcode = projectShortcode, dimX = predicates(OntologyConstants.KnoraBase.DimX).literals.head.toInt, dimY = predicates(OntologyConstants.KnoraBase.DimY).literals.head.toInt - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[TextFileValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[TextFileValueV1]]. - */ + * Converts a [[ValueProps]] into a [[TextFileValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[TextFileValueV1]]. + */ private def makeTextFileValue( - valueProps: ValueProps, - projectShortcode: String, - responderManager: ActorRef, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + projectShortcode: String, + responderManager: ActorRef, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( @@ -869,20 +899,22 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { internalFilename = predicates(OntologyConstants.KnoraBase.InternalFilename).literals.head, originalFilename = predicates.get(OntologyConstants.KnoraBase.OriginalFilename).map(_.literals.head), projectShortcode = projectShortcode - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[DocumentFileValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[DocumentFileValueV1]]. - */ + * Converts a [[ValueProps]] into a [[DocumentFileValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[DocumentFileValueV1]]. + */ private def makeDocumentFileValue( - valueProps: ValueProps, - projectShortcode: String, - responderManager: ActorRef, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + projectShortcode: String, + responderManager: ActorRef, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( @@ -894,20 +926,22 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { pageCount = predicates.get(OntologyConstants.KnoraBase.PageCount).flatMap(_.literals.headOption.map(_.toInt)), dimX = predicates.get(OntologyConstants.KnoraBase.DimX).flatMap(_.literals.headOption.map(_.toInt)), dimY = predicates.get(OntologyConstants.KnoraBase.DimY).flatMap(_.literals.headOption.map(_.toInt)) - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[AudioFileValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[AudioFileValueV1]]. - */ + * Converts a [[ValueProps]] into a [[AudioFileValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[AudioFileValueV1]]. + */ private def makeAudioFileValue( - valueProps: ValueProps, - projectShortcode: String, - responderManager: ActorRef, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + projectShortcode: String, + responderManager: ActorRef, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( @@ -919,20 +953,22 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { duration = predicates .get(OntologyConstants.KnoraBase.Duration) .map(valueLiterals => BigDecimal(valueLiterals.literals.head)) - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[MovingImageFileValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[MovingImageFileValueV1]]. - */ + * Converts a [[ValueProps]] into a [[MovingImageFileValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[MovingImageFileValueV1]]. + */ private def makeVideoFileValue( - valueProps: ValueProps, - projectShortcode: String, - responderManager: ActorRef, - userProfile: UserADM)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { + valueProps: ValueProps, + projectShortcode: String, + responderManager: ActorRef, + userProfile: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( @@ -949,18 +985,20 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { duration = predicates .get(OntologyConstants.KnoraBase.Duration) .map(valueLiterals => BigDecimal(valueLiterals.literals.head)) - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[LinkValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[LinkValueV1]]. - */ - private def makeLinkValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[LinkValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[LinkValueV1]]. + */ + private def makeLinkValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future( @@ -969,104 +1007,109 @@ class ValueUtilV1(private val settings: KnoraSettingsImpl) { predicateIri = predicates(OntologyConstants.Rdf.Predicate).literals.head, objectIri = predicates(OntologyConstants.Rdf.Object).literals.head, referenceCount = predicates(OntologyConstants.KnoraBase.ValueHasRefCount).literals.head.toInt - )) + ) + ) } /** - * Converts a [[ValueProps]] into a [[GeonameValueV1]]. - * - * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. - * @return a [[GeonameValueV1]]. - */ - private def makeGeonameValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ApiValueV1] = { + * Converts a [[ValueProps]] into a [[GeonameValueV1]]. + * + * @param valueProps a [[ValueProps]] representing the SPARQL query results to be converted. + * @return a [[GeonameValueV1]]. + */ + private def makeGeonameValue(valueProps: ValueProps, responderManager: ActorRef, userProfile: UserADM)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[ApiValueV1] = { val predicates = valueProps.literalData Future(GeonameValueV1(predicates(OntologyConstants.KnoraBase.ValueHasGeonameCode).literals.head)) } - /** Creates an attribute segment for the Salsah GUI from the given resource class. - * Example: if "http://www.knora.org/ontology/0803/incunabula#book" is given, the function returns "restypeid=http://www.knora.org/ontology/0803/incunabula#book". - * - * @param resourceClass the resource class. - * @return an attribute string to be included in the attributes for the GUI - */ - def makeAttributeRestype(resourceClass: IRI): String = { + /** + * Creates an attribute segment for the Salsah GUI from the given resource class. + * Example: if "http://www.knora.org/ontology/0803/incunabula#book" is given, the function returns "restypeid=http://www.knora.org/ontology/0803/incunabula#book". + * + * @param resourceClass the resource class. + * @return an attribute string to be included in the attributes for the GUI + */ + def makeAttributeRestype(resourceClass: IRI): String = "restypeid=" + resourceClass - } /** - * Given a set of attribute segments representing assertions about the values of [[OntologyConstants.SalsahGui.GuiAttribute]] for a property, - * combines the attributes into a string for use in an API v1 response. - * - * @param attributes the values of [[OntologyConstants.SalsahGui.GuiAttribute]] for a property. - * @return a semicolon-delimited string containing the attributes, or [[None]] if no attributes were found. - */ - def makeAttributeString(attributes: Set[String]): Option[String] = { + * Given a set of attribute segments representing assertions about the values of [[OntologyConstants.SalsahGui.GuiAttribute]] for a property, + * combines the attributes into a string for use in an API v1 response. + * + * @param attributes the values of [[OntologyConstants.SalsahGui.GuiAttribute]] for a property. + * @return a semicolon-delimited string containing the attributes, or [[None]] if no attributes were found. + */ + def makeAttributeString(attributes: Set[String]): Option[String] = if (attributes.isEmpty) { None } else { Some(attributes.toVector.sorted.mkString(";")) } - } } /** - * Represents SPARQL results to be converted into [[ApiValueV1]] objects. - */ + * Represents SPARQL results to be converted into [[ApiValueV1]] objects. + */ object GroupedProps { /** - * Contains the three types of [[GroupedProperties]] returned by a SPARQL query. - * - * @param groupedOrdinaryValueProperties properties pointing to ordinary Knora values (i.e. not link values). - * @param groupedLinkValueProperties properties pointing to link value objects (reifications of links to resources). - * @param groupedLinkProperties properties pointing to resources. - */ - case class GroupedPropertiesByType(groupedOrdinaryValueProperties: GroupedProperties, - groupedLinkValueProperties: GroupedProperties, - groupedLinkProperties: GroupedProperties) + * Contains the three types of [[GroupedProperties]] returned by a SPARQL query. + * + * @param groupedOrdinaryValueProperties properties pointing to ordinary Knora values (i.e. not link values). + * @param groupedLinkValueProperties properties pointing to link value objects (reifications of links to resources). + * @param groupedLinkProperties properties pointing to resources. + */ + case class GroupedPropertiesByType( + groupedOrdinaryValueProperties: GroupedProperties, + groupedLinkValueProperties: GroupedProperties, + groupedLinkProperties: GroupedProperties + ) /** - * Represents the grouped properties of one of the three types. - * - * @param groupedProperties The grouped properties: The Map's keys (IRI) consist of resource properties (e.g. http://www.knora.org/ontology/knora-base#hasComment). - */ + * Represents the grouped properties of one of the three types. + * + * @param groupedProperties The grouped properties: The Map's keys (IRI) consist of resource properties (e.g. http://www.knora.org/ontology/knora-base#hasComment). + */ case class GroupedProperties(groupedProperties: Map[IRI, ValueObjects]) /** - * Represents the value objects belonging to a resource property - * - * @param valueObjects The value objects: The Map's keys consist of value object Iris. - */ + * Represents the value objects belonging to a resource property + * + * @param valueObjects The value objects: The Map's keys consist of value object Iris. + */ case class ValueObjects(valueObjects: Map[IRI, ValueProps]) /** - * Represents the grouped values of a value object. - * - * @param valuesLiterals the values (literal or linking). - * @param standoff standoff nodes, if any. - */ + * Represents the grouped values of a value object. + * + * @param valuesLiterals the values (literal or linking). + * @param standoff standoff nodes, if any. + */ case class GroupedValueObject(valuesLiterals: Map[String, ValueLiterals], standoff: Map[IRI, Map[IRI, String]]) /** - * Represents the object properties belonging to a value object - * - * @param valueIri the IRI of the value object. - * @param literalData the value properties: The Map's keys (IRI) consist of value object properties (e.g. http://www.knora.org/ontology/knora-base#String). - * @param standoff the keys of the first Map are the standoff node Iris, the second Map contains all the predicates and objects related to one standoff node. - */ - case class ValueProps(valueIri: IRI, - literalData: Map[IRI, ValueLiterals], - standoff: Map[IRI, Map[IRI, String]] = Map.empty[IRI, Map[IRI, String]]) + * Represents the object properties belonging to a value object + * + * @param valueIri the IRI of the value object. + * @param literalData the value properties: The Map's keys (IRI) consist of value object properties (e.g. http://www.knora.org/ontology/knora-base#String). + * @param standoff the keys of the first Map are the standoff node Iris, the second Map contains all the predicates and objects related to one standoff node. + */ + case class ValueProps( + valueIri: IRI, + literalData: Map[IRI, ValueLiterals], + standoff: Map[IRI, Map[IRI, String]] = Map.empty[IRI, Map[IRI, String]] + ) /** - * Represents the literal values of a property (e.g. a number or a string) - * - * @param literals the literal values of a property. - */ + * Represents the literal values of a property (e.g. a number or a string) + * + * @param literals the literal values of a property. + */ case class ValueLiterals(literals: Seq[String]) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/AbstractShaclValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/AbstractShaclValidator.scala index d7a1f57e04..f5dde86bdf 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/AbstractShaclValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/AbstractShaclValidator.scala @@ -25,18 +25,18 @@ import java.nio.file.attribute.BasicFileAttributes import org.knora.webapi.exceptions.AssertionException /** - * An abstract base class for classes that validate RDF models using SHACL shapes. - * - * @param baseDir the base directory that SHACL graphs are loaded from. - * @param rdfFormatUtil an [[RdfFormatUtil]]. - * @tparam ShaclGraphT an implementation-specific representation of a graph of SHACL shapes. - */ + * An abstract base class for classes that validate RDF models using SHACL shapes. + * + * @param baseDir the base directory that SHACL graphs are loaded from. + * @param rdfFormatUtil an [[RdfFormatUtil]]. + * @tparam ShaclGraphT an implementation-specific representation of a graph of SHACL shapes. + */ abstract class AbstractShaclValidator[ShaclGraphT](baseDir: Path, private val rdfFormatUtil: RdfFormatUtil) extends ShaclValidator { /** - * A map of relative paths to objects representing graphs of SHACL shapes. - */ + * A map of relative paths to objects representing graphs of SHACL shapes. + */ private val shaclGraphs: Map[Path, ShaclGraphT] = if (Files.exists(baseDir)) { val fileVisitor = new ShaclGraphCollectingFileVisitor Files.walkFileTree(baseDir, fileVisitor) @@ -45,16 +45,15 @@ abstract class AbstractShaclValidator[ShaclGraphT](baseDir: Path, private val rd Map.empty } - def validate(rdfModel: RdfModel, shaclPath: Path): ShaclValidationResult = { + def validate(rdfModel: RdfModel, shaclPath: Path): ShaclValidationResult = validateWithShaclGraph( rdfModel = rdfModel, shaclGraph = shaclGraphs.getOrElse(shaclPath, throw AssertionException(s"SHACL graph $shaclPath not found")) ) - } /** - * A [[FileVisitor]] that loads graphs of SHACL shapes while walking a file tree. - */ + * A [[FileVisitor]] that loads graphs of SHACL shapes while walking a file tree. + */ private class ShaclGraphCollectingFileVisitor extends SimpleFileVisitor[Path] { // A collection of the graphs that have been loaded so far. val visitedShaclGraphs: collection.mutable.Map[Path, ShaclGraphT] = collection.mutable.Map.empty @@ -80,19 +79,19 @@ abstract class AbstractShaclValidator[ShaclGraphT](baseDir: Path, private val rd } /** - * Validates the default graph of an [[RdfModel]] using a graph of SHACL shapes. - * - * @param rdfModel the [[RdfModel]] to be validated. - * @param shaclGraph a graph of SHACL shapes. - * @return the validation result. - */ + * Validates the default graph of an [[RdfModel]] using a graph of SHACL shapes. + * + * @param rdfModel the [[RdfModel]] to be validated. + * @param shaclGraph a graph of SHACL shapes. + * @return the validation result. + */ protected def validateWithShaclGraph(rdfModel: RdfModel, shaclGraph: ShaclGraphT): ShaclValidationResult /** - * Converts the default graph of an [[RdfModel]] to a [[ShaclGraphT]]. - * - * @param rdfModel an [[RdfModel]] whose default graph contains SHACL shapes. - * @return a [[ShaclGraphT]] representing the SHACL shapes. - */ + * Converts the default graph of an [[RdfModel]] to a [[ShaclGraphT]]. + * + * @param rdfModel an [[RdfModel]] whose default graph contains SHACL shapes. + * @return a [[ShaclGraphT]] representing the SHACL shapes. + */ protected def rdfModelToShaclGraph(rdfModel: RdfModel): ShaclGraphT } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/JsonLDUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/JsonLDUtil.scala index 3f3806dff0..160f404a5d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/JsonLDUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/JsonLDUtil.scala @@ -49,8 +49,8 @@ The implementation uses the javax.json API and a Java implementation of the JSON */ /** - * Constant keywords used in JSON-LD. - */ + * Constant keywords used in JSON-LD. + */ object JsonLDKeywords { val CONTEXT: String = "@context" @@ -65,85 +65,79 @@ object JsonLDKeywords { val VALUE: String = "@value" /** - * The set of JSON-LD keywords that are supported by [[JsonLDUtil]]. - */ + * The set of JSON-LD keywords that are supported by [[JsonLDUtil]]. + */ val allSupported: Set[String] = Set(CONTEXT, ID, TYPE, GRAPH, LANGUAGE, VALUE) } /** - * Represents a value in a JSON-LD document. - */ + * Represents a value in a JSON-LD document. + */ sealed trait JsonLDValue extends Ordered[JsonLDValue] { /** - * Converts this JSON-LD value to a `javax.json` [[JsonValue]]. - */ + * Converts this JSON-LD value to a `javax.json` [[JsonValue]]. + */ def toJavaxJsonValue: JsonValue } /** - * Represents a string value in a JSON-LD document. - * - * @param value the underlying string. - */ + * Represents a string value in a JSON-LD document. + * + * @param value the underlying string. + */ case class JsonLDString(value: String) extends JsonLDValue { - override def toJavaxJsonValue: JsonString = { + override def toJavaxJsonValue: JsonString = Json.createValue(value) - } - override def compare(that: JsonLDValue): Int = { + override def compare(that: JsonLDValue): Int = that match { case thatStr: JsonLDString => value.compare(thatStr.value) case _ => 0 } - } } /** - * Represents an integer value in a JSON-LD document. - * - * @param value the underlying integer. - */ + * Represents an integer value in a JSON-LD document. + * + * @param value the underlying integer. + */ case class JsonLDInt(value: Int) extends JsonLDValue { - override def toJavaxJsonValue: JsonNumber = { + override def toJavaxJsonValue: JsonNumber = Json.createValue(value) - } - override def compare(that: JsonLDValue): Int = { + override def compare(that: JsonLDValue): Int = that match { case thatInt: JsonLDInt => value.compare(thatInt.value) case _ => 0 } - } } /** - * Represents a boolean value in a JSON-LD document. - * - * @param value the underlying boolean value. - */ + * Represents a boolean value in a JSON-LD document. + * + * @param value the underlying boolean value. + */ case class JsonLDBoolean(value: Boolean) extends JsonLDValue { - override def toJavaxJsonValue: JsonValue = { + override def toJavaxJsonValue: JsonValue = if (value) { JsonValue.TRUE } else { JsonValue.FALSE } - } - override def compare(that: JsonLDValue): Int = { + override def compare(that: JsonLDValue): Int = that match { case thatBoolean: JsonLDBoolean => value.compare(thatBoolean.value) case _ => 0 } - } } /** - * Represents a JSON object in a JSON-LD document. - * - * @param value a map of keys to JSON-LD values. - */ + * Represents a JSON object in a JSON-LD document. + * + * @param value a map of keys to JSON-LD values. + */ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { override def toJavaxJsonValue: JsonObject = { @@ -157,43 +151,42 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Flattens this JSON-LD object by extracting inlined entities with IRIs and replacing them with - * references to their IRI. - * - * @param entitiesToAddToTopLevel inlined entities that have been extracted. - * @param isAtTopLevel `true` if this JSON-LD object is the top level object, or if it is an element - * of a `@graph`. - * @return a flattened copy of this JSON-LD object. - */ + * Flattens this JSON-LD object by extracting inlined entities with IRIs and replacing them with + * references to their IRI. + * + * @param entitiesToAddToTopLevel inlined entities that have been extracted. + * @param isAtTopLevel `true` if this JSON-LD object is the top level object, or if it is an element + * of a `@graph`. + * @return a flattened copy of this JSON-LD object. + */ def flattened(entitiesToAddToTopLevel: collection.mutable.Set[JsonLDObject], isAtTopLevel: Boolean): JsonLDObject = { val thisWithFlattenedContent = JsonLDObject { // Flatten the object of each predicate. - value.map { - case (pred: String, obj: JsonLDValue) => - // What type of object does this predicate have? - val flatObj: JsonLDValue = obj match { - case jsonLDObject: JsonLDObject => - // A JSON-LD object. Flatten its content. It's not at the top level, so if it has an IRI, - // add it to the top level and refer to it by IRI here. - jsonLDObject.flattened( - entitiesToAddToTopLevel = entitiesToAddToTopLevel, - isAtTopLevel = false - ) + value.map { case (pred: String, obj: JsonLDValue) => + // What type of object does this predicate have? + val flatObj: JsonLDValue = obj match { + case jsonLDObject: JsonLDObject => + // A JSON-LD object. Flatten its content. It's not at the top level, so if it has an IRI, + // add it to the top level and refer to it by IRI here. + jsonLDObject.flattened( + entitiesToAddToTopLevel = entitiesToAddToTopLevel, + isAtTopLevel = false + ) - case jsonLDArray: JsonLDArray => - // An array. Flatten its elements. If the array is the object of @graph, don't - // move its elements to the top level, because they're already at the top level. - jsonLDArray.flattened( - entitiesToAddToTopLevel = entitiesToAddToTopLevel, - isAtTopLevel = pred == JsonLDKeywords.GRAPH - ) + case jsonLDArray: JsonLDArray => + // An array. Flatten its elements. If the array is the object of @graph, don't + // move its elements to the top level, because they're already at the top level. + jsonLDArray.flattened( + entitiesToAddToTopLevel = entitiesToAddToTopLevel, + isAtTopLevel = pred == JsonLDKeywords.GRAPH + ) - case _ => - // Something else. Leave it as is. - obj - } + case _ => + // Something else. Leave it as is. + obj + } - pred -> flatObj + pred -> flatObj } } @@ -215,11 +208,11 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Recursively adds the contents of a JSON-LD entity to an [[RdfModel]]. - * - * @param model the model being constructed. - * @return the subject of the contents of this JSON-LD object (an IRI or a blank node). - */ + * Recursively adds the contents of a JSON-LD entity to an [[RdfModel]]. + * + * @param model the model being constructed. + * @return the subject of the contents of this JSON-LD object (an IRI or a blank node). + */ def addToModel(model: RdfModel)(implicit stringFormatter: StringFormatter): RdfResource = { val nodeFactory: RdfNodeFactory = model.getNodeFactory @@ -260,22 +253,22 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Adds `rdf:type` statements to an [[RdfModel]] to specify the types of a JSON-LD entity. - * - * @param model the model being constructed. - * @param rdfSubj the subject of this JSON-LD object. - */ - private def addRdfTypesToModel(model: RdfModel, rdfSubj: RdfResource)( - implicit stringFormatter: StringFormatter): Unit = { + * Adds `rdf:type` statements to an [[RdfModel]] to specify the types of a JSON-LD entity. + * + * @param model the model being constructed. + * @param rdfSubj the subject of this JSON-LD object. + */ + private def addRdfTypesToModel(model: RdfModel, rdfSubj: RdfResource)(implicit + stringFormatter: StringFormatter + ): Unit = { val nodeFactory: RdfNodeFactory = model.getNodeFactory - def addRdfType(typeIri: JsonLDString): Unit = { + def addRdfType(typeIri: JsonLDString): Unit = model.add( subj = rdfSubj, pred = nodeFactory.makeIriNode(OntologyConstants.Rdf.Type), obj = nodeFactory.makeIriNode(typeIri.value) ) - } def invalidType: Nothing = throw InvalidJsonLDException("The objects of @type must be strings") @@ -313,10 +306,10 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Adds the contents of `@graph` to an [[RdfModel]]. - * - * @param model the model being constructed. - */ + * Adds the contents of `@graph` to an [[RdfModel]]. + * + * @param model the model being constructed. + */ private def addGraphToModel(model: RdfModel)(implicit stringFormatter: StringFormatter): Unit = { def invalidGraph: Nothing = throw InvalidJsonLDException("The object of @graph must be a JSON-LD array of JSON-LD objects") @@ -351,15 +344,16 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Recursively adds a [[JsonLDValue]] to an [[RdfModel]], using the specified subject and predicate. - * - * @param model the model being constructed. - * @param rdfSubj the subject. - * @param rdfPred the predicate. - * @param jsonLDValue the value to be added. - */ + * Recursively adds a [[JsonLDValue]] to an [[RdfModel]], using the specified subject and predicate. + * + * @param model the model being constructed. + * @param rdfSubj the subject. + * @param rdfPred the predicate. + * @param jsonLDValue the value to be added. + */ private def addJsonLDValueToModel(model: RdfModel, rdfSubj: RdfResource, rdfPred: IriNode, jsonLDValue: JsonLDValue)( - implicit stringFormatter: StringFormatter): Unit = { + implicit stringFormatter: StringFormatter + ): Unit = { val nodeFactory: RdfNodeFactory = model.getNodeFactory // Which type of JSON-LD value is this? @@ -434,61 +428,56 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Returns `true` if this JSON-LD object represents an RDF entity with an IRI, - * i.e. if it has an `@id` and a `@type`. - */ - def isEntityWithIri: Boolean = { + * Returns `true` if this JSON-LD object represents an RDF entity with an IRI, + * i.e. if it has an `@id` and a `@type`. + */ + def isEntityWithIri: Boolean = Set(JsonLDKeywords.ID, JsonLDKeywords.TYPE).subsetOf(value.keySet) - } /** - * Returns `true` if this JSON-LD object represents an IRI value. - */ - def isIri: Boolean = { + * Returns `true` if this JSON-LD object represents an IRI value. + */ + def isIri: Boolean = value.keySet == Set(JsonLDKeywords.ID) - } /** - * Returns `true` if this JSON-LD object represents a string literal with a language tag. - */ - def isStringWithLang: Boolean = { + * Returns `true` if this JSON-LD object represents a string literal with a language tag. + */ + def isStringWithLang: Boolean = value.keySet == Set(JsonLDKeywords.VALUE, JsonLDKeywords.LANGUAGE) - } /** - * Returns `true` if this JSON-LD object represents a datatype value. - */ - def isDatatypeValue: Boolean = { + * Returns `true` if this JSON-LD object represents a datatype value. + */ + def isDatatypeValue: Boolean = value.keySet == Set(JsonLDKeywords.TYPE, JsonLDKeywords.VALUE) - } /** - * Converts an IRI value from its JSON-LD object value representation, validating it using the specified validation - * function. - * - * @param validationFun the validation function. - * @tparam T the type returned by the validation function. - * @return the return value of the validation function. - */ - def toIri[T](validationFun: (String, => Nothing) => T): T = { + * Converts an IRI value from its JSON-LD object value representation, validating it using the specified validation + * function. + * + * @param validationFun the validation function. + * @tparam T the type returned by the validation function. + * @return the return value of the validation function. + */ + def toIri[T](validationFun: (String, => Nothing) => T): T = if (isIri) { val id: IRI = requireString(JsonLDKeywords.ID) validationFun(id, throw BadRequestException(s"Invalid IRI: $id")) } else { throw BadRequestException(s"This JSON-LD object does not represent an IRI: $this") } - } /** - * Converts a datatype value from its JSON-LD object value representation, validating it using the specified validation - * function. - * - * @param expectedDatatype the IRI of the expected datatype. - * @param validationFun the validation function. - * @tparam T the type returned by the validation function. - * @return the return value of the validation function. - */ - def toDatatypeValueLiteral[T](expectedDatatype: SmartIri, validationFun: (String, => Nothing) => T): T = { + * Converts a datatype value from its JSON-LD object value representation, validating it using the specified validation + * function. + * + * @param expectedDatatype the IRI of the expected datatype. + * @param validationFun the validation function. + * @tparam T the type returned by the validation function. + * @return the return value of the validation function. + */ + def toDatatypeValueLiteral[T](expectedDatatype: SmartIri, validationFun: (String, => Nothing) => T): T = if (isDatatypeValue) { val datatype: IRI = requireString(JsonLDKeywords.TYPE) @@ -501,260 +490,248 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } else { throw BadRequestException(s"This JSON-LD object does not represent a datatype value: $this") } - } /** - * Gets a required string value of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not a string. - * - * @param key the key of the required value. - * @return the value. - */ - def requireString(key: String): String = { + * Gets a required string value of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not a string. + * + * @param key the key of the required value. + * @return the value. + */ + def requireString(key: String): String = value.getOrElse(key, throw BadRequestException(s"No $key provided")) match { case JsonLDString(str) => str case other => throw BadRequestException(s"Invalid $key: $other (string expected)") } - } /** - * Gets a required string value of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not a string. - * Then parses the value with the specified validation function (see [[StringFormatter]] - * for examples of such functions), throwing [[BadRequestException]] if the validation fails. - * - * @param key the key of the required value. - * @param validationFun a validation function that takes two arguments: the string to be validated, and a function - * that throws an exception if the string is invalid. The function's return value is the - * validated string, possibly converted to another type T. - * @tparam T the type of the validation function's return value. - * @return the return value of the validation function. - */ + * Gets a required string value of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not a string. + * Then parses the value with the specified validation function (see [[StringFormatter]] + * for examples of such functions), throwing [[BadRequestException]] if the validation fails. + * + * @param key the key of the required value. + * @param validationFun a validation function that takes two arguments: the string to be validated, and a function + * that throws an exception if the string is invalid. The function's return value is the + * validated string, possibly converted to another type T. + * @tparam T the type of the validation function's return value. + * @return the return value of the validation function. + */ def requireStringWithValidation[T](key: String, validationFun: (String, => Nothing) => T): T = { val str: String = requireString(key) validationFun(str, throw BadRequestException(s"Invalid $key: $str")) } /** - * Gets an optional string value of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not a string. - * - * @param key the key of the optional value. - * @return the value, or `None` if not found. - */ - def maybeString(key: String): Option[String] = { + * Gets an optional string value of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not a string. + * + * @param key the key of the optional value. + * @return the value, or `None` if not found. + */ + def maybeString(key: String): Option[String] = value.get(key).map { case JsonLDString(str) => str case other => throw BadRequestException(s"Invalid $key: $other (string expected)") } - } /** - * Gets an optional string value of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not a string. Parses the value with the specified validation - * function (see [[StringFormatter]] for examples of such functions), throwing - * [[BadRequestException]] if the validation fails. - * - * @param key the key of the optional value. - * @param validationFun a validation function that takes two arguments: the string to be validated, and a function - * that throws an exception if the string is invalid. The function's return value is the - * validated string, possibly converted to another type T. - * @tparam T the type of the validation function's return value. - * @return the return value of the validation function, or `None` if the value was not present. - */ - def maybeStringWithValidation[T](key: String, validationFun: (String, => Nothing) => T): Option[T] = { + * Gets an optional string value of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not a string. Parses the value with the specified validation + * function (see [[StringFormatter]] for examples of such functions), throwing + * [[BadRequestException]] if the validation fails. + * + * @param key the key of the optional value. + * @param validationFun a validation function that takes two arguments: the string to be validated, and a function + * that throws an exception if the string is invalid. The function's return value is the + * validated string, possibly converted to another type T. + * @tparam T the type of the validation function's return value. + * @return the return value of the validation function, or `None` if the value was not present. + */ + def maybeStringWithValidation[T](key: String, validationFun: (String, => Nothing) => T): Option[T] = maybeString(key).map { str => validationFun(str, throw BadRequestException(s"Invalid $key: $str")) } - } /** - * Gets a required IRI value (contained in a JSON-LD object) of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not a JSON-LD object. - * Then parses the object's ID with the specified validation function (see [[StringFormatter]] - * for examples of such functions), throwing [[BadRequestException]] if the validation fails. - * - * @param key the key of the required value. - * @return the validated IRI. - */ - def requireIriInObject[T](key: String, validationFun: (String, => Nothing) => T): T = { + * Gets a required IRI value (contained in a JSON-LD object) of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not a JSON-LD object. + * Then parses the object's ID with the specified validation function (see [[StringFormatter]] + * for examples of such functions), throwing [[BadRequestException]] if the validation fails. + * + * @param key the key of the required value. + * @return the validated IRI. + */ + def requireIriInObject[T](key: String, validationFun: (String, => Nothing) => T): T = requireObject(key).toIri(validationFun) - } /** - * Gets an optional IRI value (contained in a JSON-LD object) value of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not a JSON-LD object. Parses the object's ID with the - * specified validation function (see [[StringFormatter]] for examples of such functions), - * throwing [[BadRequestException]] if the validation fails. - * - * @param key the key of the optional value. - * @param validationFun a validation function that takes two arguments: the string to be validated, and a function - * that throws an exception if the string is invalid. The function's return value is the - * validated string, possibly converted to another type T. - * @tparam T the type of the validation function's return value. - * @return the return value of the validation function, or `None` if the value was not present. - */ - def maybeIriInObject[T](key: String, validationFun: (String, => Nothing) => T): Option[T] = { + * Gets an optional IRI value (contained in a JSON-LD object) value of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not a JSON-LD object. Parses the object's ID with the + * specified validation function (see [[StringFormatter]] for examples of such functions), + * throwing [[BadRequestException]] if the validation fails. + * + * @param key the key of the optional value. + * @param validationFun a validation function that takes two arguments: the string to be validated, and a function + * that throws an exception if the string is invalid. The function's return value is the + * validated string, possibly converted to another type T. + * @tparam T the type of the validation function's return value. + * @return the return value of the validation function, or `None` if the value was not present. + */ + def maybeIriInObject[T](key: String, validationFun: (String, => Nothing) => T): Option[T] = maybeObject(key).map(_.toIri(validationFun)) - } /** - * Gets a required datatype value (contained in a JSON-LD object) of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not a JSON-LD object. - * Then parses the object's literal value with the specified validation function (see [[StringFormatter]] - * for examples of such functions), throwing [[BadRequestException]] if the validation fails. - * - * @param key the key of the required value. - * @param expectedDatatype the IRI of the expected datatype. - * @tparam T the type of the validation function's return value. - * @return the validated literal value. - */ - def requireDatatypeValueInObject[T](key: String, - expectedDatatype: SmartIri, - validationFun: (String, => Nothing) => T): T = { + * Gets a required datatype value (contained in a JSON-LD object) of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not a JSON-LD object. + * Then parses the object's literal value with the specified validation function (see [[StringFormatter]] + * for examples of such functions), throwing [[BadRequestException]] if the validation fails. + * + * @param key the key of the required value. + * @param expectedDatatype the IRI of the expected datatype. + * @tparam T the type of the validation function's return value. + * @return the validated literal value. + */ + def requireDatatypeValueInObject[T]( + key: String, + expectedDatatype: SmartIri, + validationFun: (String, => Nothing) => T + ): T = requireObject(key).toDatatypeValueLiteral(expectedDatatype, validationFun) - } /** - * Gets an optional datatype value (contained in a JSON-LD object) value of a property of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not a JSON-LD object. Parses the object's literal value with the - * specified validation function (see [[StringFormatter]] for examples of such functions), - * throwing [[BadRequestException]] if the validation fails. - * - * @param key the key of the optional value. - * @param expectedDatatype the IRI of the expected datatype. - * @param validationFun a validation function that takes two arguments: the string to be validated, and a function - * that throws an exception if the string is invalid. The function's return value is the - * validated string, possibly converted to another type T. - * @tparam T the type of the validation function's return value. - * @return the return value of the validation function, or `None` if the value was not present. - */ - def maybeDatatypeValueInObject[T](key: String, - expectedDatatype: SmartIri, - validationFun: (String, => Nothing) => T): Option[T] = { + * Gets an optional datatype value (contained in a JSON-LD object) value of a property of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not a JSON-LD object. Parses the object's literal value with the + * specified validation function (see [[StringFormatter]] for examples of such functions), + * throwing [[BadRequestException]] if the validation fails. + * + * @param key the key of the optional value. + * @param expectedDatatype the IRI of the expected datatype. + * @param validationFun a validation function that takes two arguments: the string to be validated, and a function + * that throws an exception if the string is invalid. The function's return value is the + * validated string, possibly converted to another type T. + * @tparam T the type of the validation function's return value. + * @return the return value of the validation function, or `None` if the value was not present. + */ + def maybeDatatypeValueInObject[T]( + key: String, + expectedDatatype: SmartIri, + validationFun: (String, => Nothing) => T + ): Option[T] = maybeObject(key).map(_.toDatatypeValueLiteral(expectedDatatype, validationFun)) - } /** - * Gets the required object value of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not an object. - * - * @param key the key of the required value. - * @return the required value. - */ - def requireObject(key: String): JsonLDObject = { + * Gets the required object value of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not an object. + * + * @param key the key of the required value. + * @return the required value. + */ + def requireObject(key: String): JsonLDObject = value.getOrElse(key, throw BadRequestException(s"No $key provided")) match { case obj: JsonLDObject => obj case other => throw BadRequestException(s"Invalid $key: $other (object expected)") } - } /** - * Gets the optional object value of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not an object. - * - * @param key the key of the optional value. - * @return the optional value. - */ - def maybeObject(key: String): Option[JsonLDObject] = { + * Gets the optional object value of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not an object. + * + * @param key the key of the optional value. + * @return the optional value. + */ + def maybeObject(key: String): Option[JsonLDObject] = value.get(key).map { case obj: JsonLDObject => obj case other => throw BadRequestException(s"Invalid $key: $other (object expected)") } - } /** - * Gets the required array value of this JSON-LD object. If the value is not an array, - * returns a one-element array containing the value. Throws - * [[BadRequestException]] if the property is not found. - * - * @param key the key of the required value. - * @return the required value. - */ - def requireArray(key: String): JsonLDArray = { + * Gets the required array value of this JSON-LD object. If the value is not an array, + * returns a one-element array containing the value. Throws + * [[BadRequestException]] if the property is not found. + * + * @param key the key of the required value. + * @return the required value. + */ + def requireArray(key: String): JsonLDArray = value.getOrElse(key, throw BadRequestException(s"No $key provided")) match { case obj: JsonLDArray => obj case other => JsonLDArray(Seq(other)) } - } /** - * Gets the optional array value of this JSON-LD object. If the value is not an array, - * returns a one-element array containing the value. - * - * @param key the key of the optional value. - * @return the optional value. - */ - def maybeArray(key: String): Option[JsonLDArray] = { + * Gets the optional array value of this JSON-LD object. If the value is not an array, + * returns a one-element array containing the value. + * + * @param key the key of the optional value. + * @return the optional value. + */ + def maybeArray(key: String): Option[JsonLDArray] = value.get(key).map { case obj: JsonLDArray => obj case other => JsonLDArray(Seq(other)) } - } /** - * Gets the required integer value of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not an integer. - * - * @param key the key of the required value. - * @return the required value. - */ - def requireInt(key: String): Int = { + * Gets the required integer value of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not an integer. + * + * @param key the key of the required value. + * @return the required value. + */ + def requireInt(key: String): Int = value.getOrElse(key, throw BadRequestException(s"No $key provided")) match { case obj: JsonLDInt => obj.value case other => throw BadRequestException(s"Invalid $key: $other (integer expected)") } - } /** - * Gets the optional integer value of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not an integer. - * - * @param key the key of the optional value. - * @return the optional value. - */ - def maybeInt(key: String): Option[Int] = { + * Gets the optional integer value of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not an integer. + * + * @param key the key of the optional value. + * @return the optional value. + */ + def maybeInt(key: String): Option[Int] = value.get(key).map { case obj: JsonLDInt => obj.value case other => throw BadRequestException(s"Invalid $key: $other (integer expected)") } - } /** - * Gets the required boolean value of this JSON-LD object, throwing - * [[BadRequestException]] if the property is not found or if its value is not a boolean. - * - * @param key the key of the required value. - * @return the required value. - */ - def requireBoolean(key: String): Boolean = { + * Gets the required boolean value of this JSON-LD object, throwing + * [[BadRequestException]] if the property is not found or if its value is not a boolean. + * + * @param key the key of the required value. + * @return the required value. + */ + def requireBoolean(key: String): Boolean = value.getOrElse(key, throw BadRequestException(s"No $key provided")) match { case obj: JsonLDBoolean => obj.value case other => throw BadRequestException(s"Invalid $key: $other (boolean expected)") } - } /** - * Gets the optional boolean value of this JSON-LD object, throwing - * [[BadRequestException]] if the property's value is not a boolean. - * - * @param key the key of the optional value. - * @return the optional value. - */ - def maybeBoolean(key: String): Option[Boolean] = { + * Gets the optional boolean value of this JSON-LD object, throwing + * [[BadRequestException]] if the property's value is not a boolean. + * + * @param key the key of the optional value. + * @return the optional value. + */ + def maybeBoolean(key: String): Option[Boolean] = value.get(key).map { case obj: JsonLDBoolean => obj.value case other => throw BadRequestException(s"Invalid $key: $other (boolean expected)") } - } override def compare(that: JsonLDValue): Int = 0 /** - * Validates the `@id` of a JSON-LD object as a Knora data IRI. - * - * @return a validated Knora data IRI. - */ + * Validates the `@id` of a JSON-LD object as a Knora data IRI. + * + * @return a validated Knora data IRI. + */ def requireIDAsKnoraDataIri: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -768,10 +745,10 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Validates the optional `@id` of a JSON-LD object as a Knora data IRI. - * - * @return an optional validated Knora data IRI. - */ + * Validates the optional `@id` of a JSON-LD object as a Knora data IRI. + * + * @return an optional validated Knora data IRI. + */ def maybeIDAsKnoraDataIri: Option[SmartIri] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -787,20 +764,20 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Validates an optional Base64-encoded UUID in a JSON-LD object. - * - * @return an optional validated decoded UUID. - */ + * Validates an optional Base64-encoded UUID in a JSON-LD object. + * + * @return an optional validated decoded UUID. + */ def maybeUUID(key: String): Option[UUID] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance maybeStringWithValidation(key, stringFormatter.validateBase64EncodedUuid) } /** - * Validates the `@type` of a JSON-LD object as a Knora type IRI in the API v2 complex schema. - * - * @return a validated Knora type IRI. - */ + * Validates the `@type` of a JSON-LD object as a Knora type IRI in the API v2 complex schema. + * + * @return a validated Knora type IRI. + */ def requireTypeAsKnoraApiV2ComplexTypeIri: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -814,11 +791,11 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * When called on a JSON-LD object representing a resource, ensures that it contains a single Knora property with - * a single value in the Knora API v2 complex schema. - * - * @return the property IRI and the value. - */ + * When called on a JSON-LD object representing a resource, ensures that it contains a single Knora property with + * a single value in the Knora API v2 complex schema. + * + * @return the property IRI and the value. + */ def requireResourcePropertyApiV2ComplexValue: (SmartIri, JsonLDObject) = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -849,14 +826,14 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue { } /** - * Represents a JSON array in a JSON-LD document. - * - * @param value a sequence of JSON-LD values. - */ + * Represents a JSON array in a JSON-LD document. + * + * @param value a sequence of JSON-LD values. + */ case class JsonLDArray(value: Seq[JsonLDValue]) extends JsonLDValue { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - override def equals(that: Any): Boolean = { + override def equals(that: Any): Boolean = // Ignore the order of elements when testing equality for this class, since by default, // order is not significant in JSON-LD arrays (see // ). @@ -864,7 +841,6 @@ case class JsonLDArray(value: Seq[JsonLDValue]) extends JsonLDValue { case otherArray: JsonLDArray => value.toSet == otherArray.value.toSet case _ => false } - } override def hashCode(): Int = { val hashCodeBuilder = new HashCodeBuilder(21, 41) @@ -887,15 +863,17 @@ case class JsonLDArray(value: Seq[JsonLDValue]) extends JsonLDValue { } /** - * Flattens this JSON-LD array by extracting inlined entities with IRIs and replacing them with - * references to their IRI. - * - * @param entitiesToAddToTopLevel inlined entities that have been extracted. - * @param isAtTopLevel `true` if this array is the object of `@graph` at the top level of the document. - * @return a flattened copy of this JSON-LD array. - */ - def flattened(entitiesToAddToTopLevel: collection.mutable.Set[JsonLDObject], - isAtTopLevel: Boolean = false): JsonLDArray = { + * Flattens this JSON-LD array by extracting inlined entities with IRIs and replacing them with + * references to their IRI. + * + * @param entitiesToAddToTopLevel inlined entities that have been extracted. + * @param isAtTopLevel `true` if this array is the object of `@graph` at the top level of the document. + * @return a flattened copy of this JSON-LD array. + */ + def flattened( + entitiesToAddToTopLevel: collection.mutable.Set[JsonLDObject], + isAtTopLevel: Boolean = false + ): JsonLDArray = JsonLDArray { // Flatten the JSON-LD objects that are elements of the array. value.map { elem: JsonLDValue => @@ -916,16 +894,15 @@ case class JsonLDArray(value: Seq[JsonLDValue]) extends JsonLDValue { } } } - } /** - * Tries to interpret the elements of this array as JSON-LD objects containing `@language` and `@value`, - * and returns the results as a set of [[StringLiteralV2]]. Throws [[BadRequestException]] - * if the array can't be interpreted in this way. - * - * @return a map of language keys to values. - */ - def toObjsWithLang: Seq[StringLiteralV2] = { + * Tries to interpret the elements of this array as JSON-LD objects containing `@language` and `@value`, + * and returns the results as a set of [[StringLiteralV2]]. Throws [[BadRequestException]] + * if the array can't be interpreted in this way. + * + * @return a map of language keys to values. + */ + def toObjsWithLang: Seq[StringLiteralV2] = value.map { case obj: JsonLDObject => val lang = obj.requireStringWithValidation(JsonLDKeywords.LANGUAGE, stringFormatter.toSparqlEncodedString) @@ -939,66 +916,69 @@ case class JsonLDArray(value: Seq[JsonLDValue]) extends JsonLDValue { case other => throw BadRequestException(s"Expected JSON-LD object: $other") } - } override def compare(that: JsonLDValue): Int = 0 } /** - * Represents a JSON-LD document. - * - * @param body the body of the JSON-LD document. - * @param context the context of the JSON-LD document. - * @param isFlat `true` if this JSON-LD document has been constructed as a flat document, i.e. - * without inlining entities that have IRIs. - * @param keepStructure if `true`, the document will not be compacted when formatted, and only - * the body will be used. - */ -case class JsonLDDocument(body: JsonLDObject, - context: JsonLDObject = JsonLDObject(Map.empty[String, JsonLDValue]), - isFlat: Boolean = false, - keepStructure: Boolean = false) { - - /** - * A convenience function that calls `body.requireString`. - */ + * Represents a JSON-LD document. + * + * @param body the body of the JSON-LD document. + * @param context the context of the JSON-LD document. + * @param isFlat `true` if this JSON-LD document has been constructed as a flat document, i.e. + * without inlining entities that have IRIs. + * @param keepStructure if `true`, the document will not be compacted when formatted, and only + * the body will be used. + */ +case class JsonLDDocument( + body: JsonLDObject, + context: JsonLDObject = JsonLDObject(Map.empty[String, JsonLDValue]), + isFlat: Boolean = false, + keepStructure: Boolean = false +) { + + /** + * A convenience function that calls `body.requireString`. + */ def requireString(key: String): String = body.requireString(key) /** - * A convenience function that calls `body.requireStringWithValidation`. - */ + * A convenience function that calls `body.requireStringWithValidation`. + */ def requireStringWithValidation[T](key: String, validationFun: (String, => Nothing) => T): T = body.requireStringWithValidation(key, validationFun) /** - * A convenience function that calls `body.maybeString`. - */ + * A convenience function that calls `body.maybeString`. + */ def maybeString(key: String): Option[String] = body.maybeString(key) /** - * A convenience function that calls `body.maybeStringWithValidation`. - */ + * A convenience function that calls `body.maybeStringWithValidation`. + */ def maybeStringWithValidation[T](key: String, validationFun: (String, => Nothing) => T): Option[T] = body.maybeStringWithValidation(key, validationFun) /** - * A convenience function that calls `body.requireIriInObject`. - */ + * A convenience function that calls `body.requireIriInObject`. + */ def requireIriInObject[T](key: String, validationFun: (String, => Nothing) => T): T = body.requireIriInObject(key, validationFun) /** - * A convenience function that calls `body.maybeIriInObject`. - */ + * A convenience function that calls `body.maybeIriInObject`. + */ def maybeIriInObject[T](key: String, validationFun: (String, => Nothing) => T): Option[T] = body.maybeIriInObject(key, validationFun) /** - * A convenience function that calls `body.requireDatatypeValueInObject`. - */ - def requireDatatypeValueInObject[T](key: String, - expectedDatatype: SmartIri, - validationFun: (String, => Nothing) => T): T = + * A convenience function that calls `body.requireDatatypeValueInObject`. + */ + def requireDatatypeValueInObject[T]( + key: String, + expectedDatatype: SmartIri, + validationFun: (String, => Nothing) => T + ): T = body.requireDatatypeValueInObject( key = key, expectedDatatype = expectedDatatype, @@ -1006,11 +986,13 @@ case class JsonLDDocument(body: JsonLDObject, ) /** - * A convenience function that calls `body.maybeDatatypeValueInObject`. - */ - def maybeDatatypeValueInObject[T](key: String, - expectedDatatype: SmartIri, - validationFun: (String, => Nothing) => T): Option[T] = + * A convenience function that calls `body.maybeDatatypeValueInObject`. + */ + def maybeDatatypeValueInObject[T]( + key: String, + expectedDatatype: SmartIri, + validationFun: (String, => Nothing) => T + ): Option[T] = body.maybeDatatypeValueInObject( key = key, expectedDatatype = expectedDatatype, @@ -1018,76 +1000,76 @@ case class JsonLDDocument(body: JsonLDObject, ) /** - * A convenience function that calls `body.requireObject`. - */ + * A convenience function that calls `body.requireObject`. + */ def requireObject(key: String): JsonLDObject = body.requireObject(key) /** - * A convenience function that calls `body.maybeObject`. - */ + * A convenience function that calls `body.maybeObject`. + */ def maybeObject(key: String): Option[JsonLDObject] = body.maybeObject(key) /** - * A convenience function that calls `body.requireArray`. - */ + * A convenience function that calls `body.requireArray`. + */ def requireArray(key: String): JsonLDArray = body.requireArray(key) /** - * A convenience function that calls `body.maybeArray`. - */ + * A convenience function that calls `body.maybeArray`. + */ def maybeArray(key: String): Option[JsonLDArray] = body.maybeArray(key) /** - * A convenience function that calls `body.requireInt`. - */ + * A convenience function that calls `body.requireInt`. + */ def requireInt(key: String): Int = body.requireInt(key) /** - * A convenience function that calls `body.maybeInt`. - */ + * A convenience function that calls `body.maybeInt`. + */ def maybeInt(key: String): Option[Int] = body.maybeInt(key) /** - * A convenience function that calls `body.requireBoolean`. - */ + * A convenience function that calls `body.requireBoolean`. + */ def requireBoolean(key: String): Boolean = body.requireBoolean(key) /** - * A convenience function that calls `body.maybeBoolean`. - */ + * A convenience function that calls `body.maybeBoolean`. + */ def maybeBoolean(key: String): Option[Boolean] = body.maybeBoolean(key) /** - * A convenience function that calls `body.requireIDAsKnoraDataIri`. - */ + * A convenience function that calls `body.requireIDAsKnoraDataIri`. + */ def requireIDAsKnoraDataIri: SmartIri = body.requireIDAsKnoraDataIri /** - * A convenience function that calls `body.maybeIDAsKnoraDataIri`. - */ + * A convenience function that calls `body.maybeIDAsKnoraDataIri`. + */ def maybeIDAsKnoraDataIri: Option[SmartIri] = body.maybeIDAsKnoraDataIri /** - * A convenience function that calls `body.requireTypeAsKnoraApiV2ComplexTypeIri`. - */ + * A convenience function that calls `body.requireTypeAsKnoraApiV2ComplexTypeIri`. + */ def requireTypeAsKnoraTypeIri: SmartIri = body.requireTypeAsKnoraApiV2ComplexTypeIri /** - * A convenience function that calls `body.requireResourcePropertyApiV2ComplexValue`. - */ + * A convenience function that calls `body.requireResourcePropertyApiV2ComplexValue`. + */ def requireResourcePropertyValue: (SmartIri, JsonLDObject) = body.requireResourcePropertyApiV2ComplexValue /** - * A convenience function that calls `body.maybeUUID`. - */ + * A convenience function that calls `body.maybeUUID`. + */ def maybeUUID(key: String): Option[UUID] = body.maybeUUID(key: String) /** - * Flattens this JSON-LD document by moving inlined entities with IRIs to the top level. - * - * @return a flattened copy of this JSON-LD document. - */ - def flattened: JsonLDDocument = { + * Flattens this JSON-LD document by moving inlined entities with IRIs to the top level. + * + * @return a flattened copy of this JSON-LD document. + */ + def flattened: JsonLDDocument = // Is this JSON-LD document already flat? if (isFlat) { // Yes. Just return it. @@ -1121,8 +1103,11 @@ case class JsonLDDocument(body: JsonLDObject, // if there is one, and the existing top-level entity if there is one. val existingGraphElements: Seq[JsonLDValue] = flattenedContent.maybeArray(JsonLDKeywords.GRAPH).map(_.value).getOrElse(Seq.empty) - JsonLDObject(Map( - JsonLDKeywords.GRAPH -> JsonLDArray(maybeTopLevelObject ++ existingGraphElements ++ entitiesToAddToTopLevel))) + JsonLDObject( + Map( + JsonLDKeywords.GRAPH -> JsonLDArray(maybeTopLevelObject ++ existingGraphElements ++ entitiesToAddToTopLevel) + ) + ) } else { // No. Just keep the existing @graph, if there is one, with the existing top-level entity. flattenedContent @@ -1133,13 +1118,12 @@ case class JsonLDDocument(body: JsonLDObject, isFlat = true ) } - } /** - * Converts this JSON-LD document to its compacted representation. - * - * @param flatten `true` if a flat JSON-LD document should be returned. - */ + * Converts this JSON-LD document to its compacted representation. + * + * @param flatten `true` if a flat JSON-LD document should be returned. + */ private def makeCompactedJavaxJsonObject(flatten: Boolean): JsonObject = { // Flatten the document if requested. val documentFlattenedIfRequested: JsonLDDocument = if (flatten) { @@ -1154,12 +1138,12 @@ case class JsonLDDocument(body: JsonLDObject, } /** - * Formats this JSON-LD document as a string, using the specified [[JsonWriterFactory]]. - * - * @param jsonWriterFactory a [[JsonWriterFactory]] configured with the desired options. - * @param flatten `true` if a flat JSON-LD document should be returned. - * @return the formatted document. - */ + * Formats this JSON-LD document as a string, using the specified [[JsonWriterFactory]]. + * + * @param jsonWriterFactory a [[JsonWriterFactory]] configured with the desired options. + * @param flatten `true` if a flat JSON-LD document should be returned. + * @return the formatted document. + */ private def formatWithJsonWriterFactory(jsonWriterFactory: JsonWriterFactory, flatten: Boolean): String = { val javaxJsonObject: JsonObject = if (keepStructure) { body.toJavaxJsonValue @@ -1175,11 +1159,11 @@ case class JsonLDDocument(body: JsonLDObject, } /** - * Converts this JSON-LD document to a pretty-printed JSON-LD string. - * - * @param flatten `true` if a flat JSON-LD document should be returned. - * @return the formatted document. - */ + * Converts this JSON-LD document to a pretty-printed JSON-LD string. + * + * @param flatten `true` if a flat JSON-LD document should be returned. + * @return the formatted document. + */ def toPrettyString(flatten: Boolean = false): String = { val config = new util.HashMap[String, Boolean]() config.put(JsonGenerator.PRETTY_PRINTING, true) @@ -1188,11 +1172,11 @@ case class JsonLDDocument(body: JsonLDObject, } /** - * Converts this [[JsonLDDocument]] to a compact JSON-LD string. - * - * @param flatten `true` if a flat JSON-LD document should be returned. - * @return the formatted document. - */ + * Converts this [[JsonLDDocument]] to a compact JSON-LD string. + * + * @param flatten `true` if a flat JSON-LD document should be returned. + * @return the formatted document. + */ def toCompactString(flatten: Boolean = false): String = { val config = new util.HashMap[String, Boolean]() val jsonWriterFactory: JsonWriterFactory = Json.createWriterFactory(config) @@ -1200,10 +1184,10 @@ case class JsonLDDocument(body: JsonLDObject, } /** - * Converts this JSON-LD document to an [[RdfModel]]. - * - * @param modelFactory an [[RdfModelFactory]]. - */ + * Converts this JSON-LD document to an [[RdfModel]]. + * + * @param modelFactory an [[RdfModelFactory]]. + */ def toRdfModel(modelFactory: RdfModelFactory): RdfModel = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val model: RdfModel = modelFactory.makeEmptyModel @@ -1223,40 +1207,41 @@ case class JsonLDDocument(body: JsonLDObject, } /** - * A tool for working with JSON-LD. - */ + * A tool for working with JSON-LD. + */ object JsonLDUtil { /** - * Makes a JSON-LD context containing prefixes for Knora and other ontologies. - * - * @param fixedPrefixes a map of fixed prefixes (e.g. `rdfs` or `knora-base`) to namespaces. - * @param knoraOntologiesNeedingPrefixes a set of IRIs of other Knora ontologies that need prefixes. - * @return a JSON-LD context. - */ - def makeContext(fixedPrefixes: Map[String, String], - knoraOntologiesNeedingPrefixes: Set[SmartIri] = Set.empty): JsonLDObject = { + * Makes a JSON-LD context containing prefixes for Knora and other ontologies. + * + * @param fixedPrefixes a map of fixed prefixes (e.g. `rdfs` or `knora-base`) to namespaces. + * @param knoraOntologiesNeedingPrefixes a set of IRIs of other Knora ontologies that need prefixes. + * @return a JSON-LD context. + */ + def makeContext( + fixedPrefixes: Map[String, String], + knoraOntologiesNeedingPrefixes: Set[SmartIri] = Set.empty + ): JsonLDObject = { /** - * Given a function that makes a prefix from a Knora ontology IRI, returns an association list in which - * each element is a prefix associated with a namespace. - * - * @param prefixFun a function that makes a prefix from a Knora ontology IRI. - * @return an association list in which each element is a prefix associated with a namespace. - */ - def makeKnoraPrefixes(prefixFun: SmartIri => String): Seq[(String, String)] = { + * Given a function that makes a prefix from a Knora ontology IRI, returns an association list in which + * each element is a prefix associated with a namespace. + * + * @param prefixFun a function that makes a prefix from a Knora ontology IRI. + * @return an association list in which each element is a prefix associated with a namespace. + */ + def makeKnoraPrefixes(prefixFun: SmartIri => String): Seq[(String, String)] = knoraOntologiesNeedingPrefixes.toSeq.map { ontology => prefixFun(ontology) -> (ontology.toString + '#') } - } /** - * Determines whether an association list returned by `makeKnoraPrefixes` contains conflicts, - * including conflicts with `fixedPrefixes`. - * - * @param knoraPrefixes the association list to check. - * @return `true` if the list contains conflicts. - */ + * Determines whether an association list returned by `makeKnoraPrefixes` contains conflicts, + * including conflicts with `fixedPrefixes`. + * + * @param knoraPrefixes the association list to check. + * @return `true` if the list contains conflicts. + */ def hasPrefixConflicts(knoraPrefixes: Seq[(String, String)]): Boolean = { val prefixSeq = knoraPrefixes.map(_._1) ++ fixedPrefixes.keys prefixSeq.size != prefixSeq.distinct.size @@ -1274,8 +1259,9 @@ object JsonLDUtil { if (hasPrefixConflicts(longKnoraPrefixes)) { // Yes. This shouldn't happen, so throw InconsistentRepositoryDataException. throw InconsistentRepositoryDataException( - s"Can't make distinct prefixes for ontologies: ${(fixedPrefixes.values ++ knoraOntologiesNeedingPrefixes.map( - _.toString)).mkString(", ")}") + s"Can't make distinct prefixes for ontologies: ${(fixedPrefixes.values ++ knoraOntologiesNeedingPrefixes + .map(_.toString)).mkString(", ")}" + ) } else { // No. Use the long prefixes. longKnoraPrefixes.toMap @@ -1286,44 +1272,43 @@ object JsonLDUtil { } // Make a JSON-LD context containing the fixed prefixes as well as the ones generated by this method. - JsonLDObject((fixedPrefixes ++ knoraPrefixMap).map { - case (prefix, namespace) => prefix -> JsonLDString(namespace) + JsonLDObject((fixedPrefixes ++ knoraPrefixMap).map { case (prefix, namespace) => + prefix -> JsonLDString(namespace) }) } /** - * Converts an IRI value to its JSON-LD object value representation. - * - * @param iri the IRI to be converted. - * @return the JSON-LD representation of the IRI as an object value. - */ - def iriToJsonLDObject(iri: IRI): JsonLDObject = { + * Converts an IRI value to its JSON-LD object value representation. + * + * @param iri the IRI to be converted. + * @return the JSON-LD representation of the IRI as an object value. + */ + def iriToJsonLDObject(iri: IRI): JsonLDObject = JsonLDObject(Map(JsonLDKeywords.ID -> JsonLDString(iri))) - } /** - * Given a predicate value and a language code, returns a JSON-LD object containing `@value` and `@language` - * predicates. - * - * @param obj a predicate value. - * @return a JSON-LD object containing `@value` and `@language` predicates. - */ - def objectWithLangToJsonLDObject(obj: String, lang: String): JsonLDObject = { + * Given a predicate value and a language code, returns a JSON-LD object containing `@value` and `@language` + * predicates. + * + * @param obj a predicate value. + * @return a JSON-LD object containing `@value` and `@language` predicates. + */ + def objectWithLangToJsonLDObject(obj: String, lang: String): JsonLDObject = JsonLDObject( Map( JsonLDKeywords.VALUE -> JsonLDString(obj), JsonLDKeywords.LANGUAGE -> JsonLDString(lang) - )) - } + ) + ) /** - * Given a predicate value and a datatype, returns a JSON-LD object containing `@value` and `@type` - * predicates. - * - * @param value a predicate value. - * @param datatype the datatype. - * @return a JSON-LD object containing `@value` and `@type` predicates. - */ + * Given a predicate value and a datatype, returns a JSON-LD object containing `@value` and `@type` + * predicates. + * + * @param value a predicate value. + * @param datatype the datatype. + * @return a JSON-LD object containing `@value` and `@type` predicates. + */ def datatypeValueToJsonLDObject(value: String, datatype: SmartIri): JsonLDObject = { // Normalise the formatting of decimal values to ensure consistency in tests. val strValue: String = if (datatype.toString == OntologyConstants.Xsd.Decimal) { @@ -1336,36 +1321,36 @@ object JsonLDUtil { Map( JsonLDKeywords.VALUE -> JsonLDString(strValue), JsonLDKeywords.TYPE -> JsonLDString(datatype.toString) - )) + ) + ) } /** - * Given a map of language codes to predicate values, returns a JSON-LD array in which each element - * has a `@value` predicate and a `@language` predicate. - * - * @param objectsWithLangs a map of language codes to predicate values. - * @return a JSON-LD array in which each element has a `@value` predicate and a `@language` predicate, - * sorted by language code. - */ + * Given a map of language codes to predicate values, returns a JSON-LD array in which each element + * has a `@value` predicate and a `@language` predicate. + * + * @param objectsWithLangs a map of language codes to predicate values. + * @return a JSON-LD array in which each element has a `@value` predicate and a `@language` predicate, + * sorted by language code. + */ def objectsWithLangsToJsonLDArray(objectsWithLangs: Map[String, String]): JsonLDArray = { - val objects: Seq[JsonLDObject] = objectsWithLangs.toSeq.sortBy(_._1).map { - case (lang, obj) => - objectWithLangToJsonLDObject( - obj = obj, - lang = lang - ) + val objects: Seq[JsonLDObject] = objectsWithLangs.toSeq.sortBy(_._1).map { case (lang, obj) => + objectWithLangToJsonLDObject( + obj = obj, + lang = lang + ) } JsonLDArray(objects) } /** - * Parses a JSON-LD string as a [[JsonLDDocument]] with an empty context. - * - * @param jsonLDString the string to be parsed. - * @param flatten `true` if a flat JSON-LD document should be returned. - * @return a [[JsonLDDocument]]. - */ + * Parses a JSON-LD string as a [[JsonLDDocument]] with an empty context. + * + * @param jsonLDString the string to be parsed. + * @param flatten `true` if a flat JSON-LD document should be returned. + * @return a [[JsonLDDocument]]. + */ def parseJsonLD(jsonLDString: String, flatten: Boolean = false): JsonLDDocument = { // Parse the string into a javax.json.JsonStructure. val stringReader = new StringReader(jsonLDString) @@ -1393,20 +1378,20 @@ object JsonLDUtil { } /** - * Converts a [[JsonObject]] into a [[JsonLDDocument]]. - * - * @param jsonObject a JSON object. - * @return the corresponding [[JsonLDDocument]]. - */ + * Converts a [[JsonObject]] into a [[JsonLDDocument]]. + * + * @param jsonObject a JSON object. + * @return the corresponding [[JsonLDDocument]]. + */ private def javaxJsonObjectToJsonLDDocument(jsonObject: JsonObject): JsonLDDocument = { /** - * Converts a [[JsonValue]] to a [[JsonLDValue]]. - * - * @param jsonValue a [[JsonValue]]. - * @return the corresponding [[JsonLDValue]]. - */ - def jsonValueToJsonLDValue(jsonValue: JsonValue): JsonLDValue = { + * Converts a [[JsonValue]] to a [[JsonLDValue]]. + * + * @param jsonValue a [[JsonValue]]. + * @return the corresponding [[JsonLDValue]]. + */ + def jsonValueToJsonLDValue(jsonValue: JsonValue): JsonLDValue = jsonValue match { case jsonString: JsonString => JsonLDString(jsonString.getString) case jsonNumber: JsonNumber => JsonLDInt(jsonNumber.intValue) @@ -1433,7 +1418,6 @@ object JsonLDUtil { case _ => throw BadRequestException(s"Unexpected type in JSON-LD input: $jsonValue") } - } jsonValueToJsonLDValue(jsonObject) match { case obj: JsonLDObject => JsonLDDocument(body = obj, context = JsonLDObject(Map.empty[IRI, JsonLDValue])) @@ -1442,22 +1426,22 @@ object JsonLDUtil { } /** - * Converts an [[RdfModel]] to a [[JsonLDDocument]]. There can be more than one valid - * way to nest objects in the converted JSON-LD. This implementation takes the following - * approach: - * - * - Inline blank nodes wherever they are used. - * - Nest each entity in the first encountered entity that refers to it, and refer to it by IRI elsewhere. - * - Don't nest an entity with an IRI inside a blank node. - * - Don't inline Knora ontology entities. - * - After nesting, if more than one top-level entity remains, wrap them all in a `@graph`. - * - * An error is returned if the same blank node is used more than once. - * - * @param model the [[RdfModel]] to be read. - * @param flatJsonLD if `true`, produce a flat JSON-LD document. - * @return the corresponding [[JsonLDDocument]]. - */ + * Converts an [[RdfModel]] to a [[JsonLDDocument]]. There can be more than one valid + * way to nest objects in the converted JSON-LD. This implementation takes the following + * approach: + * + * - Inline blank nodes wherever they are used. + * - Nest each entity in the first encountered entity that refers to it, and refer to it by IRI elsewhere. + * - Don't nest an entity with an IRI inside a blank node. + * - Don't inline Knora ontology entities. + * - After nesting, if more than one top-level entity remains, wrap them all in a `@graph`. + * + * An error is returned if the same blank node is used more than once. + * + * @param model the [[RdfModel]] to be read. + * @param flatJsonLD if `true`, produce a flat JSON-LD document. + * @return the corresponding [[JsonLDDocument]]. + */ def fromRdfModel(model: RdfModel, flatJsonLD: Boolean = false): JsonLDDocument = { if (model.getContexts.nonEmpty) { throw BadRequestException("Named graphs in JSON-LD are not supported") @@ -1466,8 +1450,8 @@ object JsonLDUtil { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Make a JSON-LD context from the model's namespaces. - val contextMap: Map[IRI, JsonLDString] = model.getNamespaces.map { - case (prefix, namespace) => prefix -> JsonLDString(namespace) + val contextMap: Map[IRI, JsonLDString] = model.getNamespaces.map { case (prefix, namespace) => + prefix -> JsonLDString(namespace) } val context: JsonLDObject = JsonLDObject(contextMap) @@ -1524,22 +1508,24 @@ object JsonLDUtil { } /** - * Converts an RDF entity to a [[JsonLDObject]] representing the statements about the entity. - * - * @param subj the subject of the entity. - * @param statements the statements representing the entity. - * @param model the [[RdfModel]] that is being read. - * @param topLevelEntities the top-level entities that have been constructed so far. - * @param processedSubjects the subjects that have already been processed. - * @param flatJsonLD if `true`, produce flat JSON-LD. - * @return the JSON-LD object that was constructed. - */ - private def entityToJsonLDObject(subj: RdfResource, - statements: Set[Statement], - model: RdfModel, - topLevelEntities: collection.mutable.Map[RdfResource, JsonLDObject], - processedSubjects: collection.mutable.Set[RdfResource], - flatJsonLD: Boolean)(implicit stringFormatter: StringFormatter): JsonLDObject = { + * Converts an RDF entity to a [[JsonLDObject]] representing the statements about the entity. + * + * @param subj the subject of the entity. + * @param statements the statements representing the entity. + * @param model the [[RdfModel]] that is being read. + * @param topLevelEntities the top-level entities that have been constructed so far. + * @param processedSubjects the subjects that have already been processed. + * @param flatJsonLD if `true`, produce flat JSON-LD. + * @return the JSON-LD object that was constructed. + */ + private def entityToJsonLDObject( + subj: RdfResource, + statements: Set[Statement], + model: RdfModel, + topLevelEntities: collection.mutable.Map[RdfResource, JsonLDObject], + processedSubjects: collection.mutable.Set[RdfResource], + flatJsonLD: Boolean + )(implicit stringFormatter: StringFormatter): JsonLDObject = { // Mark the subject as processed. processedSubjects += subj @@ -1610,12 +1596,12 @@ object JsonLDUtil { } /** - * Converts an [[RdfLiteral]] to a [[JsonLDValue]]. - * - * @param literal the literal to be converted. - * @return the corresponding JSON-LD value. - */ - private def rdfLiteralToJsonLDValue(literal: RdfLiteral)(implicit stringFormatter: StringFormatter): JsonLDValue = { + * Converts an [[RdfLiteral]] to a [[JsonLDValue]]. + * + * @param literal the literal to be converted. + * @return the corresponding JSON-LD value. + */ + private def rdfLiteralToJsonLDValue(literal: RdfLiteral)(implicit stringFormatter: StringFormatter): JsonLDValue = literal match { case stringWithLanguage: StringWithLanguage => objectWithLangToJsonLDObject(obj = stringWithLanguage.value, lang = stringWithLanguage.language) @@ -1632,13 +1618,15 @@ object JsonLDUtil { JsonLDInt( allCatch .opt(datatypeValue.toInt) - .getOrElse(throw InvalidRdfException(s"Invalid integer: $datatypeValue"))) + .getOrElse(throw InvalidRdfException(s"Invalid integer: $datatypeValue")) + ) case OntologyConstants.Xsd.Boolean => JsonLDBoolean( allCatch .opt(datatypeValue.toBoolean) - .getOrElse(throw InvalidRdfException(s"Invalid boolean: $datatypeValue"))) + .getOrElse(throw InvalidRdfException(s"Invalid boolean: $datatypeValue")) + ) case _ => // There's no native JSON-LD type for this literal. @@ -1646,37 +1634,37 @@ object JsonLDUtil { datatypeValueToJsonLDObject(value = datatypeValue, datatype = datatypeIri.toSmartIri) } } - } /** - * Given an [[RdfResource]] that is referred to by another entity, make a [[JsonLDValue]] to - * represent the referenced resource. This will be either a complete entity for nesting, or just - * the referenced resource's IRI. - * - * @param resource the resource to be converted. - * @param model the [[RdfModel]] that is being read. - * @param topLevelEntities the top-level entities that have been constructed so far. - * @param referrerIsBlankNode `true` if the referrer is a blank node. If the referenced resource has an IRI, - * it will not be inlined. - * @param processedSubjects the subjects that have already been processed. - * @param flatJsonLD if `true` and the resource has an IRI, do not inline it, regardless of the referrer. - * @return a JSON-LD value representing the resource. - */ + * Given an [[RdfResource]] that is referred to by another entity, make a [[JsonLDValue]] to + * represent the referenced resource. This will be either a complete entity for nesting, or just + * the referenced resource's IRI. + * + * @param resource the resource to be converted. + * @param model the [[RdfModel]] that is being read. + * @param topLevelEntities the top-level entities that have been constructed so far. + * @param referrerIsBlankNode `true` if the referrer is a blank node. If the referenced resource has an IRI, + * it will not be inlined. + * @param processedSubjects the subjects that have already been processed. + * @param flatJsonLD if `true` and the resource has an IRI, do not inline it, regardless of the referrer. + * @return a JSON-LD value representing the resource. + */ private def referencedRdfResourceToJsonLDValue( - resource: RdfResource, - model: RdfModel, - topLevelEntities: collection.mutable.Map[RdfResource, JsonLDObject], - referrerIsBlankNode: Boolean, - processedSubjects: collection.mutable.Set[RdfResource], - flatJsonLD: Boolean)(implicit stringFormatter: StringFormatter): JsonLDValue = { + resource: RdfResource, + model: RdfModel, + topLevelEntities: collection.mutable.Map[RdfResource, JsonLDObject], + referrerIsBlankNode: Boolean, + processedSubjects: collection.mutable.Set[RdfResource], + flatJsonLD: Boolean + )(implicit stringFormatter: StringFormatter): JsonLDValue = { /** - * Inlines a resource if possible, otherwise calls the specified function. - * - * @param nonInliningFunction a function to be called if the resource cannot be inlined. - * @return a [[JsonLDValue]] representing the resource. - */ - def inlineResource(nonInliningFunction: => JsonLDValue): JsonLDValue = { + * Inlines a resource if possible, otherwise calls the specified function. + * + * @param nonInliningFunction a function to be called if the resource cannot be inlined. + * @return a [[JsonLDValue]] representing the resource. + */ + def inlineResource(nonInliningFunction: => JsonLDValue): JsonLDValue = // How we deal with circular references: the referenced resource is not yet in topLevelEntities, but it // is already marked as processed. Therefore we will return its IRI rather than inlining it. @@ -1707,7 +1695,6 @@ object JsonLDUtil { nonInliningFunction } } - } // Is this resource an IRI? resource match { @@ -1716,9 +1703,11 @@ object JsonLDUtil { // - We were asked for flat JSON-LD. // - The resource IRI is a Knora definition IRI. // - The referrer is a blank node. - if (flatJsonLD || - iriNode.iri.toSmartIri.isKnoraDefinitionIri || - referrerIsBlankNode) { + if ( + flatJsonLD || + iriNode.iri.toSmartIri.isKnoraDefinitionIri || + referrerIsBlankNode + ) { // Yes. Don't try to inline the resource, just return its IRI. iriToJsonLDObject(iriNode.iri) } else { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala index 31e6fbc14d..2178b428d1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala @@ -26,13 +26,13 @@ import org.knora.webapi.messages.util.rdf.rdf4jimpl._ import org.knora.webapi.settings.KnoraSettingsImpl /** - * A feature factory that creates RDF processing tools. - */ + * A feature factory that creates RDF processing tools. + */ object RdfFeatureFactory extends FeatureFactory { /** - * The name of the feature toggle that enables the Jena implementation of the RDF façade. - */ + * The name of the feature toggle that enables the Jena implementation of the RDF façade. + */ private val JENA_TOGGLE_NAME = "jena-rdf-library" // Jena singletons. @@ -48,11 +48,11 @@ object RdfFeatureFactory extends FeatureFactory { private var rdf4jShaclValidator: Option[RDF4JShaclValidator] = None /** - * Initialises the [[RdfFeatureFactory]]. This method must be called once, on application startup. - * - * @param settings the application settings. - */ - def init(settings: KnoraSettingsImpl): Unit = { + * Initialises the [[RdfFeatureFactory]]. This method must be called once, on application startup. + * + * @param settings the application settings. + */ + def init(settings: KnoraSettingsImpl): Unit = // Construct the SHACL validators, which need the application settings. this.synchronized { jenaShaclValidator = Some( @@ -71,49 +71,45 @@ object RdfFeatureFactory extends FeatureFactory { ) ) } - } /** - * Returns an [[RdfModelFactory]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @return an [[RdfModelFactory]]. - */ - def getRdfModelFactory(featureFactoryConfig: FeatureFactoryConfig): RdfModelFactory = { + * Returns an [[RdfModelFactory]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @return an [[RdfModelFactory]]. + */ + def getRdfModelFactory(featureFactoryConfig: FeatureFactoryConfig): RdfModelFactory = if (featureFactoryConfig.getToggle(JENA_TOGGLE_NAME).isEnabled) { jenaModelFactory } else { rdf4jModelFactory } - } /** - * Returns an [[RdfNodeFactory]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @return an [[RdfNodeFactory]]. - */ - def getRdfNodeFactory(featureFactoryConfig: FeatureFactoryConfig): RdfNodeFactory = { + * Returns an [[RdfNodeFactory]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @return an [[RdfNodeFactory]]. + */ + def getRdfNodeFactory(featureFactoryConfig: FeatureFactoryConfig): RdfNodeFactory = if (featureFactoryConfig.getToggle(JENA_TOGGLE_NAME).isEnabled) { jenaNodeFactory } else { rdf4jNodeFactory } - } /** - * Returns an [[RdfFormatUtil]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @return an [[RdfFormatUtil]]. - */ - def getRdfFormatUtil(featureFactoryConfig: FeatureFactoryConfig): RdfFormatUtil = { + * Returns an [[RdfFormatUtil]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @return an [[RdfFormatUtil]]. + */ + def getRdfFormatUtil(featureFactoryConfig: FeatureFactoryConfig): RdfFormatUtil = if (featureFactoryConfig.getToggle(JENA_TOGGLE_NAME).isEnabled) { jenaFormatUtil } else { rdf4jFormatUtil } - } def getShaclValidator(featureFactoryConfig: FeatureFactoryConfig): ShaclValidator = { def notInitialised: Nothing = throw AssertionException("RdfFeatureFactory has not been initialised") diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtil.scala index e2cdeeb1d1..79ad5c4552 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtil.scala @@ -29,41 +29,41 @@ import org.knora.webapi.{IRI, RdfMediaTypes, SchemaOption, SchemaOptions} import scala.util.{Failure, Success, Try} /** - * A trait for supported RDF formats. - */ + * A trait for supported RDF formats. + */ sealed trait RdfFormat { /** - * The [[MediaType]] that represents this format. - */ + * The [[MediaType]] that represents this format. + */ val toMediaType: MediaType } /** - * A trait for formats other than JSON-LD. - */ + * A trait for formats other than JSON-LD. + */ sealed trait NonJsonLD extends RdfFormat { /** - * `true` if this format supports pretty-printing. - */ + * `true` if this format supports pretty-printing. + */ def supportsPrettyPrinting: Boolean } /** - * Represents a format that supports quads. - */ + * Represents a format that supports quads. + */ sealed trait QuadFormat extends NonJsonLD object RdfFormat { /** - * Converts a [[MediaType]] to an [[RdfFormat]]. - * - * @param mediaType a [[MediaType]]. - * @return the corresponding [[RdfFormat]]. - */ - def fromMediaType(mediaType: MediaType): RdfFormat = { + * Converts a [[MediaType]] to an [[RdfFormat]]. + * + * @param mediaType a [[MediaType]]. + * @return the corresponding [[RdfFormat]]. + */ + def fromMediaType(mediaType: MediaType): RdfFormat = mediaType match { case RdfMediaTypes.`application/ld+json` => JsonLD case RdfMediaTypes.`text/turtle` => Turtle @@ -72,12 +72,11 @@ object RdfFormat { case RdfMediaTypes.`application/n-quads` => NQuads case other => throw InvalidRdfException(s"Unsupported RDF media type: $other") } - } } /** - * Represents JSON-LD format. - */ + * Represents JSON-LD format. + */ case object JsonLD extends RdfFormat { override def toString: String = "JSON-LD" @@ -85,8 +84,8 @@ case object JsonLD extends RdfFormat { } /** - * Represents Turtle format. - */ + * Represents Turtle format. + */ case object Turtle extends NonJsonLD { override def toString: String = "Turtle" @@ -96,8 +95,8 @@ case object Turtle extends NonJsonLD { } /** - * Represents TriG format. - */ + * Represents TriG format. + */ case object TriG extends QuadFormat { override def toString: String = "TriG" @@ -107,8 +106,8 @@ case object TriG extends QuadFormat { } /** - * Represents RDF-XML format. - */ + * Represents RDF-XML format. + */ case object RdfXml extends NonJsonLD { override def toString: String = "RDF/XML" @@ -118,8 +117,8 @@ case object RdfXml extends NonJsonLD { } /** - * Represents N-Quads format. - */ + * Represents N-Quads format. + */ case object NQuads extends QuadFormat { override def toString: String = "N-Quads" @@ -129,68 +128,68 @@ case object NQuads extends QuadFormat { } /** - * A trait for classes that process streams of RDF data. - */ + * A trait for classes that process streams of RDF data. + */ trait RdfStreamProcessor { /** - * Signals the start of the RDF data. - */ + * Signals the start of the RDF data. + */ def start(): Unit /** - * Processes a namespace declaration. - * - * @param prefix the prefix. - * @param namespace the namespace. - */ + * Processes a namespace declaration. + * + * @param prefix the prefix. + * @param namespace the namespace. + */ def processNamespace(prefix: String, namespace: IRI): Unit /** - * Processes a statement. - * - * @param statement the statement. - */ + * Processes a statement. + * + * @param statement the statement. + */ def processStatement(statement: Statement): Unit /** - * Signals the end of the RDF data. - */ + * Signals the end of the RDF data. + */ def finish(): Unit } /** - * Represents a source of RDF data to be processed using an [[RdfStreamProcessor]]. - */ + * Represents a source of RDF data to be processed using an [[RdfStreamProcessor]]. + */ sealed trait RdfSource /** - * An [[RdfSource]] that reads RDF data from a string. - * - * @param rdfStr a string containing RDF data. - */ + * An [[RdfSource]] that reads RDF data from a string. + * + * @param rdfStr a string containing RDF data. + */ case class RdfStringSource(rdfStr: String) extends RdfSource /** - * An [[RdfSource]] that reads data from an [[InputStream]]. - * - * @param inputStream the input stream. - */ + * An [[RdfSource]] that reads data from an [[InputStream]]. + * + * @param inputStream the input stream. + */ case class RdfInputStreamSource(inputStream: InputStream) extends RdfSource /** - * Formats and parses RDF. - */ + * Formats and parses RDF. + */ trait RdfFormatUtil { /** - * Parses an RDF string to an [[RdfModel]]. - * - * @param rdfStr the RDF string to be parsed. - * @param rdfFormat the format of the string. - * @return the corresponding [[RdfModel]]. - */ - def parseToRdfModel(rdfStr: String, rdfFormat: RdfFormat): RdfModel = { + * Parses an RDF string to an [[RdfModel]]. + * + * @param rdfStr the RDF string to be parsed. + * @param rdfFormat the format of the string. + * @return the corresponding [[RdfModel]]. + */ + def parseToRdfModel(rdfStr: String, rdfFormat: RdfFormat): RdfModel = rdfFormat match { case JsonLD => // Use JsonLDUtil to parse JSON-LD, and to convert the @@ -201,17 +200,16 @@ trait RdfFormatUtil { // Use an implementation-specific function to parse other formats. parseNonJsonLDToRdfModel(rdfStr = rdfStr, rdfFormat = nonJsonLD) } - } /** - * Parses an RDF string to a [[JsonLDDocument]]. - * - * @param rdfStr the RDF string to be parsed. - * @param rdfFormat the format of the string. - * @param flatJsonLD if `true`, return flat JSON-LD. - * @return the corresponding [[JsonLDDocument]]. - */ - def parseToJsonLDDocument(rdfStr: String, rdfFormat: RdfFormat, flatJsonLD: Boolean = false): JsonLDDocument = { + * Parses an RDF string to a [[JsonLDDocument]]. + * + * @param rdfStr the RDF string to be parsed. + * @param rdfFormat the format of the string. + * @param flatJsonLD if `true`, return flat JSON-LD. + * @return the corresponding [[JsonLDDocument]]. + */ + def parseToJsonLDDocument(rdfStr: String, rdfFormat: RdfFormat, flatJsonLD: Boolean = false): JsonLDDocument = rdfFormat match { case JsonLD => // Use JsonLDUtil to parse JSON-LD. @@ -225,21 +223,22 @@ trait RdfFormatUtil { flatJsonLD = flatJsonLD ) } - } /** - * Converts an [[RdfModel]] to a string. - * - * @param rdfModel the model to be formatted. - * @param rdfFormat the format to be used. - * @param schemaOptions the schema options that were submitted with the request. - * @param prettyPrint if `true`, the output should be pretty-printed. - * @return a string representation of the RDF model. - */ - def format(rdfModel: RdfModel, - rdfFormat: RdfFormat, - schemaOptions: Set[SchemaOption] = Set.empty, - prettyPrint: Boolean = true): String = { + * Converts an [[RdfModel]] to a string. + * + * @param rdfModel the model to be formatted. + * @param rdfFormat the format to be used. + * @param schemaOptions the schema options that were submitted with the request. + * @param prettyPrint if `true`, the output should be pretty-printed. + * @return a string representation of the RDF model. + */ + def format( + rdfModel: RdfModel, + rdfFormat: RdfFormat, + schemaOptions: Set[SchemaOption] = Set.empty, + prettyPrint: Boolean = true + ): String = rdfFormat match { case JsonLD => // Use JsonLDUtil to convert to JSON-LD. @@ -271,92 +270,88 @@ trait RdfFormatUtil { prettyPrint = prettyPrint ) } - } /** - * Reads an RDF file into an [[RdfModel]]. - * - * @param file the file. - * @param rdfFormat the file format. - * @return a [[RdfModel]] representing the contents of the file. - */ - def fileToRdfModel(file: Path, rdfFormat: NonJsonLD): RdfModel = { + * Reads an RDF file into an [[RdfModel]]. + * + * @param file the file. + * @param rdfFormat the file format. + * @return a [[RdfModel]] representing the contents of the file. + */ + def fileToRdfModel(file: Path, rdfFormat: NonJsonLD): RdfModel = inputStreamToRdfModel( inputStream = new BufferedInputStream(Files.newInputStream(file)), rdfFormat = rdfFormat ) - } /** - * Writes an [[RdfModel]] to a file. - * - * @param rdfModel the [[RdfModel]]. - * @param file the file to be written. - * @param rdfFormat the file format. - */ - def rdfModelToFile(rdfModel: RdfModel, file: Path, rdfFormat: NonJsonLD): Unit = { + * Writes an [[RdfModel]] to a file. + * + * @param rdfModel the [[RdfModel]]. + * @param file the file to be written. + * @param rdfFormat the file format. + */ + def rdfModelToFile(rdfModel: RdfModel, file: Path, rdfFormat: NonJsonLD): Unit = rdfModelToOutputStream( rdfModel = rdfModel, outputStream = new BufferedOutputStream(Files.newOutputStream(file)), rdfFormat = rdfFormat ) - } /** - * Parses RDF input, processing it with an [[RdfStreamProcessor]]. If the source is an input - * stream, this method closes it before returning. The caller must close any output stream - * used by the [[RdfStreamProcessor]]. - * - * @param rdfSource the input source from which the RDF data should be read. - * @param rdfFormat the input format. - * @param rdfStreamProcessor the [[RdfStreamProcessor]] that will be used to process the input. - */ + * Parses RDF input, processing it with an [[RdfStreamProcessor]]. If the source is an input + * stream, this method closes it before returning. The caller must close any output stream + * used by the [[RdfStreamProcessor]]. + * + * @param rdfSource the input source from which the RDF data should be read. + * @param rdfFormat the input format. + * @param rdfStreamProcessor the [[RdfStreamProcessor]] that will be used to process the input. + */ def parseWithStreamProcessor(rdfSource: RdfSource, rdfFormat: NonJsonLD, rdfStreamProcessor: RdfStreamProcessor): Unit /** - * Reads RDF data from an [[InputStream]] and returns it as an [[RdfModel]]. Closes the input stream - * before returning. - * - * @param inputStream the input stream. - * @param rdfFormat the data format. - * @return the corresponding [[RdfModel]]. - */ + * Reads RDF data from an [[InputStream]] and returns it as an [[RdfModel]]. Closes the input stream + * before returning. + * + * @param inputStream the input stream. + * @param rdfFormat the data format. + * @return the corresponding [[RdfModel]]. + */ def inputStreamToRdfModel(inputStream: InputStream, rdfFormat: NonJsonLD): RdfModel /** - * Formats an [[RdfModel]], writing the output to an [[OutputStream]]. Closes the output stream before - * returning. - * - * @param rdfModel the model to be written. - * @param outputStream the output stream. - * @param rdfFormat the output format. - */ + * Formats an [[RdfModel]], writing the output to an [[OutputStream]]. Closes the output stream before + * returning. + * + * @param rdfModel the model to be written. + * @param outputStream the output stream. + * @param rdfFormat the output format. + */ def rdfModelToOutputStream(rdfModel: RdfModel, outputStream: OutputStream, rdfFormat: NonJsonLD): Unit /** - * Creates an [[RdfStreamProcessor]] that writes formatted output. - * - * @param outputStream the output stream to which the formatted RDF data should be written. - * @param rdfFormat the output format. - * @return an an [[RdfStreamProcessor]]. - */ + * Creates an [[RdfStreamProcessor]] that writes formatted output. + * + * @param outputStream the output stream to which the formatted RDF data should be written. + * @param rdfFormat the output format. + * @return an an [[RdfStreamProcessor]]. + */ def makeFormattingStreamProcessor(outputStream: OutputStream, rdfFormat: NonJsonLD): RdfStreamProcessor /** - * Adds a context IRI to RDF statements. - * - * @param graphIri the IRI of the named graph. - * @param formattingStreamProcessor an [[RdfStreamProcessor]] for writing the result. - */ + * Adds a context IRI to RDF statements. + * + * @param graphIri the IRI of the named graph. + * @param formattingStreamProcessor an [[RdfStreamProcessor]] for writing the result. + */ private class ContextAddingProcessor(graphIri: IRI, formattingStreamProcessor: RdfStreamProcessor) extends RdfStreamProcessor { private val nodeFactory: RdfNodeFactory = getRdfNodeFactory override def start(): Unit = formattingStreamProcessor.start() - override def processNamespace(prefix: String, namespace: IRI): Unit = { + override def processNamespace(prefix: String, namespace: IRI): Unit = formattingStreamProcessor.processNamespace(prefix = prefix, namespace = namespace) - } override def processStatement(statement: Statement): Unit = { val outputStatement = nodeFactory.makeStatement( @@ -373,15 +368,15 @@ trait RdfFormatUtil { } /** - * Reads RDF data in Turtle format from an [[RdfSource]], adds a named graph IRI to each statement, - * and writes the result to a file in a format that supports quads. If the source is an input - * stream, this method closes it before returning. - * - * @param rdfSource the RDF data source. - * @param graphIri the named graph IRI to be added. - * @param outputFile the output file. - * @param outputFormat the output file format. - */ + * Reads RDF data in Turtle format from an [[RdfSource]], adds a named graph IRI to each statement, + * and writes the result to a file in a format that supports quads. If the source is an input + * stream, this method closes it before returning. + * + * @param rdfSource the RDF data source. + * @param graphIri the named graph IRI to be added. + * @param outputFile the output file. + * @param outputFormat the output file format. + */ def turtleToQuadsFile(rdfSource: RdfSource, graphIri: IRI, outputFile: Path, outputFormat: QuadFormat): Unit = { var maybeBufferedFileOutputStream: Option[BufferedOutputStream] = None @@ -415,31 +410,31 @@ trait RdfFormatUtil { } /** - * Returns an [[RdfModelFactory]] with the same underlying implementation as this [[RdfFormatUtil]]. - */ + * Returns an [[RdfModelFactory]] with the same underlying implementation as this [[RdfFormatUtil]]. + */ def getRdfModelFactory: RdfModelFactory /** - * Returns an [[RdfNodeFactory]] with the same underlying implementation as this [[RdfFormatUtil]]. - */ + * Returns an [[RdfNodeFactory]] with the same underlying implementation as this [[RdfFormatUtil]]. + */ def getRdfNodeFactory: RdfNodeFactory /** - * Parses RDF in a format other than JSON-LD to an [[RdfModel]]. - * - * @param rdfStr the RDF string to be parsed. - * @param rdfFormat the format of the string. - * @return the corresponding [[RdfModel]]. - */ + * Parses RDF in a format other than JSON-LD to an [[RdfModel]]. + * + * @param rdfStr the RDF string to be parsed. + * @param rdfFormat the format of the string. + * @return the corresponding [[RdfModel]]. + */ protected def parseNonJsonLDToRdfModel(rdfStr: String, rdfFormat: NonJsonLD): RdfModel /** - * Converts an [[RdfModel]] to a string in a format other than JSON-LD. - * - * @param rdfModel the model to be formatted. - * @param rdfFormat the format to be used. - * @param prettyPrint if `true`, the output should be pretty-printed. - * @return a string representation of the RDF model. - */ + * Converts an [[RdfModel]] to a string in a format other than JSON-LD. + * + * @param rdfModel the model to be formatted. + * @param rdfFormat the format to be used. + * @param prettyPrint if `true`, the output should be pretty-printed. + * @return a string representation of the RDF model. + */ protected def formatNonJsonLD(rdfModel: RdfModel, rdfFormat: NonJsonLD, prettyPrint: Boolean): String } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfModel.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfModel.scala index df95fed09e..799ae35555 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfModel.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfModel.scala @@ -26,101 +26,98 @@ import org.knora.webapi.messages.OntologyConstants import scala.util.control.Exception.allCatch /** - * Represents an RDF subject, predicate, or object. - */ + * Represents an RDF subject, predicate, or object. + */ trait RdfNode { /** - * The lexical representation of this node. - */ + * The lexical representation of this node. + */ def stringValue: String } /** - * Represents an RDF node that can be used as a subject. - */ + * Represents an RDF node that can be used as a subject. + */ trait RdfResource extends RdfNode /** - * Represents a blank node. - */ + * Represents a blank node. + */ trait BlankNode extends RdfResource { def id: String } /** - * Represents an IRI used as an RDF resource. - */ + * Represents an IRI used as an RDF resource. + */ trait IriNode extends RdfResource { def iri: IRI } /** - * Represents an RDF literal. - */ + * Represents an RDF literal. + */ trait RdfLiteral extends RdfNode /** - * Represents a literal value with a datatype. - */ + * Represents a literal value with a datatype. + */ trait DatatypeLiteral extends RdfLiteral { /** - * The lexical value of this literal. - */ + * The lexical value of this literal. + */ def value: String /** - * The datatype IRI of this literal. - */ + * The datatype IRI of this literal. + */ def datatype: IRI /** - * The boolean value of this literal. - * - * @param errorFun a function that throws an exception. It will - * be called if this literal is not a boolean. - */ - def booleanValue(errorFun: => Nothing): Boolean = { + * The boolean value of this literal. + * + * @param errorFun a function that throws an exception. It will + * be called if this literal is not a boolean. + */ + def booleanValue(errorFun: => Nothing): Boolean = if (datatype == OntologyConstants.Xsd.Boolean) { allCatch.opt(value.toBoolean).getOrElse(errorFun) } else { errorFun } - } /** - * The integer value of this literal. - * - * @param errorFun a function that throws an exception. It will - * be called if this literal is not an integer. - */ - def integerValue(errorFun: => Nothing): BigInt = { + * The integer value of this literal. + * + * @param errorFun a function that throws an exception. It will + * be called if this literal is not an integer. + */ + def integerValue(errorFun: => Nothing): BigInt = if (OntologyConstants.Xsd.integerTypes.contains(datatype)) { allCatch.opt(BigInt(value)).getOrElse(errorFun) } else { errorFun } - } /** - * The decimal value of this literal. - * - * @param errorFun a function that throws an exception. It will - * be called if this literal is not a decimal. - */ - def decimalValue(errorFun: => Nothing): BigDecimal = { + * The decimal value of this literal. + * + * @param errorFun a function that throws an exception. It will + * be called if this literal is not a decimal. + */ + def decimalValue(errorFun: => Nothing): BigDecimal = if (datatype == OntologyConstants.Xsd.Decimal) { allCatch.opt(BigDecimal(value)).getOrElse(errorFun) } else { errorFun } - } } /** - * Represents a string value with a language tag. - */ + * Represents a string value with a language tag. + */ trait StringWithLanguage extends RdfLiteral { def value: String @@ -128,8 +125,8 @@ trait StringWithLanguage extends RdfLiteral { } /** - * Represents an RDF statement. - */ + * Represents an RDF statement. + */ trait Statement { def subj: RdfResource @@ -140,38 +137,35 @@ trait Statement { def context: Option[IRI] /** - * Returns the object of this statement as an [[RdfResource]]. - */ - def getResourceObject: RdfResource = { + * Returns the object of this statement as an [[RdfResource]]. + */ + def getResourceObject: RdfResource = obj match { case rdfResource: RdfResource => rdfResource case _ => throw InvalidRdfException(s"The object of $pred is not a resource") } - } /** - * Returns the object of this statement as an [[IriNode]]. - */ - def getIriObject: IriNode = { + * Returns the object of this statement as an [[IriNode]]. + */ + def getIriObject: IriNode = obj match { case iriNode: IriNode => iriNode case _ => throw InvalidRdfException(s"The object of $pred is not an IRI") } - } /** - * Returns the object of this statement as a [[BlankNode]]. - */ - def getBlankNodeObject: BlankNode = { + * Returns the object of this statement as a [[BlankNode]]. + */ + def getBlankNodeObject: BlankNode = obj match { case blankNode: BlankNode => blankNode case _ => throw InvalidRdfException(s"The object of $pred is not a blank node") } - } /** - * Returns the boolean value of the object of this statement. - */ + * Returns the boolean value of the object of this statement. + */ def getBooleanObject: Boolean = { def invalid: Nothing = throw InvalidRdfException(s"The object of $pred is not a boolean value") @@ -182,8 +176,8 @@ trait Statement { } /** - * Returns the integer value of the object of this statement. - */ + * Returns the integer value of the object of this statement. + */ def getIntegerObject: BigInt = { def invalid: Nothing = throw InvalidRdfException(s"The object of $pred is not an integer") @@ -194,8 +188,8 @@ trait Statement { } /** - * Returns the decimal value of the object of this statement. - */ + * Returns the decimal value of the object of this statement. + */ def getDecimalObject: BigDecimal = { def invalid: Nothing = throw InvalidRdfException(s"The object of $pred is not a decimal") @@ -207,265 +201,261 @@ trait Statement { } /** - * Represents an RDF model consisting of a default graph and/or one or more named graphs. - * An [[RdfModel]] is mutable, so don't try to modify it while iterating over its statements - * using an iterator. - */ + * Represents an RDF model consisting of a default graph and/or one or more named graphs. + * An [[RdfModel]] is mutable, so don't try to modify it while iterating over its statements + * using an iterator. + */ trait RdfModel extends Iterable[Statement] { /** - * Returns an [[RdfNodeFactory]] that can be used create nodes for use with this model. - */ + * Returns an [[RdfNodeFactory]] that can be used create nodes for use with this model. + */ def getNodeFactory: RdfNodeFactory /** - * Adds a statement to the model. - * - * @param statement the statement to be added. - */ + * Adds a statement to the model. + * + * @param statement the statement to be added. + */ def addStatement(statement: Statement): Unit /** - * Adds one or more statements to the model. - * - * @param statements the statements to be added. - */ - def addStatements(statements: Set[Statement]): Unit = { + * Adds one or more statements to the model. + * + * @param statements the statements to be added. + */ + def addStatements(statements: Set[Statement]): Unit = for (statement <- statements) { addStatement(statement) } - } /** - * Adds all the statements from another model to this model. - * - * @param otherModel another [[RdfModel]]. - */ - def addStatementsFromModel(otherModel: RdfModel): Unit = { + * Adds all the statements from another model to this model. + * + * @param otherModel another [[RdfModel]]. + */ + def addStatementsFromModel(otherModel: RdfModel): Unit = for (statement <- otherModel) { addStatement(statement) } - } /** - * Constructs a statement and adds it to the model. - * - * @param subj the subject. - * @param pred the predicate. - * @param obj the object. - * @param context the IRI of a named graph, or `None` to use the default graph. - */ + * Constructs a statement and adds it to the model. + * + * @param subj the subject. + * @param pred the predicate. + * @param obj the object. + * @param context the IRI of a named graph, or `None` to use the default graph. + */ def add(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI] = None): Unit /** - * Removes statements that match a pattern. - * - * @param subj the subject, or `None` to match any subject. - * @param pred the predicate, or `None` to match any predicate. - * @param obj the object, or `None` to match any object. - * @param context the IRI of a named graph, or `None` to match any graph. - */ + * Removes statements that match a pattern. + * + * @param subj the subject, or `None` to match any subject. + * @param pred the predicate, or `None` to match any predicate. + * @param obj the object, or `None` to match any object. + * @param context the IRI of a named graph, or `None` to match any graph. + */ def remove(subj: Option[RdfResource], pred: Option[IriNode], obj: Option[RdfNode], context: Option[IRI] = None): Unit /** - * Removes a statement from the model. - */ + * Removes a statement from the model. + */ def removeStatement(statement: Statement): Unit /** - * Removes a set of statements from the model. - * - * @param statements the statements to remove. - */ - def removeStatements(statements: Set[Statement]): Unit = { + * Removes a set of statements from the model. + * + * @param statements the statements to remove. + */ + def removeStatements(statements: Set[Statement]): Unit = for (statement <- statements) { removeStatement(statement) } - } /** - * Returns statements that match a pattern. - * - * @param subj the subject, or `None` to match any subject. - * @param pred the predicate, or `None` to match any predicate. - * @param obj the object, or `None` to match any object. - * @param context the IRI of a named graph, or `None` to match any graph. - * @return an iterator over the statements that match the pattern. - */ - def find(subj: Option[RdfResource], - pred: Option[IriNode], - obj: Option[RdfNode], - context: Option[IRI] = None): Iterator[Statement] - - /** - * Checks whether the model contains the specified statement. - * - * @param statement the statement. - * @return `true` if the model contains the statement. - */ + * Returns statements that match a pattern. + * + * @param subj the subject, or `None` to match any subject. + * @param pred the predicate, or `None` to match any predicate. + * @param obj the object, or `None` to match any object. + * @param context the IRI of a named graph, or `None` to match any graph. + * @return an iterator over the statements that match the pattern. + */ + def find( + subj: Option[RdfResource], + pred: Option[IriNode], + obj: Option[RdfNode], + context: Option[IRI] = None + ): Iterator[Statement] + + /** + * Checks whether the model contains the specified statement. + * + * @param statement the statement. + * @return `true` if the model contains the statement. + */ def contains(statement: Statement): Boolean /** - * Returns a set of all the subjects in the model. - */ + * Returns a set of all the subjects in the model. + */ def getSubjects: Set[RdfResource] /** - * Adds a namespace declaration to the model. - * - * @param prefix the namespace prefix. - * @param namespace the namespace. - */ + * Adds a namespace declaration to the model. + * + * @param prefix the namespace prefix. + * @param namespace the namespace. + */ def setNamespace(prefix: String, namespace: IRI): Unit /** - * Returns the namespace declarations in the model. - * - * @return a map of prefixes to namespaces. - */ + * Returns the namespace declarations in the model. + * + * @return a map of prefixes to namespaces. + */ def getNamespaces: Map[String, IRI] /** - * Returns `true` if this model is empty. - */ + * Returns `true` if this model is empty. + */ def isEmpty: Boolean /** - * Returns `true` if this model is isomorphic with another RDF model. - * - * @param otherRdfModel another [[RdfModel]]. - */ + * Returns `true` if this model is isomorphic with another RDF model. + * + * @param otherRdfModel another [[RdfModel]]. + */ def isIsomorphicWith(otherRdfModel: RdfModel): Boolean /** - * Returns the IRIs of the named graphs in the model. - */ + * Returns the IRIs of the named graphs in the model. + */ def getContexts: Set[IRI] /** - * @return the number of statements in the model. - */ + * @return the number of statements in the model. + */ def size: Int /** - * Empties this model. - */ + * Empties this model. + */ def clear(): Unit /** - * Returns an [[RdfRepository]] that can be used to query this model. - */ + * Returns an [[RdfRepository]] that can be used to query this model. + */ def asRepository: RdfRepository override def hashCode(): Int = super.hashCode() - override def equals(obj: Any): Boolean = { + override def equals(obj: Any): Boolean = obj match { case thatRdfModel: RdfModel => isIsomorphicWith(thatRdfModel) case _ => false } - } } /** - * Represents a factory that can create RDF nodes and statements. - */ + * Represents a factory that can create RDF nodes and statements. + */ trait RdfNodeFactory { /** - * Constructs a blank node with a generated ID. - * - * @return a [[BlankNode]]. - */ + * Constructs a blank node with a generated ID. + * + * @return a [[BlankNode]]. + */ def makeBlankNode: BlankNode /** - * Constructs a blank node with the specified ID. - * - * @param id the blank node ID. - * @return a [[BlankNode]]. - */ + * Constructs a blank node with the specified ID. + * + * @param id the blank node ID. + * @return a [[BlankNode]]. + */ def makeBlankNodeWithID(id: String): BlankNode /** - * Constructs an IRI node. - * - * @param iri the IRI. - * @return an [[IriNode]]. - */ + * Constructs an IRI node. + * + * @param iri the IRI. + * @return an [[IriNode]]. + */ def makeIriNode(iri: IRI): IriNode /** - * Constructs a literal value with a datatype. - * - * @param value the lexical value of the literal. - * @param datatype the datatype IRI. - * @return a [[DatatypeLiteral]]. - */ + * Constructs a literal value with a datatype. + * + * @param value the lexical value of the literal. + * @param datatype the datatype IRI. + * @return a [[DatatypeLiteral]]. + */ def makeDatatypeLiteral(value: String, datatype: IRI): DatatypeLiteral /** - * Creates an `xsd:string`. - * - * @param value the string value. - * @return a [[DatatypeLiteral]]. - */ - def makeStringLiteral(value: String): DatatypeLiteral = { + * Creates an `xsd:string`. + * + * @param value the string value. + * @return a [[DatatypeLiteral]]. + */ + def makeStringLiteral(value: String): DatatypeLiteral = makeDatatypeLiteral(value = value, datatype = OntologyConstants.Xsd.String) - } /** - * Constructs a string with a language tag. - * - * @param value the string. - * @param language the language tag. - */ + * Constructs a string with a language tag. + * + * @param value the string. + * @param language the language tag. + */ def makeStringWithLanguage(value: String, language: String): StringWithLanguage /** - * Constructs an `xsd:boolean`. - * - * @param value the boolean value. - * @return a [[DatatypeLiteral]]. - */ - def makeBooleanLiteral(value: Boolean): DatatypeLiteral = { + * Constructs an `xsd:boolean`. + * + * @param value the boolean value. + * @return a [[DatatypeLiteral]]. + */ + def makeBooleanLiteral(value: Boolean): DatatypeLiteral = makeDatatypeLiteral(value = value.toString, datatype = OntologyConstants.Xsd.Boolean) - } /** - * Constructs a statement. - * - * @param subj the subject. - * @param pred the predicate. - * @param obj the object. - * @param context the IRI of the named graph, or `None` to use the default graph. - */ + * Constructs a statement. + * + * @param subj the subject. + * @param pred the predicate. + * @param obj the object. + * @param context the IRI of the named graph, or `None` to use the default graph. + */ def makeStatement(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI] = None): Statement } /** - * A factory that creates [[RdfModel]] instances. - */ + * A factory that creates [[RdfModel]] instances. + */ trait RdfModelFactory { def makeEmptyModel: RdfModel } /** - * Represents a simple in-memory repository based on an [[RdfModel]]. - */ + * Represents a simple in-memory repository based on an [[RdfModel]]. + */ trait RdfRepository { /** - * Does a SPARQL SELECT query. - * - * @param selectQuery the query. - * @return the query result. - */ + * Does a SPARQL SELECT query. + * + * @param selectQuery the query. + * @return the query result. + */ def doSelect(selectQuery: String): SparqlSelectResult /** - * Shuts down this repository. The underlying [[RdfModel]] may not be usable after its - * [[RdfRepository]] has been shut down. - */ + * Shuts down this repository. The underlying [[RdfModel]] may not be usable after its + * [[RdfRepository]] has been shut down. + */ def shutDown(): Unit } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/ShaclValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/ShaclValidator.scala index de7df74c7a..f3b1027e33 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/ShaclValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/ShaclValidator.scala @@ -22,33 +22,33 @@ package org.knora.webapi.messages.util.rdf import java.nio.file.Path /** - * A trait for the results of SHACL validation operations. - */ + * A trait for the results of SHACL validation operations. + */ sealed trait ShaclValidationResult /** - * Indicates that data passed SHACL validation. - */ + * Indicates that data passed SHACL validation. + */ case object ShaclValidationSuccess extends ShaclValidationResult /** - * Indicates that data did not pass SHACL validation. - * - * @param reportModel an [[RdfModel]] describing the failure. - */ + * Indicates that data did not pass SHACL validation. + * + * @param reportModel an [[RdfModel]] describing the failure. + */ case class ShaclValidationFailure(reportModel: RdfModel) extends ShaclValidationResult /** - * A trait for classes that validate RDF models using SHACL shapes. - */ + * A trait for classes that validate RDF models using SHACL shapes. + */ trait ShaclValidator { /** - * Validates the default graph of an [[RdfModel]] using SHACL shapes. - * - * @param rdfModel the model to be validated. - * @param shaclPath a path identifying the graph of SHACL shapes to be used. - * @return a [[ShaclValidationResult]]. - */ + * Validates the default graph of an [[RdfModel]] using SHACL shapes. + * + * @param rdfModel the model to be validated. + * @param shaclPath a path identifying the graph of SHACL shapes to be used. + * @return a [[ShaclValidationResult]]. + */ def validate(rdfModel: RdfModel, shaclPath: Path): ShaclValidationResult } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/SparqlSelectResult.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/SparqlSelectResult.scala index 8dd2a0bf8f..4767832c68 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/SparqlSelectResult.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/SparqlSelectResult.scala @@ -22,51 +22,53 @@ package org.knora.webapi.messages.util.rdf import org.knora.webapi.exceptions.InconsistentRepositoryDataException /** - * Represents the result of a SPARQL SELECT query. - * - * @param head the header of the response, containing the variable names. - * @param results the body of the response, containing rows of query results. - */ + * Represents the result of a SPARQL SELECT query. + * + * @param head the header of the response, containing the variable names. + * @param results the body of the response, containing rows of query results. + */ case class SparqlSelectResult(head: SparqlSelectResultHeader, results: SparqlSelectResultBody) { /** - * Returns the contents of the first row of results. - * - * @return a [[Map]] representing the contents of the first row of results. - */ - def getFirstRow: VariableResultsRow = { + * Returns the contents of the first row of results. + * + * @return a [[Map]] representing the contents of the first row of results. + */ + def getFirstRow: VariableResultsRow = results.bindings.headOption match { case Some(row: VariableResultsRow) => row case None => throw InconsistentRepositoryDataException(s"A SPARQL query unexpectedly returned an empty result") } - } } /** - * Represents the header of the result of a SPARQL SELECT query. - * - * @param vars the names of the variables that were used in the SPARQL SELECT statement. - */ + * Represents the header of the result of a SPARQL SELECT query. + * + * @param vars the names of the variables that were used in the SPARQL SELECT statement. + */ case class SparqlSelectResultHeader(vars: Seq[String]) /** - * Represents the body of the result of a SPARQL SELECT query. - * - * @param bindings the bindings of values to the variables used in the SPARQL SELECT statement. - * Empty rows are not allowed. - */ + * Represents the body of the result of a SPARQL SELECT query. + * + * @param bindings the bindings of values to the variables used in the SPARQL SELECT statement. + * Empty rows are not allowed. + */ case class SparqlSelectResultBody(bindings: Seq[VariableResultsRow]) { require(bindings.forall(_.rowMap.nonEmpty), "Empty rows are not allowed in a SparqlSelectResponseBody") } /** - * Represents a row of results in the result of a SPARQL SELECT query. - * - * @param rowMap a map of variable names to values in the row. An empty string is not allowed as a variable - * name or value. - */ + * Represents a row of results in the result of a SPARQL SELECT query. + * + * @param rowMap a map of variable names to values in the row. An empty string is not allowed as a variable + * name or value. + */ case class VariableResultsRow(rowMap: Map[String, String]) { - require(rowMap.forall { - case (key, value) => key.nonEmpty && value.nonEmpty - }, "An empty string is not allowed as a variable name or value in a VariableResultsRow") + require( + rowMap.forall { case (key, value) => + key.nonEmpty && value.nonEmpty + }, + "An empty string is not allowed as a variable name or value in a VariableResultsRow" + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtil.scala index 27341293f6..f2549af010 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtil.scala @@ -29,53 +29,48 @@ import org.knora.webapi.messages.util.rdf._ import scala.util.{Failure, Success, Try} /** - * Wraps an [[RdfStreamProcessor]] in a [[jena.riot.system.StreamRDF]]. - */ + * Wraps an [[RdfStreamProcessor]] in a [[jena.riot.system.StreamRDF]]. + */ class StreamProcessorAsStreamRDF(streamProcessor: RdfStreamProcessor) extends jena.riot.system.StreamRDF { override def start(): Unit = streamProcessor.start() - override def triple(triple: jena.graph.Triple): Unit = { + override def triple(triple: jena.graph.Triple): Unit = streamProcessor.processStatement( JenaStatement(jena.sparql.core.Quad.create(jena.sparql.core.Quad.defaultGraphIRI, triple)) ) - } - override def quad(quad: jena.sparql.core.Quad): Unit = { + override def quad(quad: jena.sparql.core.Quad): Unit = streamProcessor.processStatement(JenaStatement(quad)) - } override def base(s: String): Unit = {} - override def prefix(prefixStr: String, namespace: String): Unit = { + override def prefix(prefixStr: String, namespace: String): Unit = streamProcessor.processNamespace(prefixStr, namespace) - } override def finish(): Unit = streamProcessor.finish() } /** - * Wraps a [[jena.riot.system.StreamRDF]] in a [[RdfStreamProcessor]]. - */ + * Wraps a [[jena.riot.system.StreamRDF]] in a [[RdfStreamProcessor]]. + */ class StreamRDFAsStreamProcessor(streamRDF: jena.riot.system.StreamRDF) extends RdfStreamProcessor { import JenaConversions._ override def start(): Unit = streamRDF.start() - override def processNamespace(prefix: String, namespace: IRI): Unit = { + override def processNamespace(prefix: String, namespace: IRI): Unit = streamRDF.prefix(prefix, namespace) - } - override def processStatement(statement: Statement): Unit = { + override def processStatement(statement: Statement): Unit = streamRDF.quad(statement.asJenaQuad) - } override def finish(): Unit = streamRDF.finish() } /** - * An implementation of [[RdfFormatUtil]] that uses the Jena API. - */ + * An implementation of [[RdfFormatUtil]] that uses the Jena API. + */ class JenaFormatUtil(private val modelFactory: JenaModelFactory, private val nodeFactory: JenaNodeFactory) extends RdfFormatUtil with Feature { @@ -83,14 +78,13 @@ class JenaFormatUtil(private val modelFactory: JenaModelFactory, private val nod override def getRdfNodeFactory: RdfNodeFactory = nodeFactory - private def rdfFormatToJenaParsingLang(rdfFormat: NonJsonLD): jena.riot.Lang = { + private def rdfFormatToJenaParsingLang(rdfFormat: NonJsonLD): jena.riot.Lang = rdfFormat match { case Turtle => jena.riot.RDFLanguages.TURTLE case TriG => jena.riot.RDFLanguages.TRIG case RdfXml => jena.riot.RDFLanguages.RDFXML case NQuads => jena.riot.RDFLanguages.NQUADS } - } override def parseNonJsonLDToRdfModel(rdfStr: String, rdfFormat: NonJsonLD): RdfModel = { val jenaModel: JenaModel = modelFactory.makeEmptyModel @@ -146,9 +140,11 @@ class JenaFormatUtil(private val modelFactory: JenaModelFactory, private val nod stringWriter.toString } - override def parseWithStreamProcessor(rdfSource: RdfSource, - rdfFormat: NonJsonLD, - rdfStreamProcessor: RdfStreamProcessor): Unit = { + override def parseWithStreamProcessor( + rdfSource: RdfSource, + rdfFormat: NonJsonLD, + rdfStreamProcessor: RdfStreamProcessor + ): Unit = { // Wrap the RdfStreamProcessor in a StreamProcessorAsStreamRDF. val streamRDF = new StreamProcessorAsStreamRDF(rdfStreamProcessor) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModel.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModel.scala index 5fa99dc7a6..c0bfc49a44 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModel.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModel.scala @@ -63,7 +63,7 @@ case class JenaStringWithLanguage(node: jena.graph.Node) extends JenaNode with S } object JenaResource { - def fromJena(node: jena.graph.Node): Option[RdfResource] = { + def fromJena(node: jena.graph.Node): Option[RdfResource] = if (node.isURI) { Some(JenaIriNode(node)) } else if (node.isBlank) { @@ -71,7 +71,6 @@ object JenaResource { } else { None } - } } case class JenaStatement(quad: jena.sparql.core.Quad) extends Statement { @@ -103,80 +102,73 @@ case class JenaStatement(quad: jena.sparql.core.Quad) extends Statement { } } - override def context: Option[IRI] = { + override def context: Option[IRI] = Option(quad.getGraph).map(_.getURI) - } } /** - * Provides extension methods for converting between Knora RDF API classes and Jena classes - * (see [[https://docs.scala-lang.org/overviews/core/value-classes.html#extension-methods Extension Methods]]). - */ + * Provides extension methods for converting between Knora RDF API classes and Jena classes + * (see [[https://docs.scala-lang.org/overviews/core/value-classes.html#extension-methods Extension Methods]]). + */ object JenaConversions { implicit class ConvertibleJenaNode(val self: RdfNode) extends AnyVal { - def asJenaNode: jena.graph.Node = { + def asJenaNode: jena.graph.Node = self match { case jenaRdfNode: JenaNode => jenaRdfNode.node case other => throw RdfProcessingException(s"$other is not a Jena node") } - } } implicit class ConvertibleJenaQuad(val self: Statement) extends AnyVal { - def asJenaQuad: jena.sparql.core.Quad = { + def asJenaQuad: jena.sparql.core.Quad = self match { case jenaStatement: JenaStatement => jenaStatement.quad case other => throw RdfProcessingException(s"$other is not a Jena statement") } - } } implicit class ConvertibleJenaModel(val self: RdfModel) extends AnyVal { - def asJenaDataset: jena.query.Dataset = { + def asJenaDataset: jena.query.Dataset = self match { case model: JenaModel => model.getDataset case other => throw RdfProcessingException(s"${other.getClass.getName} is not a Jena RDF model") } - } } } /** - * Generates Jena nodes representing contexts. - */ + * Generates Jena nodes representing contexts. + */ abstract class JenaContextFactory { /** - * Converts a named graph IRI to a [[jena.graph.Node]]. - */ - protected def contextIriToNode(context: IRI): jena.graph.Node = { + * Converts a named graph IRI to a [[jena.graph.Node]]. + */ + protected def contextIriToNode(context: IRI): jena.graph.Node = jena.graph.NodeFactory.createURI(context) - } /** - * Converts an optional named graph IRI to a [[jena.graph.Node]], converting - * `None` to the IRI of Jena's default graph. - */ - protected def contextNodeOrDefaultGraph(context: Option[IRI]): jena.graph.Node = { + * Converts an optional named graph IRI to a [[jena.graph.Node]], converting + * `None` to the IRI of Jena's default graph. + */ + protected def contextNodeOrDefaultGraph(context: Option[IRI]): jena.graph.Node = context.map(contextIriToNode).getOrElse(jena.sparql.core.Quad.defaultGraphIRI) - } /** - * Converts an optional named graph IRI to a [[jena.graph.Node]], converting - * `None` to a wildcard that will match any graph. - */ - protected def contextNodeOrWildcard(context: Option[IRI]): jena.graph.Node = { + * Converts an optional named graph IRI to a [[jena.graph.Node]], converting + * `None` to a wildcard that will match any graph. + */ + protected def contextNodeOrWildcard(context: Option[IRI]): jena.graph.Node = context.map(contextIriToNode).getOrElse(jena.graph.Node.ANY) - } } /** - * An implementation of [[RdfModel]] that wraps a [[jena.query.Dataset]]. - * - * @param dataset the underlying Jena dataset. - */ + * An implementation of [[RdfModel]] that wraps a [[jena.query.Dataset]]. + * + * @param dataset the underlying Jena dataset. + */ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory: JenaNodeFactory) extends JenaContextFactory with RdfModel @@ -193,53 +185,52 @@ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory } /** - * Returns the underlying [[jena.query.Dataset]]. - */ + * Returns the underlying [[jena.query.Dataset]]. + */ def getDataset: jena.query.Dataset = dataset override def getNodeFactory: RdfNodeFactory = nodeFactory - override def addStatement(statement: Statement): Unit = { + override def addStatement(statement: Statement): Unit = datasetGraph.add(statement.asJenaQuad) - } /** - * Converts an optional [[RdfNode]] to a [[jena.graph.Node]], converting - * `None` to a wildcard that will match any node. - */ - private def asJenaNodeOrWildcard(node: Option[RdfNode]): jena.graph.Node = { + * Converts an optional [[RdfNode]] to a [[jena.graph.Node]], converting + * `None` to a wildcard that will match any node. + */ + private def asJenaNodeOrWildcard(node: Option[RdfNode]): jena.graph.Node = node.map(_.asJenaNode).getOrElse(jena.graph.Node.ANY) - } - override def add(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI] = None): Unit = { + override def add(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI] = None): Unit = datasetGraph.add( contextNodeOrDefaultGraph(context), subj.asJenaNode, pred.asJenaNode, obj.asJenaNode ) - } - override def remove(subj: Option[RdfResource], - pred: Option[IriNode], - obj: Option[RdfNode], - context: Option[IRI] = None): Unit = { + override def remove( + subj: Option[RdfResource], + pred: Option[IriNode], + obj: Option[RdfNode], + context: Option[IRI] = None + ): Unit = datasetGraph.deleteAny( contextNodeOrWildcard(context), asJenaNodeOrWildcard(subj), asJenaNodeOrWildcard(pred), asJenaNodeOrWildcard(obj) ) - } - override def removeStatement(statement: Statement): Unit = { + override def removeStatement(statement: Statement): Unit = datasetGraph.delete(statement.asJenaQuad) - } - override def find(subj: Option[RdfResource], - pred: Option[IriNode], - obj: Option[RdfNode], - context: Option[IRI] = None): Iterator[Statement] = { + override def find( + subj: Option[RdfResource], + pred: Option[IriNode], + obj: Option[RdfNode], + context: Option[IRI] = None + ): Iterator[Statement] = new StatementIterator( datasetGraph.find( contextNodeOrWildcard(context), @@ -248,11 +239,9 @@ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory asJenaNodeOrWildcard(obj) ) ) - } - override def contains(statement: Statement): Boolean = { + override def contains(statement: Statement): Boolean = datasetGraph.contains(statement.asJenaQuad) - } override def setNamespace(prefix: String, namespace: IRI): Unit = { def setNamespaceInGraph(graph: jena.graph.Graph): Unit = { @@ -292,12 +281,11 @@ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory override def isEmpty: Boolean = dataset.isEmpty - override def getSubjects: Set[RdfResource] = { + override def getSubjects: Set[RdfResource] = datasetGraph.find.asScala.map { quad: jena.sparql.core.Quad => val subj: jena.graph.Node = quad.getSubject JenaResource.fromJena(subj).getOrElse(throw RdfProcessingException(s"Unexpected statement subject: $subj")) }.toSet - } override def isIsomorphicWith(otherRdfModel: RdfModel): Boolean = { // Jena's DatasetGraph doesn't have a method for this, so we have to do it ourselves. @@ -319,15 +307,13 @@ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory } } - override def getContexts: Set[IRI] = { + override def getContexts: Set[IRI] = datasetGraph.listGraphNodes.asScala.toSet.map { node: jena.graph.Node => node.getURI } - } - override def asRepository: RdfRepository = { + override def asRepository: RdfRepository = new JenaRepository(dataset) - } override def size: Int = { // Jena's DatasetGraph doesn't have a method for this, so we have to do it ourselves. @@ -336,10 +322,9 @@ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory val defaultGraphSize: Int = datasetGraph.getDefaultGraph.size // Get the sum of the sizes of the named graphs. - val sumOfNamedGraphSizes: Int = datasetGraph.listGraphNodes.asScala - .map { namedGraphIri => - datasetGraph.getGraph(namedGraphIri) - } + val sumOfNamedGraphSizes: Int = datasetGraph.listGraphNodes.asScala.map { namedGraphIri => + datasetGraph.getGraph(namedGraphIri) + } .map(_.size) .sum @@ -347,40 +332,35 @@ class JenaModel(private val dataset: jena.query.Dataset, private val nodeFactory defaultGraphSize + sumOfNamedGraphSizes } - override def iterator: Iterator[Statement] = { + override def iterator: Iterator[Statement] = new StatementIterator(datasetGraph.find) - } - override def clear(): Unit = { + override def clear(): Unit = datasetGraph.clear() - } } /** - * An implementation of [[RdfNodeFactory]] that creates Jena node implementation wrappers. - */ + * An implementation of [[RdfNodeFactory]] that creates Jena node implementation wrappers. + */ class JenaNodeFactory extends JenaContextFactory with RdfNodeFactory { import JenaConversions._ /** - * Represents a custom Knora datatype (used in the simple schema), for registration - * with Jena's TypeMapper. - * - * @param iri the IRI of the datatype. - */ + * Represents a custom Knora datatype (used in the simple schema), for registration + * with Jena's TypeMapper. + * + * @param iri the IRI of the datatype. + */ private class KnoraDatatype(iri: IRI) extends jena.datatypes.BaseDatatype(iri) { - override def unparse(value: java.lang.Object): String = { + override def unparse(value: java.lang.Object): String = value.toString - } - override def parse(lexicalForm: String): java.lang.Object = { + override def parse(lexicalForm: String): java.lang.Object = lexicalForm - } - override def isEqual(value1: jena.graph.impl.LiteralLabel, value2: jena.graph.impl.LiteralLabel): Boolean = { + override def isEqual(value1: jena.graph.impl.LiteralLabel, value2: jena.graph.impl.LiteralLabel): Boolean = value1.getDatatype == value2.getDatatype && value1.getValue.equals(value2.getValue) - } } // Jena's registry of datatypes. @@ -391,27 +371,22 @@ class JenaNodeFactory extends JenaContextFactory with RdfNodeFactory { typeMapper.registerDatatype(new KnoraDatatype(knoraDatatypeIri)) } - override def makeBlankNode: BlankNode = { + override def makeBlankNode: BlankNode = JenaBlankNode(jena.graph.NodeFactory.createBlankNode) - } - override def makeBlankNodeWithID(id: String): BlankNode = { + override def makeBlankNodeWithID(id: String): BlankNode = JenaBlankNode(jena.graph.NodeFactory.createBlankNode(id)) - } - override def makeIriNode(iri: IRI): IriNode = { + override def makeIriNode(iri: IRI): IriNode = JenaIriNode(jena.graph.NodeFactory.createURI(iri)) - } - override def makeDatatypeLiteral(value: String, datatype: IRI): DatatypeLiteral = { + override def makeDatatypeLiteral(value: String, datatype: IRI): DatatypeLiteral = JenaDatatypeLiteral(jena.graph.NodeFactory.createLiteral(value, typeMapper.getTypeByName(datatype))) - } - override def makeStringWithLanguage(value: String, language: String): StringWithLanguage = { + override def makeStringWithLanguage(value: String, language: String): StringWithLanguage = JenaStringWithLanguage(jena.graph.NodeFactory.createLiteral(value, language)) - } - override def makeStatement(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI]): Statement = { + override def makeStatement(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI]): Statement = JenaStatement( new jena.sparql.core.Quad( contextNodeOrDefaultGraph(context), @@ -420,13 +395,12 @@ class JenaNodeFactory extends JenaContextFactory with RdfNodeFactory { obj.asJenaNode ) ) - } } /** - * A factory for creating instances of [[JenaModel]]. - */ + * A factory for creating instances of [[JenaModel]]. + */ class JenaModelFactory(private val nodeFactory: JenaNodeFactory) extends RdfModelFactory { override def makeEmptyModel: JenaModel = new JenaModel( dataset = jena.query.DatasetFactory.create, @@ -435,10 +409,10 @@ class JenaModelFactory(private val nodeFactory: JenaNodeFactory) extends RdfMode } /** - * An [[RdfRepository]] that wraps a [[jena.query.Dataset]]. - * - * @param dataset the dataset to be queried. - */ + * An [[RdfRepository]] that wraps a [[jena.query.Dataset]]. + * + * @param dataset the dataset to be queried. + */ class JenaRepository(private val dataset: jena.query.Dataset) extends RdfRepository { override def doSelect(selectQuery: String): SparqlSelectResult = { // Run the query. @@ -472,9 +446,16 @@ class JenaRepository(private val dataset: jena.query.Dataset) extends RdfReposit varName -> varValueStr }.toMap - rowBuffer.append(VariableResultsRow(new ErrorHandlingMap[String, String](rowMap, { key: String => - s"No value found for SPARQL query variable '$key' in query result row" - }))) + rowBuffer.append( + VariableResultsRow( + new ErrorHandlingMap[String, String]( + rowMap, + { key: String => + s"No value found for SPARQL query variable '$key' in query result row" + } + ) + ) + ) } queryExecution.close() @@ -485,7 +466,6 @@ class JenaRepository(private val dataset: jena.query.Dataset) extends RdfReposit ) } - override def shutDown(): Unit = { + override def shutDown(): Unit = dataset.close() - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidator.scala index 40ab45b1f1..c689ce4757 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidator.scala @@ -26,12 +26,12 @@ import org.apache.jena.query.DatasetFactory import org.knora.webapi.messages.util.rdf._ /** - * Performs SHACL validation using Jena. - * - * @param baseDir the base directory that SHACL graphs are loaded from. - * @param rdfFormatUtil an [[JenaFormatUtil]]. - * @param nodeFactory a [[JenaNodeFactory]]. - */ + * Performs SHACL validation using Jena. + * + * @param baseDir the base directory that SHACL graphs are loaded from. + * @param rdfFormatUtil an [[JenaFormatUtil]]. + * @param nodeFactory a [[JenaNodeFactory]]. + */ class JenaShaclValidator(baseDir: Path, rdfFormatUtil: JenaFormatUtil, private val nodeFactory: JenaNodeFactory) extends AbstractShaclValidator[jena.shacl.Shapes](baseDir, rdfFormatUtil) { @@ -61,7 +61,6 @@ class JenaShaclValidator(baseDir: Path, rdfFormatUtil: JenaFormatUtil, private v } } - override def rdfModelToShaclGraph(rdfModel: RdfModel): jena.shacl.Shapes = { + override def rdfModelToShaclGraph(rdfModel: RdfModel): jena.shacl.Shapes = jena.shacl.Shapes.parse(rdfModel.asJenaDataset.asDatasetGraph.getDefaultGraph) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtil.scala index 9f5fb47105..32e18cb669 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtil.scala @@ -29,47 +29,43 @@ import org.knora.webapi.messages.util.rdf._ import scala.util.{Failure, Success, Try} /** - * Wraps an [[RdfStreamProcessor]] in an [[rdf4j.rio.RDFHandler]]. - */ + * Wraps an [[RdfStreamProcessor]] in an [[rdf4j.rio.RDFHandler]]. + */ class StreamProcessorAsRDFHandler(streamProcessor: RdfStreamProcessor) extends rdf4j.rio.RDFHandler { override def startRDF(): Unit = streamProcessor.start() override def endRDF(): Unit = streamProcessor.finish() - override def handleNamespace(prefix: String, namespace: String): Unit = { + override def handleNamespace(prefix: String, namespace: String): Unit = streamProcessor.processNamespace(prefix, namespace) - } - override def handleStatement(statement: rdf4j.model.Statement): Unit = { + override def handleStatement(statement: rdf4j.model.Statement): Unit = streamProcessor.processStatement(RDF4JStatement(statement)) - } override def handleComment(comment: String): Unit = {} } /** - * Wraps an [[rdf4j.rio.RDFHandler]] in an [[RdfStreamProcessor]]. - */ + * Wraps an [[rdf4j.rio.RDFHandler]] in an [[RdfStreamProcessor]]. + */ class RDFHandlerAsStreamProcessor(rdfWriter: rdf4j.rio.RDFHandler) extends RdfStreamProcessor { import RDF4JConversions._ override def start(): Unit = rdfWriter.startRDF() - override def processNamespace(prefix: String, namespace: IRI): Unit = { + override def processNamespace(prefix: String, namespace: IRI): Unit = rdfWriter.handleNamespace(prefix, namespace) - } - override def processStatement(statement: Statement): Unit = { + override def processStatement(statement: Statement): Unit = rdfWriter.handleStatement(statement.asRDF4JStatement) - } override def finish(): Unit = rdfWriter.endRDF() } /** - * An implementation of [[RdfFormatUtil]] that uses the RDF4J API. - */ + * An implementation of [[RdfFormatUtil]] that uses the RDF4J API. + */ class RDF4JFormatUtil(private val modelFactory: RDF4JModelFactory, private val nodeFactory: RDF4JNodeFactory) extends RdfFormatUtil with Feature { @@ -77,16 +73,15 @@ class RDF4JFormatUtil(private val modelFactory: RDF4JModelFactory, private val n override def getRdfNodeFactory: RdfNodeFactory = nodeFactory - private def rdfFormatToRDF4JFormat(rdfFormat: NonJsonLD): rdf4j.rio.RDFFormat = { + private def rdfFormatToRDF4JFormat(rdfFormat: NonJsonLD): rdf4j.rio.RDFFormat = rdfFormat match { case Turtle => rdf4j.rio.RDFFormat.TURTLE case TriG => rdf4j.rio.RDFFormat.TRIG case RdfXml => rdf4j.rio.RDFFormat.RDFXML case NQuads => rdf4j.rio.RDFFormat.NQUADS } - } - override def parseNonJsonLDToRdfModel(rdfStr: String, rdfFormat: NonJsonLD): RdfModel = { + override def parseNonJsonLDToRdfModel(rdfStr: String, rdfFormat: NonJsonLD): RdfModel = new RDF4JModel( model = rdf4j.rio.Rio.parse( new StringReader(rdfStr), @@ -95,7 +90,6 @@ class RDF4JFormatUtil(private val modelFactory: RDF4JModelFactory, private val n ), nodeFactory = nodeFactory ) - } override def formatNonJsonLD(rdfModel: RdfModel, rdfFormat: NonJsonLD, prettyPrint: Boolean): String = { import RDF4JConversions._ @@ -114,9 +108,11 @@ class RDF4JFormatUtil(private val modelFactory: RDF4JModelFactory, private val n stringWriter.toString } - override def parseWithStreamProcessor(rdfSource: RdfSource, - rdfFormat: NonJsonLD, - rdfStreamProcessor: RdfStreamProcessor): Unit = { + override def parseWithStreamProcessor( + rdfSource: RdfSource, + rdfFormat: NonJsonLD, + rdfStreamProcessor: RdfStreamProcessor + ): Unit = { // Construct an RDF4J parser for the requested format. val parser: rdf4j.rio.RDFParser = rdf4j.rio.Rio.createParser(rdfFormatToRDF4JFormat(rdfFormat)) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModel.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModel.scala index 208e654fbb..3862e944b8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModel.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModel.scala @@ -43,13 +43,12 @@ sealed trait RDF4JResource extends RDF4JNode with RdfResource { } object RDF4JResource { - def fromRDF4J(resource: rdf4j.model.Resource): RDF4JResource = { + def fromRDF4J(resource: rdf4j.model.Resource): RDF4JResource = resource match { case iri: rdf4j.model.IRI => RDF4JIriNode(iri) case blankNode: rdf4j.model.BNode => RDF4JBlankNode(blankNode) case other => throw RdfProcessingException(s"Unexpected resource: $other") } - } } case class RDF4JBlankNode(resource: rdf4j.model.BNode) extends RDF4JResource with BlankNode { @@ -80,16 +79,15 @@ case class RDF4JStringWithLanguage(literal: rdf4j.model.Literal) extends RDF4JLi } case class RDF4JStatement(statement: rdf4j.model.Statement) extends Statement { - override def subj: RdfResource = { + override def subj: RdfResource = statement.getSubject match { case resource: rdf4j.model.Resource => RDF4JResource.fromRDF4J(resource) case other => throw RdfProcessingException(s"Unexpected statement subject: $other") } - } override def pred: IriNode = RDF4JIriNode(statement.getPredicate) - override def obj: RdfNode = { + override def obj: RdfNode = statement.getObject match { case resource: rdf4j.model.Resource => RDF4JResource.fromRDF4J(resource) @@ -102,69 +100,63 @@ case class RDF4JStatement(statement: rdf4j.model.Statement) extends Statement { case other => throw RdfProcessingException(s"Unexpected statement object: $other") } - } override def context: Option[IRI] = Option(statement.getContext).map(_.stringValue) } /** - * Provides extension methods for converting between Knora RDF API classes and RDF4J classes - * (see [[https://docs.scala-lang.org/overviews/core/value-classes.html#extension-methods Extension Methods]]). - */ + * Provides extension methods for converting between Knora RDF API classes and RDF4J classes + * (see [[https://docs.scala-lang.org/overviews/core/value-classes.html#extension-methods Extension Methods]]). + */ object RDF4JConversions { implicit class ConvertibleRDF4JResource(val self: RdfResource) extends AnyVal { - def asRDF4JResource: rdf4j.model.Resource = { + def asRDF4JResource: rdf4j.model.Resource = self match { case rdf4jResource: RDF4JResource => rdf4jResource.resource case other => throw RdfProcessingException(s"$other is not a RDF4J resource") } - } } implicit class ConvertibleRDF4JIri(val self: IriNode) extends AnyVal { - def asRDF4JIri: rdf4j.model.IRI = { + def asRDF4JIri: rdf4j.model.IRI = self match { case rdf4jIriNode: RDF4JIriNode => rdf4jIriNode.resource case other => throw RdfProcessingException(s"$other is not an RDF4J IRI") } - } } implicit class ConvertibleRDF4JValue(val self: RdfNode) extends AnyVal { - def asRDF4JValue: rdf4j.model.Value = { + def asRDF4JValue: rdf4j.model.Value = self match { case rdf4jResource: RDF4JNode => rdf4jResource.rdf4jValue case other => throw RdfProcessingException(s"$other is not an RDF4J value") } - } } implicit class ConvertibleRDF4JStatement(val self: Statement) extends AnyVal { - def asRDF4JStatement: rdf4j.model.Statement = { + def asRDF4JStatement: rdf4j.model.Statement = self match { case rdf4JStatement: RDF4JStatement => rdf4JStatement.statement case other => throw RdfProcessingException(s"$other is not an RDF4J statement") } - } } implicit class ConvertibleRDF4JModel(val self: RdfModel) extends AnyVal { - def asRDF4JModel: rdf4j.model.Model = { + def asRDF4JModel: rdf4j.model.Model = self match { case rdf4JModel: RDF4JModel => rdf4JModel.getModel case other => throw RdfProcessingException(s"${other.getClass.getName} is not an RDF4J model") } - } } } /** - * An implementation of [[RdfModel]] that wraps an [[rdf4j.model.Model]]. - * - * @param model the underlying RDF4J model. - */ + * An implementation of [[RdfModel]] that wraps an [[rdf4j.model.Model]]. + * + * @param model the underlying RDF4J model. + */ class RDF4JModel(private val model: rdf4j.model.Model, private val nodeFactory: RDF4JNodeFactory) extends RdfModel with Feature { @@ -181,17 +173,16 @@ class RDF4JModel(private val model: rdf4j.model.Model, private val nodeFactory: } /** - * Returns the underlying [[rdf4j.model.Model]]. - */ + * Returns the underlying [[rdf4j.model.Model]]. + */ def getModel: rdf4j.model.Model = model override def getNodeFactory: RdfNodeFactory = nodeFactory - override def addStatement(statement: Statement): Unit = { + override def addStatement(statement: Statement): Unit = model.add(statement.asRDF4JStatement) - } - override def add(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI] = None): Unit = { + override def add(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI] = None): Unit = context match { case Some(definedContext) => model.add( @@ -208,12 +199,13 @@ class RDF4JModel(private val model: rdf4j.model.Model, private val nodeFactory: obj.asRDF4JValue ) } - } - override def remove(subj: Option[RdfResource], - pred: Option[IriNode], - obj: Option[RdfNode], - context: Option[IRI] = None): Unit = { + override def remove( + subj: Option[RdfResource], + pred: Option[IriNode], + obj: Option[RdfNode], + context: Option[IRI] = None + ): Unit = context match { case Some(definedContext) => model.remove( @@ -227,24 +219,24 @@ class RDF4JModel(private val model: rdf4j.model.Model, private val nodeFactory: model.remove( subj.map(_.asRDF4JResource).orNull, pred.map(_.asRDF4JIri).orNull, - obj.map(_.asRDF4JValue).orNull, + obj.map(_.asRDF4JValue).orNull ) } - } - override def removeStatement(statement: Statement): Unit = { + override def removeStatement(statement: Statement): Unit = model.remove( statement.subj.asRDF4JResource, statement.pred.asRDF4JIri, statement.obj.asRDF4JValue, statement.context.map(definedContext => valueFactory.createIRI(definedContext)).orNull ) - } - override def find(subj: Option[RdfResource], - pred: Option[IriNode], - obj: Option[RdfNode], - context: Option[IRI] = None): Iterator[Statement] = { + override def find( + subj: Option[RdfResource], + pred: Option[IriNode], + obj: Option[RdfNode], + context: Option[IRI] = None + ): Iterator[Statement] = { val filteredModel: rdf4j.model.Model = context match { case Some(definedContext) => model.filter( @@ -258,91 +250,77 @@ class RDF4JModel(private val model: rdf4j.model.Model, private val nodeFactory: model.filter( subj.map(_.asRDF4JResource).orNull, pred.map(_.asRDF4JIri).orNull, - obj.map(_.asRDF4JValue).orNull, + obj.map(_.asRDF4JValue).orNull ) } new StatementIterator(filteredModel.iterator) } - override def contains(statement: Statement): Boolean = { + override def contains(statement: Statement): Boolean = model.contains( statement.subj.asRDF4JResource, statement.pred.asRDF4JIri, statement.obj.asRDF4JValue, statement.context.map(definedContext => valueFactory.createIRI(definedContext)).orNull ) - } - override def setNamespace(prefix: String, namespace: IRI): Unit = { + override def setNamespace(prefix: String, namespace: IRI): Unit = model.setNamespace(new rdf4j.model.impl.SimpleNamespace(prefix, namespace)) - } - override def getNamespaces: Map[String, IRI] = { + override def getNamespaces: Map[String, IRI] = model.getNamespaces.asScala.map { namespace: rdf4j.model.Namespace => namespace.getPrefix -> namespace.getName }.toMap - } override def isEmpty: Boolean = model.isEmpty - override def getSubjects: Set[RdfResource] = { + override def getSubjects: Set[RdfResource] = model.subjects.asScala.toSet.map(resource => RDF4JResource.fromRDF4J(resource)) - } - override def isIsomorphicWith(otherRdfModel: RdfModel): Boolean = { + override def isIsomorphicWith(otherRdfModel: RdfModel): Boolean = model == otherRdfModel.asRDF4JModel - } - override def getContexts: Set[IRI] = { + override def getContexts: Set[IRI] = model.contexts.asScala.toSet.filter(_ != null).map { context: rdf4j.model.Resource => context.stringValue } - } - override def asRepository: RdfRepository = { + override def asRepository: RdfRepository = new RDF4JRepository(model) - } override def size: Int = model.size - override def iterator: Iterator[Statement] = { + override def iterator: Iterator[Statement] = new StatementIterator(model.iterator) - } - override def clear(): Unit = { + override def clear(): Unit = model.remove(null, null, null) - } } /** - * An implementation of [[RdfNodeFactory]] that creates RDF4J node implementation wrappers. - */ + * An implementation of [[RdfNodeFactory]] that creates RDF4J node implementation wrappers. + */ class RDF4JNodeFactory extends RdfNodeFactory { import RDF4JConversions._ private val valueFactory: rdf4j.model.ValueFactory = rdf4j.model.impl.SimpleValueFactory.getInstance - override def makeBlankNode: BlankNode = { + override def makeBlankNode: BlankNode = RDF4JBlankNode(valueFactory.createBNode) - } - override def makeBlankNodeWithID(id: String): BlankNode = { + override def makeBlankNodeWithID(id: String): BlankNode = RDF4JBlankNode(valueFactory.createBNode(id)) - } - override def makeIriNode(iri: IRI): IriNode = { + override def makeIriNode(iri: IRI): IriNode = RDF4JIriNode(valueFactory.createIRI(iri)) - } - override def makeDatatypeLiteral(value: String, datatype: IRI): DatatypeLiteral = { + override def makeDatatypeLiteral(value: String, datatype: IRI): DatatypeLiteral = RDF4JDatatypeLiteral(valueFactory.createLiteral(value, valueFactory.createIRI(datatype))) - } - override def makeStringWithLanguage(value: String, language: String): StringWithLanguage = { + override def makeStringWithLanguage(value: String, language: String): StringWithLanguage = RDF4JStringWithLanguage(valueFactory.createLiteral(value, language)) - } override def makeStatement(subj: RdfResource, pred: IriNode, obj: RdfNode, context: Option[IRI]): Statement = { val statement: rdf4j.model.Statement = context match { @@ -367,8 +345,8 @@ class RDF4JNodeFactory extends RdfNodeFactory { } /** - * A factory for creating instances of [[RDF4JModel]]. - */ + * A factory for creating instances of [[RDF4JModel]]. + */ class RDF4JModelFactory(private val nodeFactory: RDF4JNodeFactory) extends RdfModelFactory { override def makeEmptyModel: RDF4JModel = new RDF4JModel( model = new rdf4j.model.impl.LinkedHashModel, @@ -377,10 +355,10 @@ class RDF4JModelFactory(private val nodeFactory: RDF4JNodeFactory) extends RdfMo } /** - * An [[RdfRepository]] that wraps an [[rdf4j.model.Model]] in an [[rdf4j.repository.sail.SailRepository]]. - * - * @param model the model to be queried. - */ + * An [[RdfRepository]] that wraps an [[rdf4j.model.Model]] in an [[rdf4j.repository.sail.SailRepository]]. + * + * @param model the model to be queried. + */ class RDF4JRepository(model: rdf4j.model.Model) extends RdfRepository { // Construct an in-memory SailRepository containing the model. val repository = new rdf4j.repository.sail.SailRepository(new rdf4j.sail.memory.MemoryStore()) @@ -408,9 +386,16 @@ class RDF4JRepository(model: rdf4j.model.Model) extends RdfRepository { binding.getName -> binding.getValue.stringValue }.toMap - rowBuffer.append(VariableResultsRow(new ErrorHandlingMap[String, String](rowMap, { key: String => - s"No value found for SPARQL query variable '$key' in query result row" - }))) + rowBuffer.append( + VariableResultsRow( + new ErrorHandlingMap[String, String]( + rowMap, + { key: String => + s"No value found for SPARQL query variable '$key' in query result row" + } + ) + ) + ) } tupleQueryResult.close() @@ -422,7 +407,6 @@ class RDF4JRepository(model: rdf4j.model.Model) extends RdfRepository { ) } - override def shutDown(): Unit = { + override def shutDown(): Unit = repository.shutDown() - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidator.scala index 1a9c4a0a17..e60a07260c 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidator.scala @@ -27,12 +27,12 @@ import org.knora.webapi.messages.util.rdf._ import scala.util.{Failure, Success, Try} /** - * Performs SHACL validation using RDF4J. - * - * @param baseDir the base directory that SHACL graphs are loaded from. - * @param rdfFormatUtil an [[RdfFormatUtil]]. - * @param nodeFactory an [[RDF4JNodeFactory]]. - */ + * Performs SHACL validation using RDF4J. + * + * @param baseDir the base directory that SHACL graphs are loaded from. + * @param rdfFormatUtil an [[RdfFormatUtil]]. + * @param nodeFactory an [[RDF4JNodeFactory]]. + */ class RDF4JShaclValidator(baseDir: Path, rdfFormatUtil: RDF4JFormatUtil, private val nodeFactory: RDF4JNodeFactory) extends AbstractShaclValidator[rdf4j.model.Model](baseDir, rdfFormatUtil) { @@ -98,7 +98,6 @@ class RDF4JShaclValidator(baseDir: Path, rdfFormatUtil: RDF4JFormatUtil, private validationResultTry.get } - override protected def rdfModelToShaclGraph(rdfModel: RdfModel): rdf4j.model.Model = { + override protected def rdfModelToShaclGraph(rdfModel: RdfModel): rdf4j.model.Model = rdfModel.asRDF4JModel - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/FullTextMainQueryGenerator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/FullTextMainQueryGenerator.scala index 62776e1277..c642825522 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/FullTextMainQueryGenerator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/FullTextMainQueryGenerator.scala @@ -27,10 +27,10 @@ import org.knora.webapi.settings.KnoraSettingsImpl object FullTextMainQueryGenerator { /** - * Constants for fulltext query. - * - * These constants are used to create SPARQL CONSTRUCT queries to be executed by the triplestore and to process the results that are returned. - */ + * Constants for fulltext query. + * + * These constants are used to create SPARQL CONSTRUCT queries to be executed by the triplestore and to process the results that are returned. + */ object FullTextSearchConstants { // SPARQL variable representing the concatenated IRIs of value objects matching the search criteria @@ -71,29 +71,36 @@ object FullTextMainQueryGenerator { } /** - * Creates a CONSTRUCT query for the given resource and value object IRIs. - * - * @param resourceIris the IRIs of the resources to be queried. - * @param valueObjectIris the IRIs of the value objects to be queried. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @return a [[ConstructQuery]]. - */ - def createMainQuery(resourceIris: Set[IRI], - valueObjectIris: Set[IRI], - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - settings: KnoraSettingsImpl): ConstructQuery = { + * Creates a CONSTRUCT query for the given resource and value object IRIs. + * + * @param resourceIris the IRIs of the resources to be queried. + * @param valueObjectIris the IRIs of the value objects to be queried. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @return a [[ConstructQuery]]. + */ + def createMainQuery( + resourceIris: Set[IRI], + valueObjectIris: Set[IRI], + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + settings: KnoraSettingsImpl + ): ConstructQuery = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance import FullTextSearchConstants._ // WHERE patterns for the resources: check that the resource are a knora-base:Resource and that it is not marked as deleted val wherePatternsForResources = Seq( - ValuesPattern(resourceVar, resourceIris.map(iri => IriRef(iri.toSmartIri))), // a ValuePattern that binds the resource IRIs to the resource variable - StatementPattern.makeInferred(subj = resourceVar, - pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), - obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri)), + ValuesPattern( + resourceVar, + resourceIris.map(iri => IriRef(iri.toSmartIri)) + ), // a ValuePattern that binds the resource IRIs to the resource variable + StatementPattern.makeInferred( + subj = resourceVar, + pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), + obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri) + ), StatementPattern.makeExplicit( subj = resourceVar, pred = IriRef(OntologyConstants.KnoraBase.IsDeleted.toSmartIri), @@ -109,9 +116,11 @@ object FullTextMainQueryGenerator { pred = IriRef(OntologyConstants.KnoraBase.IsMainResource.toSmartIri), obj = XsdLiteral(value = "true", datatype = OntologyConstants.Xsd.Boolean.toSmartIri) ), - StatementPattern(subj = resourceVar, - pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), - obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri)), + StatementPattern( + subj = resourceVar, + pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), + obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri) + ), StatementPattern(subj = resourceVar, pred = resourcePropVar, obj = resourceObjectVar) ) @@ -121,25 +130,31 @@ object FullTextMainQueryGenerator { // WHERE patterns for statements about the resources' values val wherePatternsForValueObjects = Seq( ValuesPattern(resourceValueObject, valueObjectIris.map(iri => IriRef(iri.toSmartIri))), - StatementPattern.makeInferred(subj = resourceVar, - pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), - obj = resourceValueObject), + StatementPattern.makeInferred( + subj = resourceVar, + pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), + obj = resourceValueObject + ), StatementPattern.makeExplicit(subj = resourceVar, pred = resourceValueProp, obj = resourceValueObject), StatementPattern.makeExplicit( subj = resourceValueObject, pred = IriRef(OntologyConstants.KnoraBase.IsDeleted.toSmartIri), obj = XsdLiteral(value = "false", datatype = OntologyConstants.Xsd.Boolean.toSmartIri) ), - StatementPattern.makeExplicit(subj = resourceValueObject, - pred = resourceValueObjectProp, - obj = resourceValueObjectObj) + StatementPattern.makeExplicit( + subj = resourceValueObject, + pred = resourceValueObjectProp, + obj = resourceValueObjectObj + ) ) // return assertions about value objects val constructPatternsForValueObjects = Seq( - StatementPattern(subj = resourceVar, - pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), - obj = resourceValueObject), + StatementPattern( + subj = resourceVar, + pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), + obj = resourceValueObject + ), StatementPattern(subj = resourceVar, pred = resourceValueProp, obj = resourceValueObject), StatementPattern(subj = resourceValueObject, pred = resourceValueObjectProp, obj = resourceValueObjectObj) ) @@ -151,13 +166,17 @@ object FullTextMainQueryGenerator { val wherePatternsForStandoff: Seq[QueryPattern] = if (queryStandoff) { Seq( ValuesPattern(resourceValueObject, valueObjectIris.map(iri => IriRef(iri.toSmartIri))), - StatementPattern.makeExplicit(subj = resourceValueObject, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), - obj = standoffNodeVar), + StatementPattern.makeExplicit( + subj = resourceValueObject, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), + obj = standoffNodeVar + ), StatementPattern.makeExplicit(subj = standoffNodeVar, pred = standoffPropVar, obj = standoffValueVar), - StatementPattern.makeExplicit(subj = standoffNodeVar, - pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasStartIndex.toSmartIri), - obj = standoffStartIndexVar), + StatementPattern.makeExplicit( + subj = standoffNodeVar, + pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasStartIndex.toSmartIri), + obj = standoffStartIndexVar + ), FilterPattern( AndExpression( leftArg = CompareExpression( @@ -168,8 +187,10 @@ object FullTextMainQueryGenerator { rightArg = CompareExpression( leftArg = standoffStartIndexVar, operator = CompareExpressionOperator.LESS_THAN_OR_EQUAL_TO, - rightArg = XsdLiteral(value = (settings.standoffPerPage - 1).toString, - datatype = OntologyConstants.Xsd.Integer.toSmartIri) + rightArg = XsdLiteral( + value = (settings.standoffPerPage - 1).toString, + datatype = OntologyConstants.Xsd.Integer.toSmartIri + ) ) ) ) @@ -181,9 +202,11 @@ object FullTextMainQueryGenerator { // return standoff val constructPatternsForStandoff: Seq[StatementPattern] = if (queryStandoff) { Seq( - StatementPattern(subj = resourceValueObject, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), - obj = standoffNodeVar), + StatementPattern( + subj = resourceValueObject, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), + obj = standoffNodeVar + ), StatementPattern(subj = standoffNodeVar, pred = standoffPropVar, obj = standoffValueVar) ) } else { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/MainQueryResultProcessor.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/MainQueryResultProcessor.scala index 75db3206dd..a21f76c1bb 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/MainQueryResultProcessor.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/MainQueryResultProcessor.scala @@ -37,26 +37,27 @@ import org.knora.webapi.messages.{SmartIri, StringFormatter} object MainQueryResultProcessor { /** - * Given the results of the main query, filters out all values that the user did not ask for in the input query, - * i.e that are not present in its CONSTRUCT clause. - * - * @param queryResultsWithFullGraphPattern results with full graph pattern (that user has sufficient permissions on). - * @param valueObjectVarsAndIrisPerMainResource value object variables and their Iris per main resource. - * @param allResourceVariablesFromTypeInspection all variables representing resources. - * @param dependentResourceIrisFromTypeInspection Iris of dependent resources used in the input query. - * @param transformer the transformer that was used to turn the input query into the prequery. - * @param typeInspectionResult results of type inspection of the input query. - * @return results with only the values the user asked for in the input query's CONSTRUCT clause. - */ + * Given the results of the main query, filters out all values that the user did not ask for in the input query, + * i.e that are not present in its CONSTRUCT clause. + * + * @param queryResultsWithFullGraphPattern results with full graph pattern (that user has sufficient permissions on). + * @param valueObjectVarsAndIrisPerMainResource value object variables and their Iris per main resource. + * @param allResourceVariablesFromTypeInspection all variables representing resources. + * @param dependentResourceIrisFromTypeInspection Iris of dependent resources used in the input query. + * @param transformer the transformer that was used to turn the input query into the prequery. + * @param typeInspectionResult results of type inspection of the input query. + * @return results with only the values the user asked for in the input query's CONSTRUCT clause. + */ // TODO apparently not needed, work is taken care in ConstructResponseUtilV2.nestResources def getRequestedValuesFromResultsWithFullGraphPattern( - queryResultsWithFullGraphPattern: RdfResources, - valueObjectVarsAndIrisPerMainResource: ValueObjectVariablesAndValueObjectIris, - allResourceVariablesFromTypeInspection: Set[QueryVariable], - dependentResourceIrisFromTypeInspection: Set[IRI], - transformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer, - typeInspectionResult: GravsearchTypeInspectionResult, - inputQuery: ConstructQuery): RdfResources = { + queryResultsWithFullGraphPattern: RdfResources, + valueObjectVarsAndIrisPerMainResource: ValueObjectVariablesAndValueObjectIris, + allResourceVariablesFromTypeInspection: Set[QueryVariable], + dependentResourceIrisFromTypeInspection: Set[IRI], + transformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer, + typeInspectionResult: GravsearchTypeInspectionResult, + inputQuery: ConstructQuery + ): RdfResources = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // sort out those value objects that the user did not ask for in the input query's CONSTRUCT clause @@ -85,8 +86,8 @@ object MainQueryResultProcessor { } // combine all value object variables into one set - val allRequestedValueObjectVariables - : Set[QueryVariable] = requestedValueObjectVariablesForAllResVars ++ requestedValueObjectVariablesForDependentResIris + val allRequestedValueObjectVariables: Set[QueryVariable] = + requestedValueObjectVariablesForAllResVars ++ requestedValueObjectVariablesForDependentResIris // collect requested value object Iris for each main resource val requestedValObjIrisPerMainResource: Map[IRI, Set[IRI]] = queryResultsWithFullGraphPattern.keySet.map { @@ -101,79 +102,82 @@ object MainQueryResultProcessor { valueObjIrisForRes.getOrElse( requestedQueryVar, throw AssertionException( - s"key $requestedQueryVar is absent in prequery's value object IRIs collection for resource $mainResIri")) + s"key $requestedQueryVar is absent in prequery's value object IRIs collection for resource $mainResIri" + ) + ) } mainResIri -> valObjIrisRequestedForRes }.toMap // for each main resource, get only the requested value objects - queryResultsWithFullGraphPattern.map { - case (mainResIri: IRI, assertions: ResourceWithValueRdfData) => - // get the Iris of all the value objects requested for the current main resource - val valueObjIrisRequestedForRes: Set[IRI] = requestedValObjIrisPerMainResource.getOrElse( - mainResIri, - throw AssertionException( - s"key $mainResIri is absent in requested value object IRIs collection for resource $mainResIri")) - - /** - * Recursively filters out those values that the user does not want to see. - * Starts with the values of the main resource and also processes link values, possibly containing dependent resources with values. - * - * @param values the values to be filtered. - * @return filtered values. - */ - def traverseAndFilterValues(values: ResourceWithValueRdfData): RdfPropertyValues = { - values.valuePropertyAssertions.foldLeft(ConstructResponseUtilV2.emptyRdfPropertyValues) { - case (acc, (propIri: SmartIri, values: Seq[ValueRdfData])) => - // filter values for the current resource - val valuesFiltered: Seq[ValueRdfData] = values.filter { valueObj: ValueRdfData => - // only return those value objects whose Iris are contained in valueObjIrisRequestedForRes - valueObjIrisRequestedForRes(valueObj.subjectIri) - } - - // if there are link values including a target resource, apply filter to their values too - val valuesFilteredRecursively: Seq[ValueRdfData] = valuesFiltered.map { valObj: ValueRdfData => - if (valObj.nestedResource.nonEmpty) { - - val targetResourceAssertions: ResourceWithValueRdfData = valObj.nestedResource.get - - // apply filter to the target resource's values - val targetResourceAssertionsFiltered: RdfPropertyValues = - traverseAndFilterValues(targetResourceAssertions) - - valObj.copy( - nestedResource = Some( - targetResourceAssertions.copy( - valuePropertyAssertions = targetResourceAssertionsFiltered - )) + queryResultsWithFullGraphPattern.map { case (mainResIri: IRI, assertions: ResourceWithValueRdfData) => + // get the Iris of all the value objects requested for the current main resource + val valueObjIrisRequestedForRes: Set[IRI] = requestedValObjIrisPerMainResource.getOrElse( + mainResIri, + throw AssertionException( + s"key $mainResIri is absent in requested value object IRIs collection for resource $mainResIri" + ) + ) + + /** + * Recursively filters out those values that the user does not want to see. + * Starts with the values of the main resource and also processes link values, possibly containing dependent resources with values. + * + * @param values the values to be filtered. + * @return filtered values. + */ + def traverseAndFilterValues(values: ResourceWithValueRdfData): RdfPropertyValues = + values.valuePropertyAssertions.foldLeft(ConstructResponseUtilV2.emptyRdfPropertyValues) { + case (acc, (propIri: SmartIri, values: Seq[ValueRdfData])) => + // filter values for the current resource + val valuesFiltered: Seq[ValueRdfData] = values.filter { valueObj: ValueRdfData => + // only return those value objects whose Iris are contained in valueObjIrisRequestedForRes + valueObjIrisRequestedForRes(valueObj.subjectIri) + } + + // if there are link values including a target resource, apply filter to their values too + val valuesFilteredRecursively: Seq[ValueRdfData] = valuesFiltered.map { valObj: ValueRdfData => + if (valObj.nestedResource.nonEmpty) { + + val targetResourceAssertions: ResourceWithValueRdfData = valObj.nestedResource.get + + // apply filter to the target resource's values + val targetResourceAssertionsFiltered: RdfPropertyValues = + traverseAndFilterValues(targetResourceAssertions) + + valObj.copy( + nestedResource = Some( + targetResourceAssertions.copy( + valuePropertyAssertions = targetResourceAssertionsFiltered + ) ) - } else { - valObj - } - } - - // ignore properties if there are no value objects to be displayed. - // if the user does not want to see a value, the property pointing to that value has to be ignored. - if (valuesFilteredRecursively.nonEmpty) { - acc + (propIri -> valuesFilteredRecursively) + ) } else { - // ignore this property since there are no value objects - // Example: the input query's WHERE clause contains the statement "?page incunabula:seqnum ?seqnum .", - // but the statement is not present in its CONSTRUCT clause. Therefore, the property incunabula:seqnum can be ignored - // since no value objects are returned for it. - acc + valObj } - } + } + + // ignore properties if there are no value objects to be displayed. + // if the user does not want to see a value, the property pointing to that value has to be ignored. + if (valuesFilteredRecursively.nonEmpty) { + acc + (propIri -> valuesFilteredRecursively) + } else { + // ignore this property since there are no value objects + // Example: the input query's WHERE clause contains the statement "?page incunabula:seqnum ?seqnum .", + // but the statement is not present in its CONSTRUCT clause. Therefore, the property incunabula:seqnum can be ignored + // since no value objects are returned for it. + acc + } } - // filter values for the current main resource - val requestedValuePropertyAssertions: RdfPropertyValues = traverseAndFilterValues(assertions) + // filter values for the current main resource + val requestedValuePropertyAssertions: RdfPropertyValues = traverseAndFilterValues(assertions) - // only return the requested values for the current main resource - mainResIri -> assertions.copy( - valuePropertyAssertions = requestedValuePropertyAssertions - ) + // only return the requested values for the current main resource + mainResIri -> assertions.copy( + valuePropertyAssertions = requestedValuePropertyAssertions + ) } } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/QueryTraverser.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/QueryTraverser.scala index 2b5f2e233a..109a3eed6b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/QueryTraverser.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/QueryTraverser.scala @@ -20,191 +20,197 @@ package org.knora.webapi.messages.util.search /** - * A trait for classes that visit statements and filters in WHERE clauses, accumulating some result. - * - * @tparam Acc the type of the accumulator. - */ + * A trait for classes that visit statements and filters in WHERE clauses, accumulating some result. + * + * @tparam Acc the type of the accumulator. + */ trait WhereVisitor[Acc] { /** - * Visits a statement in a WHERE clause. - * - * @param statementPattern the pattern to be visited. - * @param acc the accumulator. - * @return the accumulator. - */ + * Visits a statement in a WHERE clause. + * + * @param statementPattern the pattern to be visited. + * @param acc the accumulator. + * @return the accumulator. + */ def visitStatementInWhere(statementPattern: StatementPattern, acc: Acc): Acc /** - * Visits a FILTER in a WHERE clause. - * - * @param filterPattern the pattern to be visited. - * @param acc the accumulator. - * @return the accumulator. - */ + * Visits a FILTER in a WHERE clause. + * + * @param filterPattern the pattern to be visited. + * @param acc the accumulator. + * @return the accumulator. + */ def visitFilter(filterPattern: FilterPattern, acc: Acc): Acc } /** - * A trait for classes that transform statements and filters in WHERE clauses. - */ + * A trait for classes that transform statements and filters in WHERE clauses. + */ trait WhereTransformer { /** - * Optimises query patterns. Does not recurse. Must be called before `transformStatementInWhere`, - * because optimisation might remove statements that would otherwise be expanded by `transformStatementInWhere`. - * - * @param patterns the query patterns to be optimised. - * @return the optimised query patterns. - */ + * Optimises query patterns. Does not recurse. Must be called before `transformStatementInWhere`, + * because optimisation might remove statements that would otherwise be expanded by `transformStatementInWhere`. + * + * @param patterns the query patterns to be optimised. + * @return the optimised query patterns. + */ def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] /** - * Called before entering a UNION block. - */ + * Called before entering a UNION block. + */ def enteringUnionBlock(): Unit /** - * Called before leaving a UNION block. - */ + * Called before leaving a UNION block. + */ def leavingUnionBlock(): Unit /** - * Transforms a [[StatementPattern]] in a WHERE clause into zero or more query patterns. - * - * @param statementPattern the statement to be transformed. - * @param inputOrderBy the ORDER BY clause in the input query. - * @return the result of the transformation. - */ - def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[QueryPattern] + * Transforms a [[StatementPattern]] in a WHERE clause into zero or more query patterns. + * + * @param statementPattern the statement to be transformed. + * @param inputOrderBy the ORDER BY clause in the input query. + * @return the result of the transformation. + */ + def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[QueryPattern] /** - * Transforms a [[FilterPattern]] in a WHERE clause into zero or more query patterns. - * - * @param filterPattern the filter to be transformed. - * @return the result of the transformation. - */ + * Transforms a [[FilterPattern]] in a WHERE clause into zero or more query patterns. + * + * @param filterPattern the filter to be transformed. + * @return the result of the transformation. + */ def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] /** - * Transforms a [[LuceneQueryPattern]] into one or more query patterns. - * - * @param luceneQueryPattern the query pattern to be transformed. - * @return the transformed pattern. - */ + * Transforms a [[LuceneQueryPattern]] into one or more query patterns. + * + * @param luceneQueryPattern the query pattern to be transformed. + * @return the transformed pattern. + */ def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] } /** - * A trait for classes that transform SELECT queries into other SELECT queries. - */ + * A trait for classes that transform SELECT queries into other SELECT queries. + */ trait SelectToSelectTransformer extends WhereTransformer { /** - * Transforms a [[StatementPattern]] in a SELECT's WHERE clause into zero or more statement patterns. - * - * @param statementPattern the statement to be transformed. - * @return the result of the transformation. - */ + * Transforms a [[StatementPattern]] in a SELECT's WHERE clause into zero or more statement patterns. + * + * @param statementPattern the statement to be transformed. + * @return the result of the transformation. + */ def transformStatementInSelect(statementPattern: StatementPattern): Seq[StatementPattern] /** - * Specifies a FROM clause, if needed. - * - * @return the FROM clause to be used, if any. - */ + * Specifies a FROM clause, if needed. + * + * @return the FROM clause to be used, if any. + */ def getFromClause: Option[FromClause] } /** - * A trait for classes that transform CONSTRUCT queries into other CONSTRUCT queries. - */ + * A trait for classes that transform CONSTRUCT queries into other CONSTRUCT queries. + */ trait ConstructToConstructTransformer extends WhereTransformer { /** - * Transforms a [[StatementPattern]] in a CONSTRUCT clause into zero or more statement patterns. - * - * @param statementPattern the statement to be transformed. - * @return the result of the transformation. - */ + * Transforms a [[StatementPattern]] in a CONSTRUCT clause into zero or more statement patterns. + * + * @param statementPattern the statement to be transformed. + * @return the result of the transformation. + */ def transformStatementInConstruct(statementPattern: StatementPattern): Seq[StatementPattern] } /** - * Returned by `ConstructToSelectTransformer.getOrderBy` to represent a transformed ORDER BY as well - * as any additional statement patterns that should be added to the WHERE clause to support the ORDER BY. - * - * @param statementPatterns any additional WHERE clause statements required by the ORDER BY. - * @param orderBy the ORDER BY criteria. - */ -case class TransformedOrderBy(statementPatterns: Seq[StatementPattern] = Vector.empty[StatementPattern], - orderBy: Seq[OrderCriterion] = Vector.empty[OrderCriterion]) + * Returned by `ConstructToSelectTransformer.getOrderBy` to represent a transformed ORDER BY as well + * as any additional statement patterns that should be added to the WHERE clause to support the ORDER BY. + * + * @param statementPatterns any additional WHERE clause statements required by the ORDER BY. + * @param orderBy the ORDER BY criteria. + */ +case class TransformedOrderBy( + statementPatterns: Seq[StatementPattern] = Vector.empty[StatementPattern], + orderBy: Seq[OrderCriterion] = Vector.empty[OrderCriterion] +) /** - * A trait for classes that transform SELECT queries into CONSTRUCT queries. - */ + * A trait for classes that transform SELECT queries into CONSTRUCT queries. + */ trait ConstructToSelectTransformer extends WhereTransformer { /** - * Returns the columns to be specified in the SELECT query. - */ + * Returns the columns to be specified in the SELECT query. + */ def getSelectColumns: Seq[SelectQueryColumn] /** - * Returns the variables that the query result rows are grouped by (aggregating rows into one). - * Variables returned by the SELECT query must either be present in the GROUP BY statement - * or be transformed by an aggregation function in SPARQL. - * This method will be called by [[QueryTraverser]] after the whole input query has been traversed. - * - * @param orderByCriteria the criteria used to sort the query results. They have to be included in the GROUP BY statement, otherwise they are unbound. - * @return a list of variables that the result rows are grouped by. - */ + * Returns the variables that the query result rows are grouped by (aggregating rows into one). + * Variables returned by the SELECT query must either be present in the GROUP BY statement + * or be transformed by an aggregation function in SPARQL. + * This method will be called by [[QueryTraverser]] after the whole input query has been traversed. + * + * @param orderByCriteria the criteria used to sort the query results. They have to be included in the GROUP BY statement, otherwise they are unbound. + * @return a list of variables that the result rows are grouped by. + */ def getGroupBy(orderByCriteria: TransformedOrderBy): Seq[QueryVariable] /** - * Returns the criteria, if any, that should be used in the ORDER BY clause of the SELECT query. This method will be called - * by [[QueryTraverser]] after the whole input query has been traversed. - * - * @param inputOrderBy the ORDER BY criteria in the input query. - * @return the ORDER BY criteria, if any. - */ + * Returns the criteria, if any, that should be used in the ORDER BY clause of the SELECT query. This method will be called + * by [[QueryTraverser]] after the whole input query has been traversed. + * + * @param inputOrderBy the ORDER BY criteria in the input query. + * @return the ORDER BY criteria, if any. + */ def getOrderBy(inputOrderBy: Seq[OrderCriterion]): TransformedOrderBy /** - * Returns the limit representing the maximum amount of result rows returned by the SELECT query. - * - * @return the LIMIT, if any. - */ + * Returns the limit representing the maximum amount of result rows returned by the SELECT query. + * + * @return the LIMIT, if any. + */ def getLimit: Int /** - * Returns the OFFSET to be used in the SELECT query. - * Provided the OFFSET submitted in the input query, calculates the actual offset in result rows depending on LIMIT. - * - * @param inputQueryOffset the OFFSET provided in the input query. - * @return the OFFSET. - */ + * Returns the OFFSET to be used in the SELECT query. + * Provided the OFFSET submitted in the input query, calculates the actual offset in result rows depending on LIMIT. + * + * @param inputQueryOffset the OFFSET provided in the input query. + * @return the OFFSET. + */ def getOffset(inputQueryOffset: Long, limit: Int): Long } /** - * Assists in the transformation of CONSTRUCT queries by traversing the query, delegating work to a [[ConstructToConstructTransformer]] - * or [[ConstructToSelectTransformer]]. - */ + * Assists in the transformation of CONSTRUCT queries by traversing the query, delegating work to a [[ConstructToConstructTransformer]] + * or [[ConstructToSelectTransformer]]. + */ object QueryTraverser { /** - * Traverses a WHERE clause, delegating transformation tasks to a [[WhereTransformer]], and returns the transformed query patterns. - * - * @param patterns the input query patterns. - * @param inputOrderBy the ORDER BY expression in the input query. - * @param whereTransformer a [[WhereTransformer]]. - * @return the transformed query patterns. - */ - def transformWherePatterns(patterns: Seq[QueryPattern], - inputOrderBy: Seq[OrderCriterion], - whereTransformer: WhereTransformer): Seq[QueryPattern] = { + * Traverses a WHERE clause, delegating transformation tasks to a [[WhereTransformer]], and returns the transformed query patterns. + * + * @param patterns the input query patterns. + * @param inputOrderBy the ORDER BY expression in the input query. + * @param whereTransformer a [[WhereTransformer]]. + * @return the transformed query patterns. + */ + def transformWherePatterns( + patterns: Seq[QueryPattern], + inputOrderBy: Seq[OrderCriterion], + whereTransformer: WhereTransformer + ): Seq[QueryPattern] = { // Optimization has to be called before WhereTransformer.transformStatementInWhere, because optimisation might // remove statements that would otherwise be expanded by transformStatementInWhere @@ -252,9 +258,11 @@ object QueryTraverser { case unionPattern: UnionPattern => val transformedBlocks: Seq[Seq[QueryPattern]] = unionPattern.blocks.map { blockPatterns: Seq[QueryPattern] => whereTransformer.enteringUnionBlock() - val transformedPatterns: Seq[QueryPattern] = transformWherePatterns(patterns = blockPatterns, - whereTransformer = whereTransformer, - inputOrderBy = inputOrderBy) + val transformedPatterns: Seq[QueryPattern] = transformWherePatterns( + patterns = blockPatterns, + whereTransformer = whereTransformer, + inputOrderBy = inputOrderBy + ) whereTransformer.leavingUnionBlock() transformedPatterns } @@ -272,15 +280,15 @@ object QueryTraverser { } /** - * Traverses a WHERE clause, delegating transformation tasks to a [[WhereVisitor]]. - * - * @param patterns the input query patterns. - * @param whereVisitor a [[WhereVisitor]]. - * @param initialAcc the visitor's initial accumulator. - * @tparam Acc the type of the accumulator. - * @return the accumulator. - */ - def visitWherePatterns[Acc](patterns: Seq[QueryPattern], whereVisitor: WhereVisitor[Acc], initialAcc: Acc): Acc = { + * Traverses a WHERE clause, delegating transformation tasks to a [[WhereVisitor]]. + * + * @param patterns the input query patterns. + * @param whereVisitor a [[WhereVisitor]]. + * @param initialAcc the visitor's initial accumulator. + * @tparam Acc the type of the accumulator. + * @return the accumulator. + */ + def visitWherePatterns[Acc](patterns: Seq[QueryPattern], whereVisitor: WhereVisitor[Acc], initialAcc: Acc): Acc = patterns.foldLeft(initialAcc) { case (acc, statementPattern: StatementPattern) => whereVisitor.visitStatementInWhere(statementPattern, acc) @@ -310,26 +318,24 @@ object QueryTraverser { ) case (acc, unionPattern: UnionPattern) => - unionPattern.blocks.foldLeft(acc) { - case (unionAcc, blockPatterns: Seq[QueryPattern]) => - visitWherePatterns( - patterns = blockPatterns, - whereVisitor = whereVisitor, - initialAcc = unionAcc - ) + unionPattern.blocks.foldLeft(acc) { case (unionAcc, blockPatterns: Seq[QueryPattern]) => + visitWherePatterns( + patterns = blockPatterns, + whereVisitor = whereVisitor, + initialAcc = unionAcc + ) } case (acc, _) => acc } - } /** - * Traverses a SELECT query, delegating transformation tasks to a [[ConstructToSelectTransformer]], and returns the transformed query. - * - * @param inputQuery the query to be transformed. - * @param transformer the [[ConstructToSelectTransformer]] to be used. - * @return the transformed query. - */ + * Traverses a SELECT query, delegating transformation tasks to a [[ConstructToSelectTransformer]], and returns the transformed query. + * + * @param inputQuery the query to be transformed. + * @param transformer the [[ConstructToSelectTransformer]] to be used. + * @return the transformed query. + */ def transformConstructToSelect(inputQuery: ConstructQuery, transformer: ConstructToSelectTransformer): SelectQuery = { val transformedWherePatterns = transformWherePatterns( @@ -356,7 +362,7 @@ object QueryTraverser { ) } - def transformSelectToSelect(inputQuery: SelectQuery, transformer: SelectToSelectTransformer): SelectQuery = { + def transformSelectToSelect(inputQuery: SelectQuery, transformer: SelectToSelectTransformer): SelectQuery = inputQuery.copy( fromClause = transformer.getFromClause, whereClause = WhereClause( @@ -367,17 +373,18 @@ object QueryTraverser { ) ) ) - } /** - * Traverses a CONSTRUCT query, delegating transformation tasks to a [[ConstructToConstructTransformer]], and returns the transformed query. - * - * @param inputQuery the query to be transformed. - * @param transformer the [[ConstructToConstructTransformer]] to be used. - * @return the transformed query. - */ - def transformConstructToConstruct(inputQuery: ConstructQuery, - transformer: ConstructToConstructTransformer): ConstructQuery = { + * Traverses a CONSTRUCT query, delegating transformation tasks to a [[ConstructToConstructTransformer]], and returns the transformed query. + * + * @param inputQuery the query to be transformed. + * @param transformer the [[ConstructToConstructTransformer]] to be used. + * @return the transformed query. + */ + def transformConstructToConstruct( + inputQuery: ConstructQuery, + transformer: ConstructToConstructTransformer + ): ConstructQuery = { val transformedWherePatterns = transformWherePatterns( patterns = inputQuery.whereClause.patterns, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlQuery.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlQuery.scala index d5543539b8..db3f99d440 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlQuery.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlQuery.scala @@ -27,13 +27,13 @@ import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} import org.knora.webapi.util.ApacheLuceneSupport.LuceneQueryString /** - * Constants used in processing SPARQL queries. - */ + * Constants used in processing SPARQL queries. + */ object SparqlQueryConstants { /** - * The media type of SPARQL queries. - */ + * The media type of SPARQL queries. + */ val `application/sparql-query`: MediaType.WithFixedCharset = MediaType.customWithFixedCharset( mainType = "application", subType = "sparql-query", @@ -43,27 +43,27 @@ object SparqlQueryConstants { } /** - * Represents something that can generate SPARQL source code. - */ + * Represents something that can generate SPARQL source code. + */ sealed trait SparqlGenerator { def toSparql: String } /** - * Represents something that can be the subject, predicate, or object of a triple pattern in a query. - */ + * Represents something that can be the subject, predicate, or object of a triple pattern in a query. + */ sealed trait Entity extends Expression /** - * Represents something that can be returned by a SELECT query. - */ + * Represents something that can be returned by a SELECT query. + */ trait SelectQueryColumn extends Entity /** - * Represents a variable in a query. - * - * @param variableName the name of the variable. - */ + * Represents a variable in a query. + * + * @param variableName the name of the variable. + */ case class QueryVariable(variableName: String) extends SelectQueryColumn { override def toSparql: String = s"?$variableName" @@ -71,31 +71,30 @@ case class QueryVariable(variableName: String) extends SelectQueryColumn { } /** - * Represents a GROUP_CONCAT statement that combines several values into one, separated by a character. - * - * @param inputVariable the variable to be concatenated. - * @param separator the separator to be used when concatenating the single results. - * @param outputVariableName the name of the variable representing the concatenated result. - */ + * Represents a GROUP_CONCAT statement that combines several values into one, separated by a character. + * + * @param inputVariable the variable to be concatenated. + * @param separator the separator to be used when concatenating the single results. + * @param outputVariableName the name of the variable representing the concatenated result. + */ case class GroupConcat(inputVariable: QueryVariable, separator: Char, outputVariableName: String) extends SelectQueryColumn { val outputVariable: QueryVariable = QueryVariable(outputVariableName) - override def toSparql: String = { + override def toSparql: String = s"""(GROUP_CONCAT(DISTINCT(IF(BOUND(${inputVariable.toSparql}), STR(${inputVariable.toSparql}), "")); SEPARATOR='$separator') AS ${outputVariable.toSparql})""" - } override def getVariables: Set[QueryVariable] = Set(inputVariable) } /** - * Represents a COUNT statement that counts how many instances/rows are returned for [[inputVariable]]. - * - * @param inputVariable the variable to count. - * @param distinct indicates whether DISTINCT has to be used inside COUNT. - * @param outputVariableName the name of the variable representing the result. - */ + * Represents a COUNT statement that counts how many instances/rows are returned for [[inputVariable]]. + * + * @param inputVariable the variable to count. + * @param distinct indicates whether DISTINCT has to be used inside COUNT. + * @param outputVariableName the name of the variable representing the result. + */ case class Count(inputVariable: QueryVariable, distinct: Boolean, outputVariableName: String) extends SelectQueryColumn { @@ -107,49 +106,45 @@ case class Count(inputVariable: QueryVariable, distinct: Boolean, outputVariable "" } - override def toSparql: String = { - + override def toSparql: String = s"(COUNT($distinctAsStr ${inputVariable.toSparql}) AS ${outputVariable.toSparql})" - } override def getVariables: Set[QueryVariable] = Set(inputVariable, outputVariable) } /** - * Represents an IRI in a query. - * - * @param iri the IRI. - */ + * Represents an IRI in a query. + * + * @param iri the IRI. + */ case class IriRef(iri: SmartIri, propertyPathOperator: Option[Char] = None) extends Entity { /** - * If this is a knora-api entity IRI, converts it to an internal entity IRI. - * - * @return the equivalent internal entity IRI. - */ + * If this is a knora-api entity IRI, converts it to an internal entity IRI. + * + * @return the equivalent internal entity IRI. + */ def toInternalEntityIri: IriRef = IriRef(iri.toOntologySchema(InternalSchema)) - override def toSparql: String = { + override def toSparql: String = if (propertyPathOperator.nonEmpty) { s"${iri.toSparql}${propertyPathOperator.get}" } else { iri.toSparql } - } - def toOntologySchema(targetSchema: OntologySchema): IriRef = { + def toOntologySchema(targetSchema: OntologySchema): IriRef = copy(iri = iri.toOntologySchema(targetSchema)) - } override def getVariables: Set[QueryVariable] = Set.empty } /** - * Represents a literal value with an XSD type. - * - * @param value the literal value. - * @param datatype the value's XSD type IRI. - */ + * Represents a literal value with an XSD type. + * + * @param value the literal value. + * @param datatype the value's XSD type IRI. + */ case class XsdLiteral(value: String, datatype: SmartIri) extends Entity { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -168,18 +163,18 @@ case class XsdLiteral(value: String, datatype: SmartIri) extends Entity { } /** - * Represents a statement pattern or block pattern in a query. - */ + * Represents a statement pattern or block pattern in a query. + */ sealed trait QueryPattern extends SparqlGenerator /** - * Represents a statement pattern in a query. - * - * @param subj the subject of the statement. - * @param pred the predicate of the statement. - * @param obj the object of the statement. - * @param namedGraph the named graph this statement should be searched in. Defaults to [[None]]. - */ + * Represents a statement pattern in a query. + * + * @param subj the subject of the statement. + * @param pred the predicate of the statement. + * @param obj the object of the statement. + * @param namedGraph the named graph this statement should be searched in. Defaults to [[None]]. + */ case class StatementPattern(subj: Entity, pred: Entity, obj: Entity, namedGraph: Option[IriRef] = None) extends QueryPattern { override def toSparql: String = { @@ -188,75 +183,73 @@ case class StatementPattern(subj: Entity, pred: Entity, obj: Entity, namedGraph: namedGraph match { case Some(graph) => s"""GRAPH ${graph.toSparql} { - | $triple - |} - |""".stripMargin + | $triple + |} + |""".stripMargin case None => triple + "\n" } } - def toOntologySchema(targetSchema: OntologySchema): StatementPattern = { + def toOntologySchema(targetSchema: OntologySchema): StatementPattern = copy( subj = entityToOntologySchema(subj, targetSchema), pred = entityToOntologySchema(pred, targetSchema), obj = entityToOntologySchema(obj, targetSchema) ) - } - private def entityToOntologySchema(entity: Entity, targetSchema: OntologySchema): Entity = { + private def entityToOntologySchema(entity: Entity, targetSchema: OntologySchema): Entity = entity match { case iriRef: IriRef => iriRef.toOntologySchema(targetSchema) case other => other } - } } /** - * A virtual query pattern representing a Lucene full-text index search. Will be replaced by triplestore-specific - * statements during Gravsearch processing. - * - * @param subj a variable representing the subject to be found. - * @param obj a variable representing the literal that is indexed. - * @param queryString the Lucene query string to be matched. - * @param literalStatement a statement that connects `subj` to `obj`. Needed with some triplestores but not others. - * Will be defined only if it has not already been added to the generated SPARQL. - */ -case class LuceneQueryPattern(subj: QueryVariable, - obj: QueryVariable, - queryString: LuceneQueryString, - literalStatement: Option[StatementPattern]) - extends QueryPattern { + * A virtual query pattern representing a Lucene full-text index search. Will be replaced by triplestore-specific + * statements during Gravsearch processing. + * + * @param subj a variable representing the subject to be found. + * @param obj a variable representing the literal that is indexed. + * @param queryString the Lucene query string to be matched. + * @param literalStatement a statement that connects `subj` to `obj`. Needed with some triplestores but not others. + * Will be defined only if it has not already been added to the generated SPARQL. + */ +case class LuceneQueryPattern( + subj: QueryVariable, + obj: QueryVariable, + queryString: LuceneQueryString, + literalStatement: Option[StatementPattern] +) extends QueryPattern { override def toSparql: String = throw AssertionException("LuceneQueryPattern should have been transformed into statements") } /** - * Represents a BIND command in a query. - * - * @param variable the variable in the BIND. - * @param expression the expression to be bound to the variable. - */ + * Represents a BIND command in a query. + * + * @param variable the variable in the BIND. + * @param expression the expression to be bound to the variable. + */ case class BindPattern(variable: QueryVariable, expression: Expression) extends QueryPattern { - override def toSparql: String = { + override def toSparql: String = s"BIND(${expression.toSparql} AS ${variable.toSparql})\n" - } } /** - * Provides convenience methods for making statement patterns that are marked as needing inference or not. - */ + * Provides convenience methods for making statement patterns that are marked as needing inference or not. + */ object StatementPattern { /** - * Makes a [[StatementPattern]] whose named graph is [[OntologyConstants.NamedGraphs.KnoraExplicitNamedGraph]]. - * - * @param subj the subject of the statement. - * @param pred the predicate of the statement. - * @param obj the object of the statement. - * @return the statement pattern. - */ + * Makes a [[StatementPattern]] whose named graph is [[OntologyConstants.NamedGraphs.KnoraExplicitNamedGraph]]. + * + * @param subj the subject of the statement. + * @param pred the predicate of the statement. + * @param obj the object of the statement. + * @return the statement pattern. + */ def makeExplicit(subj: Entity, pred: Entity, obj: Entity): StatementPattern = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -269,26 +262,25 @@ object StatementPattern { } /** - * Makes a [[StatementPattern]] that doesn't specify a named graph. - * - * @param subj the subject of the statement. - * @param pred the predicate of the statement. - * @param obj the object of the statement. - * @return the statement pattern. - */ - def makeInferred(subj: Entity, pred: Entity, obj: Entity): StatementPattern = { + * Makes a [[StatementPattern]] that doesn't specify a named graph. + * + * @param subj the subject of the statement. + * @param pred the predicate of the statement. + * @param obj the object of the statement. + * @return the statement pattern. + */ + def makeInferred(subj: Entity, pred: Entity, obj: Entity): StatementPattern = StatementPattern( subj = subj, pred = pred, obj = obj, namedGraph = None ) - } } /** - * Represents the supported logical operators in a [[CompareExpression]]. - */ + * Represents the supported logical operators in a [[CompareExpression]]. + */ object CompareExpressionOperator extends Enumeration { val EQUALS: CompareExpressionOperator.Value = Value("=") @@ -306,38 +298,37 @@ object CompareExpressionOperator extends Enumeration { val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, the provided error function is called. - * - * @param name the name of the value. - * @param errorFun the function to be called in case of an error. - * @return the requested value. - */ - def lookup(name: String, errorFun: => Nothing): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, the provided error function is called. + * + * @param name the name of the value. + * @param errorFun the function to be called in case of an error. + * @return the requested value. + */ + def lookup(name: String, errorFun: => Nothing): Value = valueMap.get(name) match { case Some(value) => value case None => errorFun } - } } /** - * Represents an expression that can be used in a FILTER. - */ + * Represents an expression that can be used in a FILTER. + */ sealed trait Expression extends SparqlGenerator { /** - * Returns the set of query variables used in this expression. - */ + * Returns the set of query variables used in this expression. + */ def getVariables: Set[QueryVariable] } /** - * Represents a comparison expression in a FILTER. - * - * @param leftArg the left argument. - * @param operator the operator. - * @param rightArg the right argument. - */ + * Represents a comparison expression in a FILTER. + * + * @param leftArg the left argument. + * @param operator the operator. + * @param rightArg the right argument. + */ case class CompareExpression(leftArg: Expression, operator: CompareExpressionOperator.Value, rightArg: Expression) extends Expression { override def toSparql: String = s"(${leftArg.toSparql} $operator ${rightArg.toSparql})" @@ -346,11 +337,11 @@ case class CompareExpression(leftArg: Expression, operator: CompareExpressionOpe } /** - * Represents an AND expression in a filter. - * - * @param leftArg the left argument. - * @param rightArg the right argument. - */ + * Represents an AND expression in a filter. + * + * @param leftArg the left argument. + * @param rightArg the right argument. + */ case class AndExpression(leftArg: Expression, rightArg: Expression) extends Expression { override def toSparql: String = s"(${leftArg.toSparql} && ${rightArg.toSparql})" @@ -358,11 +349,11 @@ case class AndExpression(leftArg: Expression, rightArg: Expression) extends Expr } /** - * Represents an OR expression in a filter. - * - * @param leftArg the left argument. - * @param rightArg the right argument. - */ + * Represents an OR expression in a filter. + * + * @param leftArg the left argument. + * @param rightArg the right argument. + */ case class OrExpression(leftArg: Expression, rightArg: Expression) extends Expression { override def toSparql: String = s"(${leftArg.toSparql} || ${rightArg.toSparql})" @@ -370,27 +361,27 @@ case class OrExpression(leftArg: Expression, rightArg: Expression) extends Expre } /** - * A trait representing arithmetic operators. - */ + * A trait representing arithmetic operators. + */ sealed trait ArithmeticOperator /** - * Represents the plus operator. - */ + * Represents the plus operator. + */ case object PlusOperator extends ArithmeticOperator { override def toString = "+" } /** - * Represents the minus operator. - */ + * Represents the minus operator. + */ case object MinusOperator extends ArithmeticOperator { override def toString = "-" } /** - * Represents an integer literal. - */ + * Represents an integer literal. + */ case class IntegerLiteral(value: Int) extends Expression { override def toSparql: String = value.toString @@ -398,8 +389,8 @@ case class IntegerLiteral(value: Int) extends Expression { } /** - * Represents an arithmetic expression. - */ + * Represents an arithmetic expression. + */ case class ArithmeticExpression(leftArg: Expression, operator: ArithmeticOperator, rightArg: Expression) extends Expression { override def toSparql: String = s"""${leftArg.toSparql} $operator ${rightArg.toSparql}""" @@ -408,12 +399,12 @@ case class ArithmeticExpression(leftArg: Expression, operator: ArithmeticOperato } /** - * Represents a regex function in a query (in a FILTER). - * - * @param textExpr the expression representing the text value or string literal to be checked against the provided pattern. - * @param pattern the REGEX pattern to be used. - * @param modifier the modifier to be used. - */ + * Represents a regex function in a query (in a FILTER). + * + * @param textExpr the expression representing the text value or string literal to be checked against the provided pattern. + * @param pattern the REGEX pattern to be used. + * @param modifier the modifier to be used. + */ case class RegexFunction(textExpr: Expression, pattern: String, modifier: Option[String]) extends Expression { override def toSparql: String = modifier match { case Some(modifierStr) => @@ -427,10 +418,10 @@ case class RegexFunction(textExpr: Expression, pattern: String, modifier: Option } /** - * Represents a lang function in a query (in a FILTER). - * - * @param textValueVar the variable representing the text value to be restricted to the specified language. - */ + * Represents a lang function in a query (in a FILTER). + * + * @param textValueVar the variable representing the text value to be restricted to the specified language. + */ case class LangFunction(textValueVar: QueryVariable) extends Expression { override def toSparql: String = s"""lang(${textValueVar.toSparql})""" @@ -438,12 +429,12 @@ case class LangFunction(textValueVar: QueryVariable) extends Expression { } /** - * Represents the SPARQL `substr` function. - * - * @param textLiteralVar the variable containing the string literal from which a substring is to be taken. - * @param startExpression an expression representing the 1-based index of the first character of the substring. - * @param lengthExpression the length of the substring. - */ + * Represents the SPARQL `substr` function. + * + * @param textLiteralVar the variable containing the string literal from which a substring is to be taken. + * @param startExpression an expression representing the 1-based index of the first character of the substring. + * @param lengthExpression the length of the substring. + */ case class SubStrFunction(textLiteralVar: QueryVariable, startExpression: Expression, lengthExpression: Expression) extends Expression { override def toSparql: String = @@ -453,10 +444,10 @@ case class SubStrFunction(textLiteralVar: QueryVariable, startExpression: Expres } /** - * Represents the SPARQL `str` function. - * - * @param textLiteralVar the variable containing the string literal possibly with a language tag from which the string is to be taken. - */ + * Represents the SPARQL `str` function. + * + * @param textLiteralVar the variable containing the string literal possibly with a language tag from which the string is to be taken. + */ case class StrFunction(textLiteralVar: QueryVariable) extends Expression { override def toSparql: String = s"""str(${textLiteralVar.toSparql})""" @@ -464,26 +455,27 @@ case class StrFunction(textLiteralVar: QueryVariable) extends Expression { } /** - * Represents a function call in a filter. - * - * @param functionIri the IRI of the function. - * @param args the arguments passed to the function. - */ + * Represents a function call in a filter. + * + * @param functionIri the IRI of the function. + * @param args the arguments passed to the function. + */ case class FunctionCallExpression(functionIri: IriRef, args: Seq[Entity]) extends Expression { override def toSparql: String = s"${functionIri.iri.toSparql}(${args.map(_.toSparql).mkString(", ")})" /** - * Gets the argument at the given position as a [[QueryVariable]]. - * Throws a [[GravsearchException]] no argument exists at the given position or if it is not a [[QueryVariable]]. - * - * @param pos the argument to be returned from [[args]]. - * @return a [[QueryVariable]]. - */ + * Gets the argument at the given position as a [[QueryVariable]]. + * Throws a [[GravsearchException]] no argument exists at the given position or if it is not a [[QueryVariable]]. + * + * @param pos the argument to be returned from [[args]]. + * @return a [[QueryVariable]]. + */ def getArgAsQueryVar(pos: Int): QueryVariable = { if (args.size <= pos) throw GravsearchException( - s"Not enough arguments given for call of $functionIri. ${args.size} are given, argument at position $pos is requested (0-based index)") + s"Not enough arguments given for call of $functionIri. ${args.size} are given, argument at position $pos is requested (0-based index)" + ) args(pos) match { case queryVar: QueryVariable => queryVar @@ -494,18 +486,19 @@ case class FunctionCallExpression(functionIri: IriRef, args: Seq[Entity]) extend } /** - * Gets the argument at the given position as a [[XsdLiteral]] of the given datatype. - * Throws a [[GravsearchException]] no argument exists at the given position or if it is not a [[XsdLiteral]] of the requested datatype. - * - * @param pos the argument to be returned from [[args]]. - * @param xsdDatatype the argeument's datatype. - * @return an [[XsdLiteral]]. - */ + * Gets the argument at the given position as a [[XsdLiteral]] of the given datatype. + * Throws a [[GravsearchException]] no argument exists at the given position or if it is not a [[XsdLiteral]] of the requested datatype. + * + * @param pos the argument to be returned from [[args]]. + * @param xsdDatatype the argeument's datatype. + * @return an [[XsdLiteral]]. + */ def getArgAsLiteral(pos: Int, xsdDatatype: SmartIri): XsdLiteral = { if (args.size <= pos) throw GravsearchException( - s"Not enough arguments given for call of $functionIri. ${args.size} are given, argument at position $pos is requested (0-based index)") + s"Not enough arguments given for call of $functionIri. ${args.size} are given, argument at position $pos is requested (0-based index)" + ) args(pos) match { case literal: XsdLiteral if literal.datatype == xsdDatatype => literal @@ -520,29 +513,29 @@ case class FunctionCallExpression(functionIri: IriRef, args: Seq[Entity]) extend } /** - * Represents a FILTER pattern in a query. - * - * @param expression the expression in the FILTER. - */ + * Represents a FILTER pattern in a query. + * + * @param expression the expression in the FILTER. + */ case class FilterPattern(expression: Expression) extends QueryPattern { override def toSparql: String = s"FILTER(${expression.toSparql})\n" } /** - * Represents VALUES in a query. - * - * @param variable the variable that the values will be assigned to. - * @param values the IRIs that will be assigned to the variable. - */ + * Represents VALUES in a query. + * + * @param variable the variable that the values will be assigned to. + * @param values the IRIs that will be assigned to the variable. + */ case class ValuesPattern(variable: QueryVariable, values: Set[IriRef]) extends QueryPattern { override def toSparql: String = s"VALUES ${variable.toSparql} { ${values.map(_.toSparql).mkString(" ")} }\n" } /** - * Represents a UNION in the WHERE clause of a query. - * - * @param blocks the blocks of patterns contained in the UNION. - */ + * Represents a UNION in the WHERE clause of a query. + * + * @param blocks the blocks of patterns contained in the UNION. + */ case class UnionPattern(blocks: Seq[Seq[QueryPattern]]) extends QueryPattern { override def toSparql: String = { val blocksAsStrings = blocks.map { block: Seq[QueryPattern] => @@ -558,10 +551,10 @@ case class UnionPattern(blocks: Seq[Seq[QueryPattern]]) extends QueryPattern { } /** - * Represents an OPTIONAL in the WHERE clause of a query. - * - * @param patterns the patterns in the OPTIONAL block. - */ + * Represents an OPTIONAL in the WHERE clause of a query. + * + * @param patterns the patterns in the OPTIONAL block. + */ case class OptionalPattern(patterns: Seq[QueryPattern]) extends QueryPattern { override def toSparql: String = { val queryPatternStrings: Seq[String] = patterns.map { queryPattern: QueryPattern => @@ -573,55 +566,56 @@ case class OptionalPattern(patterns: Seq[QueryPattern]) extends QueryPattern { } /** - * Represents a FILTER NOT EXISTS in a query. - * - * @param patterns the patterns contained in the FILTER NOT EXISTS. - */ + * Represents a FILTER NOT EXISTS in a query. + * + * @param patterns the patterns contained in the FILTER NOT EXISTS. + */ case class FilterNotExistsPattern(patterns: Seq[QueryPattern]) extends QueryPattern { override def toSparql: String = s"FILTER NOT EXISTS {\n ${patterns.map(_.toSparql).mkString}\n}\n" } /** - * Represents a MINUS in a query. - * - * @param patterns the patterns contained in the MINUS. - */ + * Represents a MINUS in a query. + * + * @param patterns the patterns contained in the MINUS. + */ case class MinusPattern(patterns: Seq[QueryPattern]) extends QueryPattern { override def toSparql: String = s"MINUS {\n ${patterns.map(_.toSparql).mkString}\n}\n" } /** - * Represents a CONSTRUCT clause in a query. - * - * @param statements the statements in the CONSTRUCT clause. - * @param querySchema if this is a Gravsearch query, represents the Knora API v2 ontology schema used in the query. - */ + * Represents a CONSTRUCT clause in a query. + * + * @param statements the statements in the CONSTRUCT clause. + * @param querySchema if this is a Gravsearch query, represents the Knora API v2 ontology schema used in the query. + */ case class ConstructClause(statements: Seq[StatementPattern], querySchema: Option[ApiV2Schema] = None) extends SparqlGenerator { override def toSparql: String = "CONSTRUCT {\n" + statements.map(_.toSparql).mkString + "} " } /** - * Represents a WHERE clause in a query. - * - * @param patterns the patterns in the WHERE clause. - * @param positiveEntities if this is a Gravsearch query, contains the entities that are used in positive contexts - * in the WHERE clause, i.e. not in MINUS or FILTER NOT EXISTS. - * @param querySchema if this is a Gravsearch query, represents the Knora API v2 ontology schema used in the query. - */ -case class WhereClause(patterns: Seq[QueryPattern], - positiveEntities: Set[Entity] = Set.empty[Entity], - querySchema: Option[ApiV2Schema] = None) - extends SparqlGenerator { + * Represents a WHERE clause in a query. + * + * @param patterns the patterns in the WHERE clause. + * @param positiveEntities if this is a Gravsearch query, contains the entities that are used in positive contexts + * in the WHERE clause, i.e. not in MINUS or FILTER NOT EXISTS. + * @param querySchema if this is a Gravsearch query, represents the Knora API v2 ontology schema used in the query. + */ +case class WhereClause( + patterns: Seq[QueryPattern], + positiveEntities: Set[Entity] = Set.empty[Entity], + querySchema: Option[ApiV2Schema] = None +) extends SparqlGenerator { override def toSparql: String = "WHERE {\n" + patterns.map(_.toSparql).mkString + "}\n" } /** - * Represents a criterion to order by. - * - * @param queryVariable the variable used for ordering. - * @param isAscending indicates if the order is ascending or descending. - */ + * Represents a criterion to order by. + * + * @param queryVariable the variable used for ordering. + * @param isAscending indicates if the order is ascending or descending. + */ case class OrderCriterion(queryVariable: QueryVariable, isAscending: Boolean) extends SparqlGenerator { override def toSparql: String = if (isAscending) { @@ -632,31 +626,32 @@ case class OrderCriterion(queryVariable: QueryVariable, isAscending: Boolean) ex } /** - * Represents a FROM clause. - * - * @param defaultGraph the graph to be used as the default graph in the query. - */ + * Represents a FROM clause. + * + * @param defaultGraph the graph to be used as the default graph in the query. + */ case class FromClause(defaultGraph: IriRef) extends SparqlGenerator { override def toSparql: String = s"FROM ${defaultGraph.toSparql}\n" } /** - * Represents a SPARQL CONSTRUCT query. - * - * @param constructClause the CONSTRUCT clause. - * @param fromClause the FROM clause, if any. - * @param whereClause the WHERE clause. - * @param orderBy the variables that the results should be ordered by. - * @param offset if this is a Gravsearch query, represents the OFFSET specified in the query. - * @param querySchema if this is a Gravsearch query, represents the Knora API v2 ontology schema used in the query. - */ -case class ConstructQuery(constructClause: ConstructClause, - fromClause: Option[FromClause] = None, - whereClause: WhereClause, - orderBy: Seq[OrderCriterion] = Seq.empty[OrderCriterion], - offset: Long = 0, - querySchema: Option[ApiV2Schema] = None) - extends SparqlGenerator { + * Represents a SPARQL CONSTRUCT query. + * + * @param constructClause the CONSTRUCT clause. + * @param fromClause the FROM clause, if any. + * @param whereClause the WHERE clause. + * @param orderBy the variables that the results should be ordered by. + * @param offset if this is a Gravsearch query, represents the OFFSET specified in the query. + * @param querySchema if this is a Gravsearch query, represents the Knora API v2 ontology schema used in the query. + */ +case class ConstructQuery( + constructClause: ConstructClause, + fromClause: Option[FromClause] = None, + whereClause: WhereClause, + orderBy: Seq[OrderCriterion] = Seq.empty[OrderCriterion], + offset: Long = 0, + querySchema: Option[ApiV2Schema] = None +) extends SparqlGenerator { override def toSparql: String = { val stringBuilder = new StringBuilder @@ -678,25 +673,26 @@ case class ConstructQuery(constructClause: ConstructClause, } /** - * Represents a SPARQL SELECT query. - * - * @param variables the variables to be returned by the query. - * @param useDistinct indicates if DISTINCT should be used. - * @param fromClause the FROM clause, if any. - * @param whereClause the WHERE clause. - * @param orderBy the variables that the results should be ordered by. - * @param limit the maximum number of result rows to be returned. - * @param offset the offset to be used (limit of the previous query + 1 to do paging). - */ -case class SelectQuery(variables: Seq[SelectQueryColumn], - useDistinct: Boolean = true, - fromClause: Option[FromClause] = None, - whereClause: WhereClause, - groupBy: Seq[QueryVariable] = Seq.empty[QueryVariable], - orderBy: Seq[OrderCriterion] = Seq.empty[OrderCriterion], - limit: Option[Int] = None, - offset: Long = 0) - extends SparqlGenerator { + * Represents a SPARQL SELECT query. + * + * @param variables the variables to be returned by the query. + * @param useDistinct indicates if DISTINCT should be used. + * @param fromClause the FROM clause, if any. + * @param whereClause the WHERE clause. + * @param orderBy the variables that the results should be ordered by. + * @param limit the maximum number of result rows to be returned. + * @param offset the offset to be used (limit of the previous query + 1 to do paging). + */ +case class SelectQuery( + variables: Seq[SelectQueryColumn], + useDistinct: Boolean = true, + fromClause: Option[FromClause] = None, + whereClause: WhereClause, + groupBy: Seq[QueryVariable] = Seq.empty[QueryVariable], + orderBy: Seq[OrderCriterion] = Seq.empty[OrderCriterion], + limit: Option[Int] = None, + offset: Long = 0 +) extends SparqlGenerator { override def toSparql: String = { val stringBuilder = new StringBuilder diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlTransformer.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlTransformer.scala index 97c871d82f..d71845c8b7 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlTransformer.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/SparqlTransformer.scala @@ -25,44 +25,42 @@ import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} /** - * Methods and classes for transforming generated SPARQL. - */ + * Methods and classes for transforming generated SPARQL. + */ object SparqlTransformer { /** - * Transforms a non-triplestore-specific SELECT query for GraphDB. - * - * @param useInference `true` if the triplestore's inference should be used. - */ + * Transforms a non-triplestore-specific SELECT query for GraphDB. + * + * @param useInference `true` if the triplestore's inference should be used. + */ class GraphDBSelectToSelectTransformer(useInference: Boolean) extends SelectToSelectTransformer { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override def transformStatementInSelect(statementPattern: StatementPattern): Seq[StatementPattern] = Seq(statementPattern) - override def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[StatementPattern] = { + override def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[StatementPattern] = transformKnoraExplicitToGraphDBExplicit(statement = statementPattern, useInference = useInference) - } override def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = Seq(filterPattern) - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = moveBindToBeginning(moveLuceneToBeginning(patterns)) - } - override def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = { + override def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = transformLuceneQueryPatternForGraphDB(luceneQueryPattern) - } - override def getFromClause: Option[FromClause] = { + override def getFromClause: Option[FromClause] = if (useInference) { None } else { // To turn off inference, add FROM . Some(FromClause(IriRef(OntologyConstants.NamedGraphs.GraphDBExplicitNamedGraph.toSmartIri))) } - } override def enteringUnionBlock(): Unit = {} @@ -70,26 +68,29 @@ object SparqlTransformer { } /** - * Transforms a non-triplestore-specific SELECT for a triplestore that does not have inference enabled (e.g., Fuseki). - * - * @param simulateInference `true` if RDFS inference should be simulated using property path syntax. - */ + * Transforms a non-triplestore-specific SELECT for a triplestore that does not have inference enabled (e.g., Fuseki). + * + * @param simulateInference `true` if RDFS inference should be simulated using property path syntax. + */ class NoInferenceSelectToSelectTransformer(simulateInference: Boolean) extends SelectToSelectTransformer { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override def transformStatementInSelect(statementPattern: StatementPattern): Seq[StatementPattern] = Seq(statementPattern) - override def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[StatementPattern] = - transformStatementInWhereForNoInference(statementPattern = statementPattern, - simulateInference = simulateInference) + override def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[StatementPattern] = + transformStatementInWhereForNoInference( + statementPattern = statementPattern, + simulateInference = simulateInference + ) override def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = Seq(filterPattern) - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = moveBindToBeginning(optimiseIsDeletedWithFilter(moveLuceneToBeginning(patterns))) - } override def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = transformLuceneQueryPatternForFuseki(luceneQueryPattern) @@ -102,24 +103,24 @@ object SparqlTransformer { } /** - * Transforms a non-triplestore-specific CONSTRUCT query for GraphDB. - */ + * Transforms a non-triplestore-specific CONSTRUCT query for GraphDB. + */ class GraphDBConstructToConstructTransformer extends ConstructToConstructTransformer { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override def transformStatementInConstruct(statementPattern: StatementPattern): Seq[StatementPattern] = Seq(statementPattern) - override def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[StatementPattern] = { + override def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[StatementPattern] = transformKnoraExplicitToGraphDBExplicit(statement = statementPattern, useInference = true) - } override def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = Seq(filterPattern) - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = moveBindToBeginning(moveLuceneToBeginning(patterns)) - } override def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = transformLuceneQueryPatternForGraphDB(luceneQueryPattern) @@ -130,23 +131,24 @@ object SparqlTransformer { } /** - * Transforms a non-triplestore-specific CONSTRUCT query for a triplestore that does not have inference enabled (e.g., Fuseki). - */ + * Transforms a non-triplestore-specific CONSTRUCT query for a triplestore that does not have inference enabled (e.g., Fuseki). + */ class NoInferenceConstructToConstructTransformer extends ConstructToConstructTransformer { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override def transformStatementInConstruct(statementPattern: StatementPattern): Seq[StatementPattern] = Seq(statementPattern) - override def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[StatementPattern] = + override def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[StatementPattern] = transformStatementInWhereForNoInference(statementPattern = statementPattern, simulateInference = true) override def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = Seq(filterPattern) - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = moveBindToBeginning(optimiseIsDeletedWithFilter(moveLuceneToBeginning(patterns))) - } override def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = transformLuceneQueryPatternForFuseki(luceneQueryPattern) @@ -157,11 +159,11 @@ object SparqlTransformer { } /** - * Creates a syntactically valid variable base name, based on the given entity. - * - * @param entity the entity to be used to create a base name for a variable. - * @return a base name for a variable. - */ + * Creates a syntactically valid variable base name, based on the given entity. + * + * @param entity the entity to be used to create a base name for a variable. + * @return a base name for a variable. + */ def escapeEntityForVariable(entity: Entity): String = { val entityStr = entity match { case QueryVariable(varName) => varName @@ -176,12 +178,12 @@ object SparqlTransformer { } /** - * Creates a unique variable name from the given entity and the local part of a property IRI. - * - * @param base the entity to use to create the variable base name. - * @param propertyIri the IRI of the property whose local part will be used to form the unique name. - * @return a unique variable. - */ + * Creates a unique variable name from the given entity and the local part of a property IRI. + * + * @param base the entity to use to create the variable base name. + * @param propertyIri the IRI of the property whose local part will be used to form the unique name. + * @return a unique variable. + */ def createUniqueVariableNameFromEntityAndProperty(base: Entity, propertyIri: IRI): QueryVariable = { val propertyHashIndex = propertyIri.lastIndexOf('#') @@ -194,55 +196,56 @@ object SparqlTransformer { } /** - * Creates a unique variable name representing the `rdf:type` of an entity with a given base class. - * - * @param base the entity to use to create the variable base name. - * @param baseClassIri a base class of the entity's type. - * @return a unique variable. - */ - def createUniqueVariableNameForEntityAndBaseClass(base: Entity, baseClassIri: IriRef): QueryVariable = { + * Creates a unique variable name representing the `rdf:type` of an entity with a given base class. + * + * @param base the entity to use to create the variable base name. + * @param baseClassIri a base class of the entity's type. + * @return a unique variable. + */ + def createUniqueVariableNameForEntityAndBaseClass(base: Entity, baseClassIri: IriRef): QueryVariable = QueryVariable(escapeEntityForVariable(base) + "__subClassOf__" + escapeEntityForVariable(baseClassIri)) - } /** - * Create a unique variable from a whole statement. - * - * @param baseStatement the statement to be used to create the variable base name. - * @param suffix the suffix to be appended to the base name. - * @return a unique variable. - */ - def createUniqueVariableFromStatement(baseStatement: StatementPattern, suffix: String): QueryVariable = { + * Create a unique variable from a whole statement. + * + * @param baseStatement the statement to be used to create the variable base name. + * @param suffix the suffix to be appended to the base name. + * @return a unique variable. + */ + def createUniqueVariableFromStatement(baseStatement: StatementPattern, suffix: String): QueryVariable = QueryVariable( - escapeEntityForVariable(baseStatement.subj) + "__" + escapeEntityForVariable(baseStatement.pred) + "__" + escapeEntityForVariable( - baseStatement.obj) + "__" + suffix) - } + escapeEntityForVariable(baseStatement.subj) + "__" + escapeEntityForVariable( + baseStatement.pred + ) + "__" + escapeEntityForVariable(baseStatement.obj) + "__" + suffix + ) /** - * Create a unique variable name from a whole statement for a link value. - * - * @param baseStatement the statement to be used to create the variable base name. - * @return a unique variable for a link value. - */ - def createUniqueVariableFromStatementForLinkValue(baseStatement: StatementPattern): QueryVariable = { + * Create a unique variable name from a whole statement for a link value. + * + * @param baseStatement the statement to be used to create the variable base name. + * @return a unique variable for a link value. + */ + def createUniqueVariableFromStatementForLinkValue(baseStatement: StatementPattern): QueryVariable = createUniqueVariableFromStatement(baseStatement, "LinkValue") - } /** - * Optimises a query by replacing `knora-base:isDeleted false` with a `FILTER NOT EXISTS` pattern - * placed at the end of the block. - * - * @param patterns the block of patterns to be optimised. - * @return the result of the optimisation. - */ + * Optimises a query by replacing `knora-base:isDeleted false` with a `FILTER NOT EXISTS` pattern + * placed at the end of the block. + * + * @param patterns the block of patterns to be optimised. + * @return the result of the optimisation. + */ def optimiseIsDeletedWithFilter(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Separate the knora-base:isDeleted statements from the rest of the block. val (isDeletedPatterns: Seq[QueryPattern], otherPatterns: Seq[QueryPattern]) = patterns.partition { - case StatementPattern(_, - IriRef(SmartIri(OntologyConstants.KnoraBase.IsDeleted), _), - XsdLiteral("false", SmartIri(OntologyConstants.Xsd.Boolean)), - _) => + case StatementPattern( + _, + IriRef(SmartIri(OntologyConstants.KnoraBase.IsDeleted), _), + XsdLiteral("false", SmartIri(OntologyConstants.Xsd.Boolean)), + _ + ) => true case _ => false } @@ -265,11 +268,11 @@ object SparqlTransformer { } /** - * Optimises a query by moving BIND patterns to the beginning of a block. - * - * @param patterns the block of patterns to be optimised. - * @return the result of the optimisation. - */ + * Optimises a query by moving BIND patterns to the beginning of a block. + * + * @param patterns the block of patterns to be optimised. + * @return the result of the optimisation. + */ def moveBindToBeginning(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { val (bindQueryPatterns: Seq[QueryPattern], otherPatterns: Seq[QueryPattern]) = patterns.partition { case _: BindPattern => true @@ -280,11 +283,11 @@ object SparqlTransformer { } /** - * Optimises a query by moving Lucene query patterns to the beginning of a block. - * - * @param patterns the block of patterns to be optimised. - * @return the result of the optimisation. - */ + * Optimises a query by moving Lucene query patterns to the beginning of a block. + * + * @param patterns the block of patterns to be optimised. + * @return the result of the optimisation. + */ def moveLuceneToBeginning(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { val (luceneQueryPatterns: Seq[QueryPattern], otherPatterns: Seq[QueryPattern]) = patterns.partition { case _: LuceneQueryPattern => true @@ -295,14 +298,16 @@ object SparqlTransformer { } /** - * Transforms a statement in a WHERE clause for a triplestore that does not provide inference. - * - * @param statementPattern the statement pattern. - * @param simulateInference `true` if RDFS inference should be simulated using property path syntax. - * @return the statement pattern as expanded to work without inference. - */ - def transformStatementInWhereForNoInference(statementPattern: StatementPattern, - simulateInference: Boolean): Seq[StatementPattern] = { + * Transforms a statement in a WHERE clause for a triplestore that does not provide inference. + * + * @param statementPattern the statement pattern. + * @param simulateInference `true` if RDFS inference should be simulated using property path syntax. + * @return the statement pattern as expanded to work without inference. + */ + def transformStatementInWhereForNoInference( + statementPattern: StatementPattern, + simulateInference: Boolean + ): Seq[StatementPattern] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance statementPattern.pred match { @@ -343,7 +348,8 @@ object SparqlTransformer { val rdfTypeVariable: QueryVariable = createUniqueVariableNameForEntityAndBaseClass( base = statementPattern.subj, - baseClassIri = baseClassIri) + baseClassIri = baseClassIri + ) Seq( StatementPattern( @@ -365,7 +371,8 @@ object SparqlTransformer { val propertyVariable: QueryVariable = createUniqueVariableNameFromEntityAndProperty( base = statementPattern.pred, - propertyIri = OntologyConstants.Rdfs.SubPropertyOf) + propertyIri = OntologyConstants.Rdfs.SubPropertyOf + ) Seq( StatementPattern( @@ -397,14 +404,16 @@ object SparqlTransformer { } /** - * Transforms the the Knora explicit graph name to GraphDB explicit graph name. - * - * @param statement the given statement whose graph name has to be renamed. - * @param useInference `true` if the triplestore's inference should be used. - * @return the statement with the renamed graph, if given. - */ - private def transformKnoraExplicitToGraphDBExplicit(statement: StatementPattern, - useInference: Boolean): Seq[StatementPattern] = { + * Transforms the the Knora explicit graph name to GraphDB explicit graph name. + * + * @param statement the given statement whose graph name has to be renamed. + * @param useInference `true` if the triplestore's inference should be used. + * @return the statement with the renamed graph, if given. + */ + private def transformKnoraExplicitToGraphDBExplicit( + statement: StatementPattern, + useInference: Boolean + ): Seq[StatementPattern] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val transformedPattern = statement.copy( @@ -420,7 +429,8 @@ object SparqlTransformer { case Some(IriRef(_, _)) => throw AssertionException( - s"Named graphs other than ${OntologyConstants.NamedGraphs.KnoraExplicitNamedGraph} cannot occur in non-triplestore-specific generated search query SPARQL") + s"Named graphs other than ${OntologyConstants.NamedGraphs.KnoraExplicitNamedGraph} cannot occur in non-triplestore-specific generated search query SPARQL" + ) case None => None } @@ -430,11 +440,11 @@ object SparqlTransformer { } /** - * Transforms a [[LuceneQueryPattern]] for GraphDB. - * - * @param luceneQueryPattern the query pattern. - * @return GraphDB-specific statements implementing the query. - */ + * Transforms a [[LuceneQueryPattern]] for GraphDB. + * + * @param luceneQueryPattern the query pattern. + * @return GraphDB-specific statements implementing the query. + */ private def transformLuceneQueryPatternForGraphDB(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -451,11 +461,11 @@ object SparqlTransformer { } /** - * Transforms a [[LuceneQueryPattern]] for Fuseki. - * - * @param luceneQueryPattern the query pattern. - * @return Fuseki-specific statements implementing the query. - */ + * Transforms a [[LuceneQueryPattern]] for Fuseki. + * + * @param luceneQueryPattern the query pattern. + * @return Fuseki-specific statements implementing the query. + */ private def transformLuceneQueryPatternForFuseki(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParser.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParser.scala index 2bf9115a2b..addcec6d97 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParser.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParser.scala @@ -32,45 +32,46 @@ import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} import scala.jdk.CollectionConverters._ /** - * Parses a Gravsearch query. The syntax that is accepted is that of a SPARQL CONSTRUCT query, with some restrictions: - * - * - The query must be a CONSTRUCT query. - * - It must use no internal ontologies. - * - The CONSTRUCT clause may contain only quad patterns. - * - The WHERE clause may contain only quad patterns, FILTER, and UNION. - * - A UNION may not contain a nested UNION or OPTIONAL. - * - The value assigned in a BIND must be a Knora data IRI. - */ + * Parses a Gravsearch query. The syntax that is accepted is that of a SPARQL CONSTRUCT query, with some restrictions: + * + * - The query must be a CONSTRUCT query. + * - It must use no internal ontologies. + * - The CONSTRUCT clause may contain only quad patterns. + * - The WHERE clause may contain only quad patterns, FILTER, and UNION. + * - A UNION may not contain a nested UNION or OPTIONAL. + * - The value assigned in a BIND must be a Knora data IRI. + */ object GravsearchParser { // This implementation uses the RDF4J SPARQL parser. private val sparqlParserFactory = new SPARQLParserFactory() private val sparqlParser: QueryParser = sparqlParserFactory.getParser /** - * Given a string representation of a Gravsearch query, returns a [[ConstructQuery]]. - * - * @param query the Gravsearch string to be parsed. - * @return a [[ConstructQuery]]. - */ + * Given a string representation of a Gravsearch query, returns a [[ConstructQuery]]. + * + * @param query the Gravsearch string to be parsed. + * @return a [[ConstructQuery]]. + */ def parseQuery(query: String): ConstructQuery = { val visitor = new ConstructQueryModelVisitor - val parsedQuery = try { - sparqlParser.parseQuery(query, OntologyConstants.KnoraApiV2Simple.KnoraApiV2PrefixExpansion) - } catch { - case malformed: MalformedQueryException => - throw GravsearchException(s"Invalid search query: ${malformed.getMessage}") - } + val parsedQuery = + try { + sparqlParser.parseQuery(query, OntologyConstants.KnoraApiV2Simple.KnoraApiV2PrefixExpansion) + } catch { + case malformed: MalformedQueryException => + throw GravsearchException(s"Invalid search query: ${malformed.getMessage}") + } parsedQuery.getTupleExpr.visit(visitor) visitor.makeConstructQuery } /** - * An RDF4J [[algebra.QueryModelVisitor]] that converts a [[ParsedQuery]] into a [[ConstructQuery]]. - * - * @param isInNegation Indicates if the element currently processed is in a context of negation (FILTER NOT EXISTS or MINUS). - */ + * An RDF4J [[algebra.QueryModelVisitor]] that converts a [[ParsedQuery]] into a [[ConstructQuery]]. + * + * @param isInNegation Indicates if the element currently processed is in a context of negation (FILTER NOT EXISTS or MINUS). + */ class ConstructQueryModelVisitor(isInNegation: Boolean = false) extends algebra.QueryModelVisitor[GravsearchException] { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -105,22 +106,22 @@ object GravsearchParser { private val allIris: collection.mutable.Set[SmartIri] = collection.mutable.Set.empty[SmartIri] /** - * After this visitor has visited the parse tree, this method returns a [[ConstructQuery]] representing - * the query that was parsed. - * - * @return a [[ConstructQuery]]. - */ + * After this visitor has visited the parse tree, this method returns a [[ConstructQuery]] representing + * the query that was parsed. + * + * @return a [[ConstructQuery]]. + */ def makeConstructQuery: ConstructQuery = { /** - * Given a source name used in an [[algebra.ProjectionElem]], checks whether it's the name of a constant whose - * literal value was saved when the [[algebra.ExtensionElem]] nodes were processed. If so, returns a [[algebra.Var]] representing - * the literal value. Otherwise, returns an [[algebra.Var]] representing the name itself. The resulting [[algebra.Var]] can be - * passed to `makeStatementPatternSubject`, `makeStatementPatternPredicate`, or `makeStatementPatternObject`. - * - * @param sourceName the source name. - * @return an [[algebra.Var]] representing the name or its literal value. - */ + * Given a source name used in an [[algebra.ProjectionElem]], checks whether it's the name of a constant whose + * literal value was saved when the [[algebra.ExtensionElem]] nodes were processed. If so, returns a [[algebra.Var]] representing + * the literal value. Otherwise, returns an [[algebra.Var]] representing the name itself. The resulting [[algebra.Var]] can be + * passed to `makeStatementPatternSubject`, `makeStatementPatternPredicate`, or `makeStatementPatternObject`. + * + * @param sourceName the source name. + * @return an [[algebra.Var]] representing the name or its literal value. + */ def nameToVar(sourceName: String): algebra.Var = { val sparqlVar = new algebra.Var sparqlVar.setName(sourceName) @@ -156,9 +157,11 @@ object GravsearchParser { ConstructQuery( constructClause = ConstructClause(statements = constructStatements, querySchema = Some(querySchema)), - whereClause = WhereClause(patterns = getWherePatterns, - positiveEntities = positiveEntities.toSet, - querySchema = Some(querySchema)), + whereClause = WhereClause( + patterns = getWherePatterns, + positiveEntities = positiveEntities.toSet, + querySchema = Some(querySchema) + ), orderBy = orderBy.toSeq, offset = offset, querySchema = Some(querySchema) @@ -166,15 +169,13 @@ object GravsearchParser { } /** - * Returns the WHERE patterns found in the query. - */ - private def getWherePatterns: Seq[QueryPattern] = { + * Returns the WHERE patterns found in the query. + */ + private def getWherePatterns: Seq[QueryPattern] = wherePatterns.toSeq - } - private def unsupported(node: algebra.QueryModelNode): Unit = { + private def unsupported(node: algebra.QueryModelNode): Unit = throw GravsearchException(s"SPARQL feature not supported in Gravsearch query: $node") - } private def checkIriSchema(smartIri: SmartIri): Unit = { if (smartIri.isKnoraOntologyIri) { @@ -217,26 +218,25 @@ object GravsearchParser { } /** - * Converts an RDF4J [[algebra.Var]] into an [[Entity]]. - * - * @param objVar the [[algebra.Var]] to be converted. - * @return a [[Entity]]. - */ - private def makeEntityFromVar(objVar: algebra.Var): Entity = { + * Converts an RDF4J [[algebra.Var]] into an [[Entity]]. + * + * @param objVar the [[algebra.Var]] to be converted. + * @return a [[Entity]]. + */ + private def makeEntityFromVar(objVar: algebra.Var): Entity = if (objVar.isAnonymous || objVar.isConstant) { makeEntityFromValue(objVar.getValue) } else { makeQueryVariable(objVar.getName) } - } /** - * Converts an [[rdf4j.model.Value]] into an [[Entity]]. - * - * @param value the value to be converted. - * @return a [[Entity]]. - */ - private def makeEntityFromValue(value: rdf4j.model.Value): Entity = { + * Converts an [[rdf4j.model.Value]] into an [[Entity]]. + * + * @param value the value to be converted. + * @return a [[Entity]]. + */ + private def makeEntityFromValue(value: rdf4j.model.Value): Entity = value match { case iri: rdf4j.model.IRI => makeIri(iri) @@ -248,7 +248,6 @@ object GravsearchParser { case other => throw GravsearchException(s"Unsupported Value: $other with class ${other.getClass.getName}") } - } private def makeQueryVariable(variableName: String): QueryVariable = { val queryVar = if (variableName.contains("__")) { @@ -276,20 +275,18 @@ object GravsearchParser { wherePatterns.append(StatementPattern(subj = subj, pred = pred, obj = obj)) } - override def meet(node: algebra.Str): Unit = { + override def meet(node: algebra.Str): Unit = unsupported(node) - } - override def meet(node: algebra.Sum): Unit = { + override def meet(node: algebra.Sum): Unit = unsupported(node) - } /** - * Checks the contents of a block patterns to prevent nested blocks. - * - * @param patterns the patterns inside the block. - * @return the same patterns. - */ + * Checks the contents of a block patterns to prevent nested blocks. + * + * @param patterns the patterns inside the block. + * @return the same patterns. + */ private def checkBlockPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { for (pattern <- patterns) { pattern match { @@ -347,29 +344,23 @@ object GravsearchParser { wherePatterns.append(UnionPattern(Seq(leftPatterns) ++ rightPatterns)) } - override def meet(node: algebra.ValueConstant): Unit = { + override def meet(node: algebra.ValueConstant): Unit = unsupported(node) - } - override def meet(node: algebra.ListMemberOperator): Unit = { + override def meet(node: algebra.ListMemberOperator): Unit = unsupported(node) - } - override def meet(node: algebra.Var): Unit = { + override def meet(node: algebra.Var): Unit = unsupported(node) - } - override def meet(node: algebra.ZeroLengthPath): Unit = { + override def meet(node: algebra.ZeroLengthPath): Unit = unsupported(node) - } - override def meet(node: algebra.Regex): Unit = { + override def meet(node: algebra.Regex): Unit = unsupported(node) - } - override def meet(node: algebra.Reduced): Unit = { + override def meet(node: algebra.Reduced): Unit = node.visitChildren(this) - } override def meet(node: algebra.ProjectionElemList): Unit = { // A ProjectionElemList represents the patterns in the CONSTRUCT clause. They're represented using @@ -402,16 +393,15 @@ object GravsearchParser { } constructStatementsWithConstants.append( - ConstructStatementWithConstants(subj = subj.get, pred = pred.get, obj = obj.get)) + ConstructStatementWithConstants(subj = subj.get, pred = pred.get, obj = obj.get) + ) } - override def meet(node: algebra.ProjectionElem): Unit = { + override def meet(node: algebra.ProjectionElem): Unit = unsupported(node) - } - override def meet(node: algebra.Projection): Unit = { + override def meet(node: algebra.Projection): Unit = node.visitChildren(this) - } override def meet(node: algebra.OrderElem): Unit = { // Ignored, because it's handled in meet(algebra.Order) @@ -442,67 +432,53 @@ object GravsearchParser { // Do nothing, because this is handled elsewhere. } - override def meet(node: algebra.Not): Unit = { + override def meet(node: algebra.Not): Unit = unsupported(node) - } - override def meet(node: algebra.Namespace): Unit = { + override def meet(node: algebra.Namespace): Unit = unsupported(node) - } - override def meet(node: algebra.MultiProjection): Unit = { + override def meet(node: algebra.MultiProjection): Unit = node.visitChildren(this) - } - override def meet(move: algebra.Move): Unit = { + override def meet(move: algebra.Move): Unit = unsupported(move) - } - override def meet(node: algebra.Coalesce): Unit = { + override def meet(node: algebra.Coalesce): Unit = unsupported(node) - } override def meet(node: algebra.Compare): Unit = { // Do nothing, because this is handled elsewhere. } - override def meet(node: algebra.CompareAll): Unit = { + override def meet(node: algebra.CompareAll): Unit = unsupported(node) - } - override def meet(node: algebra.IsLiteral): Unit = { + override def meet(node: algebra.IsLiteral): Unit = unsupported(node) - } - override def meet(node: algebra.IsNumeric): Unit = { + override def meet(node: algebra.IsNumeric): Unit = unsupported(node) - } - override def meet(node: algebra.IsResource): Unit = { + override def meet(node: algebra.IsResource): Unit = unsupported(node) - } - override def meet(node: algebra.IsURI): Unit = { + override def meet(node: algebra.IsURI): Unit = unsupported(node) - } - override def meet(node: algebra.SameTerm): Unit = { + override def meet(node: algebra.SameTerm): Unit = unsupported(node) - } - override def meet(modify: algebra.Modify): Unit = { + override def meet(modify: algebra.Modify): Unit = unsupported(modify) - } - override def meet(node: algebra.Min): Unit = { + override def meet(node: algebra.Min): Unit = unsupported(node) - } - override def meet(node: algebra.Max): Unit = { + override def meet(node: algebra.Max): Unit = unsupported(node) - } - override def meet(node: algebra.ExtensionElem): Unit = { + override def meet(node: algebra.ExtensionElem): Unit = node.getExpr match { case valueConstant: algebra.ValueConstant => if (node.getName.startsWith("_const_")) { @@ -533,23 +509,18 @@ object GravsearchParser { case _ => () } - } - override def meet(node: algebra.Extension): Unit = { + override def meet(node: algebra.Extension): Unit = node.visitChildren(this) - } - override def meet(node: algebra.Exists): Unit = { + override def meet(node: algebra.Exists): Unit = unsupported(node) - } - override def meet(node: algebra.EmptySet): Unit = { + override def meet(node: algebra.EmptySet): Unit = unsupported(node) - } - override def meet(node: algebra.Distinct): Unit = { + override def meet(node: algebra.Distinct): Unit = unsupported(node) - } override def meet(node: algebra.Difference): Unit = { // Visit the nodes that the MINUS applies to. @@ -562,81 +533,63 @@ object GravsearchParser { allIris ++= subQueryVisitor.allIris } - override def meet(deleteData: algebra.DeleteData): Unit = { + override def meet(deleteData: algebra.DeleteData): Unit = unsupported(deleteData) - } - override def meet(node: algebra.Datatype): Unit = { + override def meet(node: algebra.Datatype): Unit = unsupported(node) - } - override def meet(clear: algebra.Clear): Unit = { + override def meet(clear: algebra.Clear): Unit = unsupported(clear) - } - override def meet(node: algebra.Bound): Unit = { + override def meet(node: algebra.Bound): Unit = unsupported(node) - } - override def meet(node: algebra.BNodeGenerator): Unit = { + override def meet(node: algebra.BNodeGenerator): Unit = unsupported(node) - } - override def meet(node: algebra.BindingSetAssignment): Unit = { + override def meet(node: algebra.BindingSetAssignment): Unit = unsupported(node) - } - override def meet(node: algebra.Avg): Unit = { + override def meet(node: algebra.Avg): Unit = unsupported(node) - } - override def meet(node: algebra.ArbitraryLengthPath): Unit = { + override def meet(node: algebra.ArbitraryLengthPath): Unit = unsupported(node) - } override def meet(node: algebra.And): Unit = { // Do nothing, because this is handled elsewhere. } - override def meet(add: algebra.Add): Unit = { + override def meet(add: algebra.Add): Unit = unsupported(add) - } - override def meet(node: algebra.QueryRoot): Unit = { + override def meet(node: algebra.QueryRoot): Unit = node.visitChildren(this) - } - override def meet(node: algebra.DescribeOperator): Unit = { + override def meet(node: algebra.DescribeOperator): Unit = throw GravsearchException(s"DESCRIBE queries are not allowed in search, please use a CONSTRUCT query instead") - } - override def meet(copy: algebra.Copy): Unit = { + override def meet(copy: algebra.Copy): Unit = unsupported(copy) - } - override def meet(node: algebra.Count): Unit = { + override def meet(node: algebra.Count): Unit = unsupported(node) - } - override def meet(create: algebra.Create): Unit = { + override def meet(create: algebra.Create): Unit = unsupported(create) - } - override def meet(node: algebra.Sample): Unit = { + override def meet(node: algebra.Sample): Unit = unsupported(node) - } - override def meet(node: algebra.Service): Unit = { + override def meet(node: algebra.Service): Unit = unsupported(node) - } - override def meet(node: algebra.SingletonSet): Unit = { + override def meet(node: algebra.SingletonSet): Unit = node.visitChildren(this) - } - override def meet(node: algebra.CompareAny): Unit = { + override def meet(node: algebra.CompareAny): Unit = unsupported(node) - } private def makeFilterExpression(valueExpr: algebra.ValueExpr): Expression = { valueExpr match { @@ -649,7 +602,8 @@ object GravsearchParser { leftArg = leftArg, operator = CompareExpressionOperator.lookup( operator, - throw GravsearchException(s"Operator $operator is not supported in a CompareExpression")), + throw GravsearchException(s"Operator $operator is not supported in a CompareExpression") + ), rightArg = rightArg ) @@ -701,11 +655,13 @@ object GravsearchParser { case queryVar: QueryVariable => queryVar case _ => throw GravsearchException( - s"Entity $objVar not allowed in regex function as the first argument, a variable is required") + s"Entity $objVar not allowed in regex function as the first argument, a variable is required" + ) } case other => throw GravsearchException( - s"$other is not allowed in regex function as first argument, a variable is required") + s"$other is not allowed in regex function as first argument, a variable is required" + ) } // second argument representing the REGEX pattern to be used to perform the check @@ -716,7 +672,8 @@ object GravsearchParser { valConstant.getValue.stringValue() case other => throw GravsearchException( - s"$other not allowed in regex function as the second argument, a string is expected") + s"$other not allowed in regex function as the second argument, a string is expected" + ) } @@ -726,7 +683,8 @@ object GravsearchParser { case None => None case other => throw GravsearchException( - s"$other not allowed in regex function as the third argument, a string is expected") + s"$other not allowed in regex function as the third argument, a string is expected" + ) } @@ -743,11 +701,13 @@ object GravsearchParser { case queryVar: QueryVariable => queryVar case _ => throw GravsearchException( - s"Entity $objVar not allowed in lang function as an argument, a variable is required") + s"Entity $objVar not allowed in lang function as an argument, a variable is required" + ) } case other => throw GravsearchException( - s"$other is not allowed in lang function as an argument, a variable is required") + s"$other is not allowed in lang function as an argument, a variable is required" + ) } LangFunction(textValueVar) @@ -757,7 +717,7 @@ object GravsearchParser { } override def meet(node: algebra.Filter): Unit = { - def makeFilterNotExists(not: algebra.Not): FilterNotExistsPattern = { + def makeFilterNotExists(not: algebra.Not): FilterNotExistsPattern = not.getArg match { case exists: algebra.Exists => val subQueryVisitor = new ConstructQueryModelVisitor(isInNegation = true) @@ -767,7 +727,6 @@ object GravsearchParser { case _ => throw GravsearchException(s"Unsupported NOT expression: $not") } - } // Visit the nodes that the filter applies to. node.getArg.visit(this) @@ -787,61 +746,47 @@ object GravsearchParser { wherePatterns.append(filterQueryPattern) } - override def meet(node: algebra.FunctionCall): Unit = { + override def meet(node: algebra.FunctionCall): Unit = unsupported(node) - } - override def meet(node: algebra.Group): Unit = { + override def meet(node: algebra.Group): Unit = unsupported(node) - } - override def meet(node: algebra.GroupConcat): Unit = { + override def meet(node: algebra.GroupConcat): Unit = unsupported(node) - } - override def meet(node: algebra.GroupElem): Unit = { + override def meet(node: algebra.GroupElem): Unit = unsupported(node) - } - override def meet(node: algebra.If): Unit = { + override def meet(node: algebra.If): Unit = unsupported(node) - } - override def meet(node: algebra.In): Unit = { + override def meet(node: algebra.In): Unit = unsupported(node) - } - override def meet(insertData: algebra.InsertData): Unit = { + override def meet(insertData: algebra.InsertData): Unit = unsupported(insertData) - } - override def meet(node: algebra.Intersection): Unit = { + override def meet(node: algebra.Intersection): Unit = unsupported(node) - } - override def meet(node: algebra.IRIFunction): Unit = { + override def meet(node: algebra.IRIFunction): Unit = unsupported(node) - } - override def meet(node: algebra.IsBNode): Unit = { + override def meet(node: algebra.IsBNode): Unit = unsupported(node) - } - override def meet(node: algebra.MathExpr): Unit = { + override def meet(node: algebra.MathExpr): Unit = unsupported(node) - } - override def meet(node: algebra.LocalName): Unit = { + override def meet(node: algebra.LocalName): Unit = unsupported(node) - } - override def meet(load: algebra.Load): Unit = { + override def meet(load: algebra.Load): Unit = unsupported(load) - } - override def meet(node: algebra.Like): Unit = { + override def meet(node: algebra.Like): Unit = unsupported(node) - } override def meet(node: algebra.LeftJoin): Unit = { // Visit the nodes that aren't in the OPTIONAL. @@ -863,26 +808,21 @@ object GravsearchParser { wherePatterns.append(OptionalPattern(rightArgVisitor.getWherePatterns ++ filterPattern)) } - override def meet(node: algebra.LangMatches): Unit = { + override def meet(node: algebra.LangMatches): Unit = unsupported(node) - } - override def meet(node: algebra.Lang): Unit = { + override def meet(node: algebra.Lang): Unit = unsupported(node) - } - override def meet(node: algebra.Label): Unit = { + override def meet(node: algebra.Label): Unit = unsupported(node) - } - override def meet(node: algebra.Join): Unit = { + override def meet(node: algebra.Join): Unit = // Successive statements are connected by Joins. node.visitChildren(this) - } - override def meetOther(node: algebra.QueryModelNode): Unit = { + override def meetOther(node: algebra.QueryModelNode): Unit = unsupported(node) - } } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchQueryChecker.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchQueryChecker.scala index 68b6b8d217..06c46901cb 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchQueryChecker.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchQueryChecker.scala @@ -33,25 +33,29 @@ import org.knora.webapi.messages.util.search._ object GravsearchQueryChecker { /** - * Checks that the correct schema is used in a statement pattern and that the predicate is allowed in Gravsearch. - * If the statement is in the CONSTRUCT clause in the complex schema, non-property variables may refer only to resources or Knora values. - * - * @param statementPattern the statement pattern to be checked. - * @param querySchema the API v2 ontology schema used in the query. - * @param typeInspectionResult the type inspection result. - * @param inConstructClause `true` if the statement is in the CONSTRUCT clause. - */ - def checkStatement(statementPattern: StatementPattern, - querySchema: ApiV2Schema, - typeInspectionResult: GravsearchTypeInspectionResult, - inConstructClause: Boolean = false): Unit = { + * Checks that the correct schema is used in a statement pattern and that the predicate is allowed in Gravsearch. + * If the statement is in the CONSTRUCT clause in the complex schema, non-property variables may refer only to resources or Knora values. + * + * @param statementPattern the statement pattern to be checked. + * @param querySchema the API v2 ontology schema used in the query. + * @param typeInspectionResult the type inspection result. + * @param inConstructClause `true` if the statement is in the CONSTRUCT clause. + */ + def checkStatement( + statementPattern: StatementPattern, + querySchema: ApiV2Schema, + typeInspectionResult: GravsearchTypeInspectionResult, + inConstructClause: Boolean = false + ): Unit = { // Check each entity in the statement. for (entity <- Seq(statementPattern.subj, statementPattern.pred, statementPattern.obj)) { entity match { case iriRef: IriRef => // The entity is an IRI. If it has a schema, check that it's the query schema. - iriRef.iri.checkApiV2Schema(querySchema, - throw GravsearchException(s"${iriRef.toSparql} is not in the correct schema")) + iriRef.iri.checkApiV2Schema( + querySchema, + throw GravsearchException(s"${iriRef.toSparql} is not in the correct schema") + ) // If we're in the CONSTRUCT clause, don't allow rdf, rdfs, or owl IRIs. if (inConstructClause && iriRef.iri.toString.contains('#')) { @@ -72,12 +76,14 @@ object GravsearchQueryChecker { case propertyTypeInfo: PropertyTypeInfo => propertyTypeInfo.objectTypeIri.checkApiV2Schema( querySchema, - throw GravsearchException(s"${entity.toSparql} is not in the correct schema")) + throw GravsearchException(s"${entity.toSparql} is not in the correct schema") + ) case nonPropertyTypeInfo: NonPropertyTypeInfo => nonPropertyTypeInfo.typeIri.checkApiV2Schema( querySchema, - throw GravsearchException(s"${entity.toSparql} is not in the correct schema")) + throw GravsearchException(s"${entity.toSparql} is not in the correct schema") + ) // If it's a variable that doesn't represent a property, and we're using the complex schema and the statement // is in the CONSTRUCT clause, check that it refers to a resource or value. @@ -129,14 +135,16 @@ object GravsearchQueryChecker { } /** - * Checks that the correct schema is used in a CONSTRUCT clause, that all the predicates used are allowed in Gravsearch, - * and that in the complex schema, non-property variables refer only to resources or Knora values. - * - * @param constructClause the CONSTRUCT clause to be checked. - * @param typeInspectionResult the type inspection result. - */ - def checkConstructClause(constructClause: ConstructClause, - typeInspectionResult: GravsearchTypeInspectionResult): Unit = { + * Checks that the correct schema is used in a CONSTRUCT clause, that all the predicates used are allowed in Gravsearch, + * and that in the complex schema, non-property variables refer only to resources or Knora values. + * + * @param constructClause the CONSTRUCT clause to be checked. + * @param typeInspectionResult the type inspection result. + */ + def checkConstructClause( + constructClause: ConstructClause, + typeInspectionResult: GravsearchTypeInspectionResult + ): Unit = for (statementPattern <- constructClause.statements) { checkStatement( statementPattern = statementPattern, @@ -146,7 +154,6 @@ object GravsearchQueryChecker { inConstructClause = true ) } - } // A set of predicates that aren't allowed in Gravsearch. val forbiddenPredicates: Set[IRI] = Set( diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/mainquery/GravsearchMainQueryGenerator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/mainquery/GravsearchMainQueryGenerator.scala index fd73cd8601..e251dc45f1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/mainquery/GravsearchMainQueryGenerator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/mainquery/GravsearchMainQueryGenerator.scala @@ -35,10 +35,10 @@ import org.knora.webapi.settings.KnoraSettingsImpl object GravsearchMainQueryGenerator { /** - * Constants used in the processing of Gravsearch queries. - * - * These constants are used to create SPARQL CONSTRUCT queries to be executed by the triplestore and to process the results that are returned. - */ + * Constants used in the processing of Gravsearch queries. + * + * These constants are used to create SPARQL CONSTRUCT queries to be executed by the triplestore and to process the results that are returned. + */ private object GravsearchConstants { // SPARQL variable representing the main resource and its properties @@ -61,7 +61,8 @@ object GravsearchMainQueryGenerator { // SPARQL variable representing the predicates of value objects of the main and dependent resources val mainAndDependentResourceValueObjectProp: QueryVariable = QueryVariable( - "mainAndDependentResourceValueObjectProp") + "mainAndDependentResourceValueObjectProp" + ) // SPARQL variable representing the objects of value objects of the main and dependent resources val mainAndDependentResourceValueObjectObj: QueryVariable = QueryVariable("mainAndDependentResourceValueObjectObj") @@ -93,32 +94,35 @@ object GravsearchMainQueryGenerator { } /** - * Represents dependent resources organized by main resource. - * - * @param dependentResourcesPerMainResource a set of dependent resource Iris organized by main resource. - */ + * Represents dependent resources organized by main resource. + * + * @param dependentResourcesPerMainResource a set of dependent resource Iris organized by main resource. + */ case class DependentResourcesPerMainResource(dependentResourcesPerMainResource: Map[IRI, Set[IRI]]) /** - * Represents value object variables and value object Iris organized by main resource. - * - * @param valueObjectVariablesAndValueObjectIris a set of value object Iris organized by value object variable and main resource. - */ + * Represents value object variables and value object Iris organized by main resource. + * + * @param valueObjectVariablesAndValueObjectIris a set of value object Iris organized by value object variable and main resource. + */ case class ValueObjectVariablesAndValueObjectIris( - valueObjectVariablesAndValueObjectIris: Map[IRI, Map[QueryVariable, Set[IRI]]]) + valueObjectVariablesAndValueObjectIris: Map[IRI, Map[QueryVariable, Set[IRI]]] + ) /** - * Collects the Iris of dependent resources per main resource from the results returned by the prequery. - * Dependent resource Iris are grouped by main resource. - * - * @param prequeryResponse the results returned by the prequery. - * @param transformer the transformer that was used to turn the Gravsearch query into the prequery. - * @param mainResourceVar the variable representing the main resource. - * @return a [[DependentResourcesPerMainResource]]. - */ - def getDependentResourceIrisPerMainResource(prequeryResponse: SparqlSelectResult, - transformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer, - mainResourceVar: QueryVariable): DependentResourcesPerMainResource = { + * Collects the Iris of dependent resources per main resource from the results returned by the prequery. + * Dependent resource Iris are grouped by main resource. + * + * @param prequeryResponse the results returned by the prequery. + * @param transformer the transformer that was used to turn the Gravsearch query into the prequery. + * @param mainResourceVar the variable representing the main resource. + * @return a [[DependentResourcesPerMainResource]]. + */ + def getDependentResourceIrisPerMainResource( + prequeryResponse: SparqlSelectResult, + transformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer, + mainResourceVar: QueryVariable + ): DependentResourcesPerMainResource = { // variables representing dependent resources val dependentResourceVariablesGroupConcat: Set[QueryVariable] = transformer.dependentResourceVariablesGroupConcat @@ -156,24 +160,28 @@ object GravsearchMainQueryGenerator { acc + (mainResIri -> dependentResIris) } - DependentResourcesPerMainResource(new ErrorHandlingMap(dependentResourcesPerMainRes, { key => - throw GravsearchException(s"main resource not found: $key") - })) + DependentResourcesPerMainResource( + new ErrorHandlingMap( + dependentResourcesPerMainRes, + key => throw GravsearchException(s"main resource not found: $key") + ) + ) } /** - * Collects object variables and their values per main resource from the results returned by the prequery. - * Value objects variables and their Iris are grouped by main resource. - * - * @param prequeryResponse the results returned by the prequery. - * @param transformer the transformer that was used to turn the Gravsearch query into the prequery. - * @param mainResourceVar the variable representing the main resource. - * @return [[ValueObjectVariablesAndValueObjectIris]]. - */ + * Collects object variables and their values per main resource from the results returned by the prequery. + * Value objects variables and their Iris are grouped by main resource. + * + * @param prequeryResponse the results returned by the prequery. + * @param transformer the transformer that was used to turn the Gravsearch query into the prequery. + * @param mainResourceVar the variable representing the main resource. + * @return [[ValueObjectVariablesAndValueObjectIris]]. + */ def getValueObjectVarsAndIrisPerMainResource( - prequeryResponse: SparqlSelectResult, - transformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer, - mainResourceVar: QueryVariable): ValueObjectVariablesAndValueObjectIris = { + prequeryResponse: SparqlSelectResult, + transformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer, + mainResourceVar: QueryVariable + ): ValueObjectVariablesAndValueObjectIris = { // value objects variables present in the prequery's WHERE clause val valueObjectVariablesConcat = transformer.valueObjectVariablesGroupConcat @@ -208,44 +216,54 @@ object GravsearchMainQueryGenerator { valueObjVarConcat -> valueObjIris }.toMap - val valueObjVarToIrisErrorHandlingMap = new ErrorHandlingMap(valueObjVarToIris, { key: QueryVariable => - throw GravsearchException(s"variable not found: $key") - }) + val valueObjVarToIrisErrorHandlingMap = new ErrorHandlingMap( + valueObjVarToIris, + { key: QueryVariable => + throw GravsearchException(s"variable not found: $key") + } + ) acc + (mainResIri -> valueObjVarToIrisErrorHandlingMap) } - ValueObjectVariablesAndValueObjectIris(new ErrorHandlingMap(valueObjVarsAndIris, { key => - throw GravsearchException(s"main resource not found: $key") - })) + ValueObjectVariablesAndValueObjectIris( + new ErrorHandlingMap(valueObjVarsAndIris, key => throw GravsearchException(s"main resource not found: $key")) + ) } /** - * Creates the main query to be sent to the triplestore. - * Requests two sets of information: about the main resources and the dependent resources. - * - * @param mainResourceIris IRIs of main resources to be queried. - * @param dependentResourceIris IRIs of dependent resources to be queried. - * @param valueObjectIris IRIs of value objects to be queried (for both main and dependent resources) - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @return the main [[ConstructQuery]] query to be executed. - */ - def createMainQuery(mainResourceIris: Set[IriRef], - dependentResourceIris: Set[IriRef], - valueObjectIris: Set[IRI], - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - settings: KnoraSettingsImpl): ConstructQuery = { + * Creates the main query to be sent to the triplestore. + * Requests two sets of information: about the main resources and the dependent resources. + * + * @param mainResourceIris IRIs of main resources to be queried. + * @param dependentResourceIris IRIs of dependent resources to be queried. + * @param valueObjectIris IRIs of value objects to be queried (for both main and dependent resources) + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @return the main [[ConstructQuery]] query to be executed. + */ + def createMainQuery( + mainResourceIris: Set[IriRef], + dependentResourceIris: Set[IriRef], + valueObjectIris: Set[IRI], + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + settings: KnoraSettingsImpl + ): ConstructQuery = { import GravsearchConstants._ implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // WHERE patterns for the main resource variable: check that main resource is a knora-base:Resource and that it is not marked as deleted val wherePatternsForMainResource = Seq( - ValuesPattern(mainResourceVar, mainResourceIris), // a ValuePattern that binds the main resources' IRIs to the main resource variable - StatementPattern.makeInferred(subj = mainResourceVar, - pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), - obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri)), + ValuesPattern( + mainResourceVar, + mainResourceIris + ), // a ValuePattern that binds the main resources' IRIs to the main resource variable + StatementPattern.makeInferred( + subj = mainResourceVar, + pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), + obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri) + ), StatementPattern.makeExplicit( subj = mainResourceVar, pred = IriRef(OntologyConstants.KnoraBase.IsDeleted.toSmartIri), @@ -266,28 +284,39 @@ object GravsearchMainQueryGenerator { // WHERE patterns for direct statements about the main resource and dependent resources val wherePatternsForMainAndDependentResources = Seq( - ValuesPattern(mainAndDependentResourceVar, mainResourceIris ++ dependentResourceIris), // a ValuePattern that binds the main and dependent resources' IRIs to a variable - StatementPattern.makeInferred(subj = mainAndDependentResourceVar, - pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), - obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri)), + ValuesPattern( + mainAndDependentResourceVar, + mainResourceIris ++ dependentResourceIris + ), // a ValuePattern that binds the main and dependent resources' IRIs to a variable + StatementPattern.makeInferred( + subj = mainAndDependentResourceVar, + pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), + obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri) + ), StatementPattern.makeExplicit( subj = mainAndDependentResourceVar, pred = IriRef(OntologyConstants.KnoraBase.IsDeleted.toSmartIri), obj = XsdLiteral(value = "false", datatype = OntologyConstants.Xsd.Boolean.toSmartIri) ), - StatementPattern.makeExplicit(subj = mainAndDependentResourceVar, - pred = mainAndDependentResourcePropVar, - obj = mainAndDependentResourceObjectVar) + StatementPattern.makeExplicit( + subj = mainAndDependentResourceVar, + pred = mainAndDependentResourcePropVar, + obj = mainAndDependentResourceObjectVar + ) ) // mark main and dependent resources as a knora-base:Resource in CONSTRUCT clause and return direct assertions about all resources val constructPatternsForMainAndDependentResources = Seq( - StatementPattern(subj = mainAndDependentResourceVar, - pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), - obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri)), - StatementPattern(subj = mainAndDependentResourceVar, - pred = mainAndDependentResourcePropVar, - obj = mainAndDependentResourceObjectVar) + StatementPattern( + subj = mainAndDependentResourceVar, + pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), + obj = IriRef(OntologyConstants.KnoraBase.Resource.toSmartIri) + ), + StatementPattern( + subj = mainAndDependentResourceVar, + pred = mainAndDependentResourcePropVar, + obj = mainAndDependentResourceObjectVar + ) ) if (valueObjectIris.nonEmpty) { @@ -300,20 +329,26 @@ object GravsearchMainQueryGenerator { // not including standoff markup in text values val wherePatternsForMainAndDependentResourcesValues = Seq( mainAndDependentResourcesValueObjectsValuePattern, - StatementPattern.makeInferred(subj = mainAndDependentResourceVar, - pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), - obj = mainAndDependentResourceValueObject), - StatementPattern.makeExplicit(subj = mainAndDependentResourceVar, - pred = mainAndDependentResourceValueProp, - obj = mainAndDependentResourceValueObject), + StatementPattern.makeInferred( + subj = mainAndDependentResourceVar, + pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), + obj = mainAndDependentResourceValueObject + ), + StatementPattern.makeExplicit( + subj = mainAndDependentResourceVar, + pred = mainAndDependentResourceValueProp, + obj = mainAndDependentResourceValueObject + ), StatementPattern.makeExplicit( subj = mainAndDependentResourceValueObject, pred = IriRef(OntologyConstants.KnoraBase.IsDeleted.toSmartIri), obj = XsdLiteral(value = "false", datatype = OntologyConstants.Xsd.Boolean.toSmartIri) ), - StatementPattern.makeExplicit(subj = mainAndDependentResourceValueObject, - pred = mainAndDependentResourceValueObjectProp, - obj = mainAndDependentResourceValueObjectObj), + StatementPattern.makeExplicit( + subj = mainAndDependentResourceValueObject, + pred = mainAndDependentResourceValueObjectProp, + obj = mainAndDependentResourceValueObjectObj + ), FilterPattern( CompareExpression( leftArg = mainAndDependentResourceValueObjectProp, @@ -325,15 +360,21 @@ object GravsearchMainQueryGenerator { // return assertions about the main and dependent resources' values in CONSTRUCT clause val constructPatternsForMainAndDependentResourcesValues = Seq( - StatementPattern(subj = mainAndDependentResourceVar, - pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), - obj = mainAndDependentResourceValueObject), - StatementPattern(subj = mainAndDependentResourceVar, - pred = mainAndDependentResourceValueProp, - obj = mainAndDependentResourceValueObject), - StatementPattern(subj = mainAndDependentResourceValueObject, - pred = mainAndDependentResourceValueObjectProp, - obj = mainAndDependentResourceValueObjectObj) + StatementPattern( + subj = mainAndDependentResourceVar, + pred = IriRef(OntologyConstants.KnoraBase.HasValue.toSmartIri), + obj = mainAndDependentResourceValueObject + ), + StatementPattern( + subj = mainAndDependentResourceVar, + pred = mainAndDependentResourceValueProp, + obj = mainAndDependentResourceValueObject + ), + StatementPattern( + subj = mainAndDependentResourceValueObject, + pred = mainAndDependentResourceValueObjectProp, + obj = mainAndDependentResourceValueObjectObj + ) ) // Check whether the response should include standoff. @@ -343,23 +384,29 @@ object GravsearchMainQueryGenerator { val wherePatternsForStandoff: Seq[QueryPattern] = if (queryStandoff) { Seq( mainAndDependentResourcesValueObjectsValuePattern, - StatementPattern.makeExplicit(subj = mainAndDependentResourceValueObject, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), - obj = standoffNodeVar), + StatementPattern.makeExplicit( + subj = mainAndDependentResourceValueObject, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), + obj = standoffNodeVar + ), StatementPattern.makeExplicit(subj = standoffNodeVar, pred = standoffPropVar, obj = standoffValueVar), - StatementPattern.makeExplicit(subj = standoffNodeVar, - pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasStartIndex.toSmartIri), - obj = standoffStartIndexVar), + StatementPattern.makeExplicit( + subj = standoffNodeVar, + pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasStartIndex.toSmartIri), + obj = standoffStartIndexVar + ), OptionalPattern( Seq( StatementPattern.makeExplicit( subj = standoffNodeVar, pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasInternalReference.toSmartIri), - obj = targetStandoffTagVar), + obj = targetStandoffTagVar + ), StatementPattern.makeExplicit( subj = targetStandoffTagVar, pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasOriginalXMLID.toSmartIri), - obj = targetOriginalXMLIDVar) + obj = targetOriginalXMLIDVar + ) ) ), FilterPattern( @@ -372,8 +419,10 @@ object GravsearchMainQueryGenerator { rightArg = CompareExpression( leftArg = standoffStartIndexVar, operator = CompareExpressionOperator.LESS_THAN_OR_EQUAL_TO, - rightArg = XsdLiteral(value = (settings.standoffPerPage - 1).toString, - datatype = OntologyConstants.Xsd.Integer.toSmartIri) + rightArg = XsdLiteral( + value = (settings.standoffPerPage - 1).toString, + datatype = OntologyConstants.Xsd.Integer.toSmartIri + ) ) ) ) @@ -385,13 +434,17 @@ object GravsearchMainQueryGenerator { // return standoff assertions val constructPatternsForStandoff: Seq[StatementPattern] = if (queryStandoff) { Seq( - StatementPattern(subj = mainAndDependentResourceValueObject, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), - obj = standoffNodeVar), + StatementPattern( + subj = mainAndDependentResourceValueObject, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasStandoff.toSmartIri), + obj = standoffNodeVar + ), StatementPattern(subj = standoffNodeVar, pred = standoffPropVar, obj = standoffValueVar), - StatementPattern(subj = standoffNodeVar, - pred = IriRef(OntologyConstants.KnoraBase.TargetHasOriginalXMLID.toSmartIri), - obj = targetOriginalXMLIDVar) + StatementPattern( + subj = standoffNodeVar, + pred = IriRef(OntologyConstants.KnoraBase.TargetHasOriginalXMLID.toSmartIri), + obj = targetOriginalXMLIDVar + ) ) } else { Seq.empty[StatementPattern] @@ -399,15 +452,18 @@ object GravsearchMainQueryGenerator { ConstructQuery( constructClause = ConstructClause( - statements = constructPatternsForMainResource ++ constructPatternsForMainAndDependentResources ++ constructPatternsForMainAndDependentResourcesValues ++ constructPatternsForStandoff + statements = + constructPatternsForMainResource ++ constructPatternsForMainAndDependentResources ++ constructPatternsForMainAndDependentResourcesValues ++ constructPatternsForStandoff ), whereClause = WhereClause( Seq( UnionPattern( - Seq(wherePatternsForMainResource, - wherePatternsForMainAndDependentResources, - wherePatternsForMainAndDependentResourcesValues, - wherePatternsForStandoff).filter(_.nonEmpty) + Seq( + wherePatternsForMainResource, + wherePatternsForMainAndDependentResources, + wherePatternsForMainAndDependentResourcesValues, + wherePatternsForStandoff + ).filter(_.nonEmpty) ) ) ) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/AbstractPrequeryGenerator.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/AbstractPrequeryGenerator.scala index 7558dc07bc..102661c4bc 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/AbstractPrequeryGenerator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/AbstractPrequeryGenerator.scala @@ -37,15 +37,16 @@ object AbstractPrequeryGenerator { } /** - * An abstract base class for [[WhereTransformer]] instances that generate SPARQL prequeries from Gravsearch input. - * - * @param typeInspectionResult the result of running type inspection on the Gravsearch input. - * @param querySchema the ontology schema used in the input Gravsearch query. - */ -abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, - typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema) - extends WhereTransformer { + * An abstract base class for [[WhereTransformer]] instances that generate SPARQL prequeries from Gravsearch input. + * + * @param typeInspectionResult the result of running type inspection on the Gravsearch input. + * @param querySchema the ontology schema used in the input Gravsearch query. + */ +abstract class AbstractPrequeryGenerator( + constructClause: ConstructClause, + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema +) extends WhereTransformer { protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // a Set containing all `TypeableEntity` (keys of `typeInspectionResult`) that have already been processed @@ -56,11 +57,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, protected val groupConcatVariableSuffix = "__Concat" /** - * A container for a generated variable representing a value literal. - * - * @param variable the generated variable. - * @param useInOrderBy if `true`, the generated variable can be used in ORDER BY. - */ + * A container for a generated variable representing a value literal. + * + * @param variable the generated variable. + * @param useInOrderBy if `true`, the generated variable can be used in ORDER BY. + */ private case class GeneratedQueryVariable(variable: QueryVariable, useInOrderBy: Boolean) // Variables that are created when processing filter statements or for a value object var used as a sort criterion. @@ -81,9 +82,9 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, var useInference = true /** - * When we enter a UNION block, pushes an empty collection of generated variables on to the stack - * valueVariablesAutomaticallyGenerated. - */ + * When we enter a UNION block, pushes an empty collection of generated variables on to the stack + * valueVariablesAutomaticallyGenerated. + */ override def enteringUnionBlock(): Unit = { valueVariablesAutomaticallyGenerated = Map .empty[QueryVariable, Set[GeneratedQueryVariable]] :: valueVariablesAutomaticallyGenerated @@ -92,30 +93,31 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * When we leave a UNION block, pops that block's collection of generated variables off the - * stack valueVariablesAutomaticallyGenerated. - */ + * When we leave a UNION block, pops that block's collection of generated variables off the + * stack valueVariablesAutomaticallyGenerated. + */ override def leavingUnionBlock(): Unit = { valueVariablesAutomaticallyGenerated = valueVariablesAutomaticallyGenerated.tail variablesInUnionBlocks = variablesInUnionBlocks.tail } - private def inUnionBlock: Boolean = { + private def inUnionBlock: Boolean = variablesInUnionBlocks.nonEmpty - } /** - * Saves a generated variable representing a value literal, if it hasn't been saved already. - * - * @param valueVar the variable representing the value. - * @param generatedVar the generated variable representing the value literal. - * @param useInOrderBy if `true`, the generated variable can be used in ORDER BY. - * @return `true` if the generated variable was saved, `false` if it had already been saved. - */ - private def addGeneratedVariableForValueLiteral(valueVar: QueryVariable, - generatedVar: QueryVariable, - useInOrderBy: Boolean = true): Boolean = { + * Saves a generated variable representing a value literal, if it hasn't been saved already. + * + * @param valueVar the variable representing the value. + * @param generatedVar the generated variable representing the value literal. + * @param useInOrderBy if `true`, the generated variable can be used in ORDER BY. + * @return `true` if the generated variable was saved, `false` if it had already been saved. + */ + private def addGeneratedVariableForValueLiteral( + valueVar: QueryVariable, + generatedVar: QueryVariable, + useInOrderBy: Boolean = true + ): Boolean = { val currentGeneratedVarsForBlock: Map[QueryVariable, Set[GeneratedQueryVariable]] = valueVariablesAutomaticallyGenerated.head @@ -123,10 +125,13 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, currentGeneratedVarsForBlock.getOrElse(valueVar, Set.empty[GeneratedQueryVariable]) val newGeneratedVarsForBlock = - if (!currentGeneratedVarsForValueVar.exists(currentGeneratedVar => currentGeneratedVar.variable == generatedVar)) { + if ( + !currentGeneratedVarsForValueVar.exists(currentGeneratedVar => currentGeneratedVar.variable == generatedVar) + ) { currentGeneratedVarsForBlock + (valueVar -> (currentGeneratedVarsForValueVar + GeneratedQueryVariable( generatedVar, - useInOrderBy))) + useInOrderBy + ))) } else { currentGeneratedVarsForBlock } @@ -137,26 +142,26 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Gets a saved generated variable representing a value literal, for use in ORDER BY. - * - * @param valueVar the variable representing the value. - * @return a generated variable that represents a value literal and can be used in ORDER BY, or `None` if no such variable has been saved. - */ - protected def getGeneratedVariableForValueLiteralInOrderBy(valueVar: QueryVariable): Option[QueryVariable] = { + * Gets a saved generated variable representing a value literal, for use in ORDER BY. + * + * @param valueVar the variable representing the value. + * @return a generated variable that represents a value literal and can be used in ORDER BY, or `None` if no such variable has been saved. + */ + protected def getGeneratedVariableForValueLiteralInOrderBy(valueVar: QueryVariable): Option[QueryVariable] = valueVariablesAutomaticallyGenerated.head.get(valueVar) match { case Some(generatedVars: Set[GeneratedQueryVariable]) => val generatedVarsForOrderBy: Set[QueryVariable] = generatedVars.filter(_.useInOrderBy).map(_.variable) if (generatedVarsForOrderBy.size > 1) { throw AssertionException( - s"More than one variable was generated for the literal values of ${valueVar.toSparql} and marked for use in ORDER BY: ${generatedVarsForOrderBy.map(_.toSparql).mkString(", ")}") + s"More than one variable was generated for the literal values of ${valueVar.toSparql} and marked for use in ORDER BY: ${generatedVarsForOrderBy.map(_.toSparql).mkString(", ")}" + ) } generatedVarsForOrderBy.headOption case None => None } - } // Generated statements for date literals, so we don't generate the same statements twice. private val generatedDateStatements = mutable.Set.empty[StatementPattern] @@ -165,8 +170,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, private val standoffMarkedUpVariables = mutable.Set.empty[QueryVariable] /** - * The variable in the CONSTRUCT clause that represents the main resource. - */ + * The variable in the CONSTRUCT clause that represents the main resource. + */ val mainResourceVariable: QueryVariable = { val mainResourceQueryVariables = constructClause.statements.foldLeft(Set.empty[QueryVariable]) { case (acc: Set[QueryVariable], statementPattern) => @@ -174,7 +179,9 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case IriRef(iri, _) => val iriStr = iri.toString - if (iriStr == OntologyConstants.KnoraApiV2Simple.IsMainResource || iriStr == OntologyConstants.KnoraApiV2Complex.IsMainResource) { + if ( + iriStr == OntologyConstants.KnoraApiV2Simple.IsMainResource || iriStr == OntologyConstants.KnoraApiV2Complex.IsMainResource + ) { statementPattern.obj match { case XsdLiteral(value, SmartIri(OntologyConstants.Xsd.Boolean)) if value.toBoolean => statementPattern.subj match { @@ -204,14 +211,16 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Creates additional statements for a non property type (e.g., a resource). - * - * @param nonPropertyTypeInfo type information about non property type. - * @param inputEntity the [[Entity]] to make the statements about. - * @return a sequence of [[QueryPattern]] representing the additional statements. - */ - private def createAdditionalStatementsForNonPropertyType(nonPropertyTypeInfo: NonPropertyTypeInfo, - inputEntity: Entity): Seq[QueryPattern] = { + * Creates additional statements for a non property type (e.g., a resource). + * + * @param nonPropertyTypeInfo type information about non property type. + * @param inputEntity the [[Entity]] to make the statements about. + * @return a sequence of [[QueryPattern]] representing the additional statements. + */ + private def createAdditionalStatementsForNonPropertyType( + nonPropertyTypeInfo: NonPropertyTypeInfo, + inputEntity: Entity + ): Seq[QueryPattern] = if (nonPropertyTypeInfo.isResourceType) { // inputEntity is either source or target of a linking property @@ -230,19 +239,20 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, Seq.empty[QueryPattern] } - } /** - * Generates statements matching a `knora-base:LinkValue`. - * - * @param linkSource the resource that is the source of the link. - * @param linkPred the link predicate. - * @param linkTarget the resource that is the target of the link. - * @return statements matching the `knora-base:LinkValue` that describes the link. - */ - private def generateStatementsForLinkValue(linkSource: Entity, - linkPred: Entity, - linkTarget: Entity): Seq[StatementPattern] = { + * Generates statements matching a `knora-base:LinkValue`. + * + * @param linkSource the resource that is the source of the link. + * @param linkPred the link predicate. + * @param linkTarget the resource that is the target of the link. + * @return statements matching the `knora-base:LinkValue` that describes the link. + */ + private def generateStatementsForLinkValue( + linkSource: Entity, + linkPred: Entity, + linkTarget: Entity + ): Seq[StatementPattern] = { // Generate a variable name representing the link value val linkValueObjVar: QueryVariable = SparqlTransformer.createUniqueVariableFromStatementForLinkValue( baseStatement = StatementPattern( @@ -278,49 +288,55 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // same source with the same property but with different targets. Seq( StatementPattern.makeInferred(subj = linkSource, pred = linkValueProp, obj = linkValueObjVar), - StatementPattern.makeExplicit(subj = linkValueObjVar, - pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), - obj = IriRef(OntologyConstants.KnoraBase.LinkValue.toSmartIri)), + StatementPattern.makeExplicit( + subj = linkValueObjVar, + pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), + obj = IriRef(OntologyConstants.KnoraBase.LinkValue.toSmartIri) + ), StatementPattern.makeExplicit( subj = linkValueObjVar, pred = IriRef(OntologyConstants.KnoraBase.IsDeleted.toSmartIri), obj = XsdLiteral(value = "false", datatype = OntologyConstants.Xsd.Boolean.toSmartIri) ), - StatementPattern.makeExplicit(subj = linkValueObjVar, - pred = IriRef(OntologyConstants.Rdf.Object.toSmartIri), - obj = linkTarget) + StatementPattern.makeExplicit( + subj = linkValueObjVar, + pred = IriRef(OntologyConstants.Rdf.Object.toSmartIri), + obj = linkTarget + ) ) } private def convertStatementForPropertyType(inputOrderBy: Seq[OrderCriterion])( - propertyTypeInfo: PropertyTypeInfo, - statementPattern: StatementPattern, - typeInspectionResult: GravsearchTypeInspectionResult): Seq[QueryPattern] = { + propertyTypeInfo: PropertyTypeInfo, + statementPattern: StatementPattern, + typeInspectionResult: GravsearchTypeInspectionResult + ): Seq[QueryPattern] = { /** - * Ensures that if the object of a statement is a variable, and is used in the ORDER BY clause of the input query, the subject of the statement - * is the main resource. Throws an exception otherwise. - * - * @param objectVar the variable that is the object of the statement. - */ - def checkSubjectInOrderBy(objectVar: QueryVariable): Unit = { + * Ensures that if the object of a statement is a variable, and is used in the ORDER BY clause of the input query, the subject of the statement + * is the main resource. Throws an exception otherwise. + * + * @param objectVar the variable that is the object of the statement. + */ + def checkSubjectInOrderBy(objectVar: QueryVariable): Unit = statementPattern.subj match { case subjectVar: QueryVariable => - if (mainResourceVariable != subjectVar && inputOrderBy.exists( - criterion => criterion.queryVariable == objectVar)) { + if ( + mainResourceVariable != subjectVar && inputOrderBy.exists(criterion => criterion.queryVariable == objectVar) + ) { throw GravsearchException( - s"Variable ${objectVar.toSparql} is used in ORDER BY, but does not represent a value of the main resource") + s"Variable ${objectVar.toSparql} is used in ORDER BY, but does not represent a value of the main resource" + ) } case _ => () } - } /** - * Transforms a statement pointing to a list node so it matches also any of its subnodes. - * - * @return transformed statements. - */ + * Transforms a statement pointing to a list node so it matches also any of its subnodes. + * + * @return transformed statements. + */ def handleListNode(): Seq[StatementPattern] = { if (querySchema == ApiV2Simple) { @@ -364,7 +380,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case objectVar: QueryVariable => checkSubjectInOrderBy(objectVar) case other => throw GravsearchException( - s"Object of a linking statement must be an IRI or a variable, but ${other.toSparql} given.") + s"Object of a linking statement must be an IRI or a variable, but ${other.toSparql} given." + ) } // Generate statement patterns to match the link value. @@ -405,7 +422,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case other => throw GravsearchException( - s"Object of a value property statement must be a QueryVariable, but ${other.toSparql} given.") + s"Object of a value property statement must be a QueryVariable, but ${other.toSparql} given." + ) } // Does the variable refer to a Knora value object? We assume it does if the query just uses the @@ -438,12 +456,14 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, valueTypesToValuePredsForOrderBy .getOrElse( nonPropertyTypeInfo.typeIri.toString, - throw GravsearchException(s"${criterion.queryVariable.toSparql} cannot be used in ORDER BY")) + throw GravsearchException(s"${criterion.queryVariable.toSparql} cannot be used in ORDER BY") + ) .toSmartIri case Some(_) => throw GravsearchException( - s"Variable ${criterion.queryVariable.toSparql} represents a property, and therefore cannot be used in ORDER BY") + s"Variable ${criterion.queryVariable.toSparql} represents a property, and therefore cannot be used in ORDER BY" + ) case None => throw GravsearchException(s"No type information found for ${criterion.queryVariable.toSparql}") @@ -452,7 +472,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // Generate the variable name. val variableForLiteral: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( criterion.queryVariable, - propertyIri.toString) + propertyIri.toString + ) // put the generated variable into a collection so it can be reused in `NonTriplestoreSpecificGravsearchToPrequeryGenerator.getOrderBy` // set to true when the variable already exists @@ -461,9 +482,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, if (!variableForLiteralExists) { // Generate a statement to get the literal value - val statementPatternForSortCriterion = StatementPattern.makeExplicit(subj = criterion.queryVariable, - pred = IriRef(propertyIri), - obj = variableForLiteral) + val statementPatternForSortCriterion = StatementPattern.makeExplicit( + subj = criterion.queryVariable, + pred = IriRef(propertyIri), + obj = variableForLiteral + ) Some(statementPatternForSortCriterion) } else { // statement has already been created @@ -474,7 +497,10 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, None } - Seq(statementPatternToInternalSchema(statementPattern, typeInspectionResult), valueObjectIsNotDeleted) ++ orderByStatement + Seq( + statementPatternToInternalSchema(statementPattern, typeInspectionResult), + valueObjectIsNotDeleted + ) ++ orderByStatement } else { // The variable doesn't refer to a value object. Just convert the statement pattern to the internal schema. Seq(statementPatternToInternalSchema(statementPattern, typeInspectionResult)) @@ -488,7 +514,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // has to use the knora-api:standoffLink function instead. if (maybeSubjectType.exists(_.isStandoffTagType) && propertyTypeInfo.objectIsResourceType) { throw GravsearchException( - s"Invalid statement pattern (use the knora-api:standoffLink function instead): ${statementPattern.toSparql.trim}") + s"Invalid statement pattern (use the knora-api:standoffLink function instead): ${statementPattern.toSparql.trim}" + ) } else { // Is the object of the statement a list node? propertyTypeInfo.objectTypeIri match { @@ -509,11 +536,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Processes Gravsearch options. - * - * @param statementPattern the statement specifying the option to be set. - */ - private def processGravsearchOption(statementPattern: StatementPattern): Unit = { + * Processes Gravsearch options. + * + * @param statementPattern the statement specifying the option to be set. + */ + private def processGravsearchOption(statementPattern: StatementPattern): Unit = statementPattern.pred match { case iriRef: IriRef if OntologyConstants.KnoraApi.UseInferenceIris.contains(iriRef.iri.toString) => useInference = statementPattern.obj match { @@ -525,30 +552,28 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case other => throw GravsearchException(s"Invalid predicate for knora-api:GravsearchOptions: ${other.toSparql}") } - } /** - * If we're in a UNION block, records any variables that are used in the specified statement, - * so we can make sure that they're defined before they're used in a FILTER pattern. - * - * @param statementPattern the statement pattern being processed. - */ + * If we're in a UNION block, records any variables that are used in the specified statement, + * so we can make sure that they're defined before they're used in a FILTER pattern. + * + * @param statementPattern the statement pattern being processed. + */ private def recordVariablesInUnionBlock(statementPattern: StatementPattern): Unit = { - def entityAsVariable(entity: Entity): Option[QueryVariable] = { + def entityAsVariable(entity: Entity): Option[QueryVariable] = entity match { case queryVariable: QueryVariable => Some(queryVariable) case _ => None } - } // Are we in a UNION block? variablesInUnionBlocks match { case variablesInCurrentBlock :: tail => // Yes. Collect any variables in the statement. - val newVariablesInCurrentBlock: Set[QueryVariable] = variablesInCurrentBlock ++ entityAsVariable( - statementPattern.subj) ++ - entityAsVariable(statementPattern.pred) ++ - entityAsVariable(statementPattern.obj) + val newVariablesInCurrentBlock: Set[QueryVariable] = + variablesInCurrentBlock ++ entityAsVariable(statementPattern.subj) ++ + entityAsVariable(statementPattern.pred) ++ + entityAsVariable(statementPattern.obj) // Record them. variablesInUnionBlocks = newVariablesInCurrentBlock :: tail @@ -559,9 +584,10 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } } - protected def processStatementPatternFromWhereClause(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[QueryPattern] = { - + protected def processStatementPatternFromWhereClause( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[QueryPattern] = // Does this statement set a Gravsearch option? statementPattern.subj match { case iriRef: IriRef if OntologyConstants.KnoraApi.GravsearchOptionsIris.contains(iriRef.iri.toString) => @@ -602,23 +628,23 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, additionalStatementsForSubj ++ additionalStatementsForWholeStatement ++ additionalStatementsForObj } - } /** - * Creates additional statements for a given [[Entity]] based on type information using `conversionFuncForNonPropertyType` - * for a non property type (e.g., a resource). - * - * @param entity the entity to be taken into consideration (a statement's subject or object). - * @param typeInspectionResult type information. - * @param processedTypeInfo the keys of type information that have already been looked at. - * @param conversionFuncForNonPropertyType the function to use to create additional statements. - * @return a sequence of [[QueryPattern]] representing the additional statements. - */ + * Creates additional statements for a given [[Entity]] based on type information using `conversionFuncForNonPropertyType` + * for a non property type (e.g., a resource). + * + * @param entity the entity to be taken into consideration (a statement's subject or object). + * @param typeInspectionResult type information. + * @param processedTypeInfo the keys of type information that have already been looked at. + * @param conversionFuncForNonPropertyType the function to use to create additional statements. + * @return a sequence of [[QueryPattern]] representing the additional statements. + */ private def checkForNonPropertyTypeInfoForEntity( - entity: Entity, - typeInspectionResult: GravsearchTypeInspectionResult, - processedTypeInfo: mutable.Set[TypeableEntity], - conversionFuncForNonPropertyType: (NonPropertyTypeInfo, Entity) => Seq[QueryPattern]): Seq[QueryPattern] = { + entity: Entity, + typeInspectionResult: GravsearchTypeInspectionResult, + processedTypeInfo: mutable.Set[TypeableEntity], + conversionFuncForNonPropertyType: (NonPropertyTypeInfo, Entity) => Seq[QueryPattern] + ): Seq[QueryPattern] = { val typesNotYetProcessed = typeInspectionResult.copy(entities = typeInspectionResult.entities -- processedTypeInfo) typesNotYetProcessed.getTypeOfEntity(entity) match { @@ -634,19 +660,22 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Converts the given statement based on the given type information using `conversionFuncForPropertyType`. - * - * @param statementPattern the statement to be converted. - * @param typeInspectionResult type information. - * @param conversionFuncForPropertyType the function to use for the conversion. - * @return a sequence of [[QueryPattern]] representing the converted statement. - */ + * Converts the given statement based on the given type information using `conversionFuncForPropertyType`. + * + * @param statementPattern the statement to be converted. + * @param typeInspectionResult type information. + * @param conversionFuncForPropertyType the function to use for the conversion. + * @return a sequence of [[QueryPattern]] representing the converted statement. + */ private def checkForPropertyTypeInfoForStatement( - statementPattern: StatementPattern, - typeInspectionResult: GravsearchTypeInspectionResult, - conversionFuncForPropertyType: (PropertyTypeInfo, - StatementPattern, - GravsearchTypeInspectionResult) => Seq[QueryPattern]): Seq[QueryPattern] = { + statementPattern: StatementPattern, + typeInspectionResult: GravsearchTypeInspectionResult, + conversionFuncForPropertyType: ( + PropertyTypeInfo, + StatementPattern, + GravsearchTypeInspectionResult + ) => Seq[QueryPattern] + ): Seq[QueryPattern] = typeInspectionResult.getTypeOfEntity(statementPattern.pred) match { case Some(propInfo: PropertyTypeInfo) => // process type information for the predicate into additional statements @@ -659,7 +688,6 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // no type information given and thus no further processing needed, just return the originally given statement (e.g., rdf:type), converted to the internal schema. Seq(statementPatternToInternalSchema(statementPattern, typeInspectionResult)) } - } // A Map of knora-api value types (both complex and simple) to the corresponding knora-base value predicates // that point to literals. This is used only for generating additional statements for ORDER BY clauses, so it only needs to include @@ -683,15 +711,16 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) /** - * Calls [[GravsearchQueryChecker.checkStatement]], then converts the specified statement pattern to the internal schema. - * - * @param statementPattern the statement pattern to be converted. - * @param typeInspectionResult the type inspection result. - * @return the converted statement pattern. - */ + * Calls [[GravsearchQueryChecker.checkStatement]], then converts the specified statement pattern to the internal schema. + * + * @param statementPattern the statement pattern to be converted. + * @param typeInspectionResult the type inspection result. + * @return the converted statement pattern. + */ private def statementPatternToInternalSchema( - statementPattern: StatementPattern, - typeInspectionResult: GravsearchTypeInspectionResult): StatementPattern = { + statementPattern: StatementPattern, + typeInspectionResult: GravsearchTypeInspectionResult + ): StatementPattern = { GravsearchQueryChecker.checkStatement( statementPattern = statementPattern, querySchema = querySchema, @@ -702,63 +731,71 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Given a variable representing a linking property, creates a variable representing the corresponding link value property. - * - * @param linkingPropertyQueryVariable variable representing a linking property. - * @return variable representing the corresponding link value property. - */ + * Given a variable representing a linking property, creates a variable representing the corresponding link value property. + * + * @param linkingPropertyQueryVariable variable representing a linking property. + * @return variable representing the corresponding link value property. + */ private def createLinkValuePropertyVariableFromLinkingPropertyVariable( - linkingPropertyQueryVariable: QueryVariable): QueryVariable = { + linkingPropertyQueryVariable: QueryVariable + ): QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = linkingPropertyQueryVariable, propertyIri = OntologyConstants.KnoraBase.HasLinkToValue ) - } /** - * Represents a transformed Filter expression and additional statement patterns that possibly had to be created during transformation. - * - * @param expression the transformed FILTER expression. In some cases, a given FILTER expression is replaced by additional statements, but - * only if it is the top-level expression in the FILTER. - * @param additionalPatterns additionally created query patterns. - */ - protected case class TransformedFilterPattern(expression: Option[Expression], - additionalPatterns: Seq[QueryPattern] = Seq.empty[QueryPattern]) + * Represents a transformed Filter expression and additional statement patterns that possibly had to be created during transformation. + * + * @param expression the transformed FILTER expression. In some cases, a given FILTER expression is replaced by additional statements, but + * only if it is the top-level expression in the FILTER. + * @param additionalPatterns additionally created query patterns. + */ + protected case class TransformedFilterPattern( + expression: Option[Expression], + additionalPatterns: Seq[QueryPattern] = Seq.empty[QueryPattern] + ) /** - * Handles query variables that represent properties in a [[FilterPattern]]. - * - * @param queryVar the query variable to be handled. - * @param comparisonOperator the comparison operator used in the filter pattern. - * @param iriRef the IRI the property query variable is restricted to. - * @param propInfo information about the query variable's type. - * @return a [[TransformedFilterPattern]]. - */ - private def handlePropertyIriQueryVar(queryVar: QueryVariable, - comparisonOperator: CompareExpressionOperator.Value, - iriRef: IriRef, - propInfo: PropertyTypeInfo): TransformedFilterPattern = { + * Handles query variables that represent properties in a [[FilterPattern]]. + * + * @param queryVar the query variable to be handled. + * @param comparisonOperator the comparison operator used in the filter pattern. + * @param iriRef the IRI the property query variable is restricted to. + * @param propInfo information about the query variable's type. + * @return a [[TransformedFilterPattern]]. + */ + private def handlePropertyIriQueryVar( + queryVar: QueryVariable, + comparisonOperator: CompareExpressionOperator.Value, + iriRef: IriRef, + propInfo: PropertyTypeInfo + ): TransformedFilterPattern = { iriRef.iri.checkApiV2Schema(querySchema, throw GravsearchException(s"Invalid schema for IRI: ${iriRef.toSparql}")) // make sure that the comparison operator is a CompareExpressionOperator.EQUALS if (comparisonOperator != CompareExpressionOperator.EQUALS) throw GravsearchException( - s"Comparison operator in a CompareExpression for a property type must be ${CompareExpressionOperator.EQUALS}, but '$comparisonOperator' given (for negations use 'FILTER NOT EXISTS')") + s"Comparison operator in a CompareExpression for a property type must be ${CompareExpressionOperator.EQUALS}, but '$comparisonOperator' given (for negations use 'FILTER NOT EXISTS')" + ) TransformedFilterPattern( - Some(CompareExpression(queryVar, comparisonOperator, iriRef.toOntologySchema(InternalSchema)))) + Some(CompareExpression(queryVar, comparisonOperator, iriRef.toOntologySchema(InternalSchema))) + ) } /** - * Handles query variables that represent a list node label in a [[FilterPattern]]. - * - * @param queryVar the query variable to be handled. - * @param comparisonOperator the comparison operator used in the filter pattern. - * @param literalValueExpression the label to match against. - */ - private def handleListQueryVar(queryVar: QueryVariable, - comparisonOperator: CompareExpressionOperator.Value, - literalValueExpression: Expression): TransformedFilterPattern = { + * Handles query variables that represent a list node label in a [[FilterPattern]]. + * + * @param queryVar the query variable to be handled. + * @param comparisonOperator the comparison operator used in the filter pattern. + * @param literalValueExpression the label to match against. + */ + private def handleListQueryVar( + queryVar: QueryVariable, + comparisonOperator: CompareExpressionOperator.Value, + literalValueExpression: Expression + ): TransformedFilterPattern = { // make sure that the expression is a literal of the expected type val nodeLabel: String = literalValueExpression match { @@ -776,7 +813,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, throw GravsearchException( s"Invalid operator '$comparisonOperator' in expression (allowed operators in this context are ${validComparisonOperators .map(op => "'" + op + "'") - .mkString(", ")})") + .mkString(", ")})" + ) // Generate a variable name representing the list node pointed to by the list value object val listNodeVar: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( @@ -796,37 +834,44 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, CompareExpression( StrFunction(listNodeLabel), comparisonOperator, - XsdLiteral(nodeLabel, OntologyConstants.Xsd.String.toSmartIri))), // compares the provided literal to the value object's literal value + XsdLiteral(nodeLabel, OntologyConstants.Xsd.String.toSmartIri) + ) + ), // compares the provided literal to the value object's literal value Seq( // connects the query variable with the list node label - StatementPattern.makeExplicit(subj = queryVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasListNode.toSmartIri), - listNodeVar), - StatementPattern.makeExplicit(subj = listNodeVar, - pred = IriRef(OntologyConstants.Rdfs.Label.toSmartIri), - obj = listNodeLabel) + StatementPattern.makeExplicit( + subj = queryVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasListNode.toSmartIri), + listNodeVar + ), + StatementPattern.makeExplicit( + subj = listNodeVar, + pred = IriRef(OntologyConstants.Rdfs.Label.toSmartIri), + obj = listNodeLabel + ) ) ) } /** - * Handles query variables that represent literals in a [[FilterPattern]]. - * - * @param queryVar the query variable to be handled. - * @param comparisonOperator the comparison operator used in the filter pattern. - * @param literalValueExpression the literal provided in the [[FilterPattern]] as an [[Expression]]. - * @param xsdType valid xsd types of the literal. - * @param valueHasProperty the property of the value object pointing to the literal (in the internal schema). - * @param validComparisonOperators a set of valid comparison operators, if to be restricted. - * @return a [[TransformedFilterPattern]]. - */ - private def handleLiteralQueryVar(queryVar: QueryVariable, - comparisonOperator: CompareExpressionOperator.Value, - literalValueExpression: Expression, - xsdType: Set[IRI], - valueHasProperty: IRI, - validComparisonOperators: Set[CompareExpressionOperator.Value] = - Set.empty[CompareExpressionOperator.Value]): TransformedFilterPattern = { + * Handles query variables that represent literals in a [[FilterPattern]]. + * + * @param queryVar the query variable to be handled. + * @param comparisonOperator the comparison operator used in the filter pattern. + * @param literalValueExpression the literal provided in the [[FilterPattern]] as an [[Expression]]. + * @param xsdType valid xsd types of the literal. + * @param valueHasProperty the property of the value object pointing to the literal (in the internal schema). + * @param validComparisonOperators a set of valid comparison operators, if to be restricted. + * @return a [[TransformedFilterPattern]]. + */ + private def handleLiteralQueryVar( + queryVar: QueryVariable, + comparisonOperator: CompareExpressionOperator.Value, + literalValueExpression: Expression, + xsdType: Set[IRI], + valueHasProperty: IRI, + validComparisonOperators: Set[CompareExpressionOperator.Value] = Set.empty[CompareExpressionOperator.Value] + ): TransformedFilterPattern = { // make sure that the expression is a literal of the expected type val literal: XsdLiteral = literalValueExpression match { @@ -834,7 +879,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case other => throw GravsearchException( - s"Invalid right argument ${other.toSparql} in comparison (allowed types in this context are ${xsdType.map(_.toSmartIri.toSparql).mkString(", ")})") + s"Invalid right argument ${other.toSparql} in comparison (allowed types in this context are ${xsdType.map(_.toSmartIri.toSparql).mkString(", ")})" + ) } // check if comparison operator is supported for given type @@ -842,7 +888,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, throw GravsearchException( s"Invalid operator '$comparisonOperator' in expression (allowed operators in this context are ${validComparisonOperators .map(op => "'" + op + "'") - .mkString(", ")})") + .mkString(", ")})" + ) // Does the variable refer to resource metadata? if (resourceMetadataVariables.contains(queryVar)) { @@ -866,32 +913,38 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, if (addGeneratedVariableForValueLiteral(queryVar, valueObjectLiteralVar)) { Seq( // connects the query variable with the value object (internal structure: values are represented as objects) - StatementPattern.makeExplicit(subj = queryVar, - pred = IriRef(valueHasProperty.toSmartIri), - valueObjectLiteralVar) + StatementPattern.makeExplicit( + subj = queryVar, + pred = IriRef(valueHasProperty.toSmartIri), + valueObjectLiteralVar + ) ) } else { Seq.empty[StatementPattern] } TransformedFilterPattern( - Some(CompareExpression(valueObjectLiteralVar, comparisonOperator, literal)), // compares the provided literal to the value object's literal value + Some( + CompareExpression(valueObjectLiteralVar, comparisonOperator, literal) + ), // compares the provided literal to the value object's literal value statementToAddForValueHas ) } } /** - * Handles query variables that represent a date in a [[FilterPattern]]. - * - * @param queryVar the query variable to be handled. - * @param comparisonOperator the comparison operator used in the filter pattern. - * @param dateValueExpression the date literal provided in the [[FilterPattern]] as an [[Expression]]. - * @return a [[TransformedFilterPattern]]. - */ - private def handleDateQueryVar(queryVar: QueryVariable, - comparisonOperator: CompareExpressionOperator.Value, - dateValueExpression: Expression): TransformedFilterPattern = { + * Handles query variables that represent a date in a [[FilterPattern]]. + * + * @param queryVar the query variable to be handled. + * @param comparisonOperator the comparison operator used in the filter pattern. + * @param dateValueExpression the date literal provided in the [[FilterPattern]] as an [[Expression]]. + * @return a [[TransformedFilterPattern]]. + */ + private def handleDateQueryVar( + queryVar: QueryVariable, + comparisonOperator: CompareExpressionOperator.Value, + dateValueExpression: Expression + ): TransformedFilterPattern = { // make sure that the right argument is a string literal (dates are represented as knora date strings in knora-api simple) val dateStringLiteral: XsdLiteral = dateValueExpression match { @@ -904,7 +957,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // validate Knora date string val dateStr: String = stringFormatter.validateDate( dateStringLiteral.value, - throw BadRequestException(s"${dateStringLiteral.value} is not a valid date string")) + throw BadRequestException(s"${dateStringLiteral.value} is not a valid date string") + ) // Convert it to Julian Day Numbers. val dateValueContent = DateValueContentV2.parse(dateStr) @@ -912,7 +966,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // Generate a variable name representing the period's start val dateValueHasStartVar = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = queryVar, - propertyIri = OntologyConstants.KnoraBase.ValueHasStartJDN) + propertyIri = OntologyConstants.KnoraBase.ValueHasStartJDN + ) // sort dates by their period's start (in the prequery) // is set to `true` if the date value object var is a sort criterion and has been handled already @@ -921,15 +976,19 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // Generate a variable name representing the period's end val dateValueHasEndVar = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = queryVar, - propertyIri = OntologyConstants.KnoraBase.ValueHasEndJDN) + propertyIri = OntologyConstants.KnoraBase.ValueHasEndJDN + ) // connects the value object with the periods start variable // only generate a new statement if it has not already been created when handling the sort criteria val dateValStartStatementOption: Option[StatementPattern] = if (!dateValVarExists) { Some( - StatementPattern.makeExplicit(subj = queryVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasStartJDN.toSmartIri), - obj = dateValueHasStartVar)) + StatementPattern.makeExplicit( + subj = queryVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasStartJDN.toSmartIri), + obj = dateValueHasStartVar + ) + ) } else { None } @@ -938,7 +997,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val dateValEndStatement = StatementPattern.makeExplicit( subj = queryVar, pred = IriRef(OntologyConstants.KnoraBase.ValueHasEndJDN.toSmartIri), - obj = dateValueHasEndVar) + obj = dateValueHasEndVar + ) // process filter expression based on given comparison operator comparisonOperator match { @@ -960,7 +1020,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val filter = AndExpression(leftArgFilter, rightArgFilter) val statementsToAdd = (dateValStartStatementOption.toSeq :+ dateValEndStatement).filterNot(statement => - generatedDateStatements.contains(statement)) + generatedDateStatements.contains(statement) + ) generatedDateStatements ++= statementsToAdd TransformedFilterPattern( @@ -985,7 +1046,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val filter = OrExpression(leftArgFilter, rightArgFilter) val statementsToAdd = (dateValStartStatementOption.toSeq :+ dateValEndStatement).filterNot(statement => - generatedDateStatements.contains(statement)) + generatedDateStatements.contains(statement) + ) generatedDateStatements ++= statementsToAdd TransformedFilterPattern( @@ -1002,7 +1064,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) val statementsToAdd = (dateValStartStatementOption.toSeq :+ dateValEndStatement).filterNot(statement => - generatedDateStatements.contains(statement)) // dateValStartStatement may be used as ORDER BY statement + generatedDateStatements.contains(statement) + ) // dateValStartStatement may be used as ORDER BY statement generatedDateStatements ++= statementsToAdd TransformedFilterPattern( @@ -1019,8 +1082,9 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) val statementToAdd = - if (dateValStartStatementOption.nonEmpty && !generatedDateStatements.contains( - dateValStartStatementOption.get)) { + if ( + dateValStartStatementOption.nonEmpty && !generatedDateStatements.contains(dateValStartStatementOption.get) + ) { generatedDateStatements += dateValStartStatementOption.get Seq(dateValStartStatementOption.get) } else { @@ -1041,8 +1105,9 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) val statementToAdd = - if (dateValStartStatementOption.nonEmpty && !generatedDateStatements.contains( - dateValStartStatementOption.get)) { + if ( + dateValStartStatementOption.nonEmpty && !generatedDateStatements.contains(dateValStartStatementOption.get) + ) { generatedDateStatements += dateValStartStatementOption.get Seq(dateValStartStatementOption.get) } else { @@ -1063,7 +1128,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) val statementsToAdd = (dateValStartStatementOption.toSeq :+ dateValEndStatement).filterNot(statement => - generatedDateStatements.contains(statement)) // dateValStartStatement may be used as ORDER BY statement + generatedDateStatements.contains(statement) + ) // dateValStartStatement may be used as ORDER BY statement generatedDateStatements ++= statementsToAdd TransformedFilterPattern( @@ -1078,16 +1144,18 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Handles a [[FilterPattern]] containing a query variable. - * - * @param queryVar the query variable. - * @param compareExpression the filter pattern's compare expression. - * @param typeInspectionResult the type inspection results. - * @return a [[TransformedFilterPattern]]. - */ - private def handleQueryVar(queryVar: QueryVariable, - compareExpression: CompareExpression, - typeInspectionResult: GravsearchTypeInspectionResult): TransformedFilterPattern = { + * Handles a [[FilterPattern]] containing a query variable. + * + * @param queryVar the query variable. + * @param compareExpression the filter pattern's compare expression. + * @param typeInspectionResult the type inspection results. + * @return a [[TransformedFilterPattern]]. + */ + private def handleQueryVar( + queryVar: QueryVariable, + compareExpression: CompareExpression, + typeInspectionResult: GravsearchTypeInspectionResult + ): TransformedFilterPattern = { typeInspectionResult.getTypeOfEntity(queryVar) match { case Some(typeInfo) => @@ -1106,7 +1174,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case other => throw GravsearchException( - s"Invalid right argument ${other.toSparql} in comparison (expected a property IRI)") + s"Invalid right argument ${other.toSparql} in comparison (expected a property IRI)" + ) } case nonPropInfo: NonPropertyTypeInfo => @@ -1118,7 +1187,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case _: QueryVariable | _: IriRef => TransformedFilterPattern(Some(compareExpression)) case other => throw GravsearchException( - s"Invalid right argument ${other.toSparql} in comparison (expected a variable or IRI representing a resource)") + s"Invalid right argument ${other.toSparql} in comparison (expected a variable or IRI representing a resource)" + ) } } else if (querySchema == ApiV2Simple) { // The left operand doesn't represent a resource. Is the query using the API v2 simple schema? // Yes. Depending on the value type, transform the given Filter pattern. @@ -1140,7 +1210,10 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, queryVar = queryVar, comparisonOperator = compareExpression.operator, literalValueExpression = compareExpression.rightArg, - xsdType = Set(OntologyConstants.Xsd.Decimal, OntologyConstants.Xsd.Integer), // an integer literal is also valid + xsdType = Set( + OntologyConstants.Xsd.Decimal, + OntologyConstants.Xsd.Integer + ), // an integer literal is also valid valueHasProperty = OntologyConstants.KnoraBase.ValueHasDecimal ) @@ -1187,14 +1260,18 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) case OntologyConstants.KnoraApiV2Simple.Date => - handleDateQueryVar(queryVar = queryVar, - comparisonOperator = compareExpression.operator, - dateValueExpression = compareExpression.rightArg) + handleDateQueryVar( + queryVar = queryVar, + comparisonOperator = compareExpression.operator, + dateValueExpression = compareExpression.rightArg + ) case OntologyConstants.KnoraApiV2Simple.ListNode => - handleListQueryVar(queryVar = queryVar, - comparisonOperator = compareExpression.operator, - literalValueExpression = compareExpression.rightArg) + handleListQueryVar( + queryVar = queryVar, + comparisonOperator = compareExpression.operator, + literalValueExpression = compareExpression.rightArg + ) case other => throw NotImplementedException(s"Value type $other not supported in FilterExpression") } @@ -1210,21 +1287,23 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * - * Handles the use of the SPARQL lang function in a [[FilterPattern]]. - * - * @param langFunctionCall the lang function call to be handled. - * @param compareExpression the filter pattern's compare expression. - * @param typeInspectionResult the type inspection results. - * @return a [[TransformedFilterPattern]]. - */ - private def handleLangFunctionCall(langFunctionCall: LangFunction, - compareExpression: CompareExpression, - typeInspectionResult: GravsearchTypeInspectionResult): TransformedFilterPattern = { + * Handles the use of the SPARQL lang function in a [[FilterPattern]]. + * + * @param langFunctionCall the lang function call to be handled. + * @param compareExpression the filter pattern's compare expression. + * @param typeInspectionResult the type inspection results. + * @return a [[TransformedFilterPattern]]. + */ + private def handleLangFunctionCall( + langFunctionCall: LangFunction, + compareExpression: CompareExpression, + typeInspectionResult: GravsearchTypeInspectionResult + ): TransformedFilterPattern = { if (querySchema == ApiV2Complex) { throw GravsearchException( - s"The lang function is not allowed in a Gravsearch query that uses the API v2 complex schema") + s"The lang function is not allowed in a Gravsearch query that uses the API v2 complex schema" + ) } // make sure that the query variable represents a text value @@ -1248,8 +1327,9 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } // comparison operator must be '=' or '!=' - if (!Set(CompareExpressionOperator.EQUALS, CompareExpressionOperator.NOT_EQUALS).contains( - compareExpression.operator)) + if ( + !Set(CompareExpressionOperator.EQUALS, CompareExpressionOperator.NOT_EQUALS).contains(compareExpression.operator) + ) throw GravsearchException(s"Comparison operator must be '=' or '!=' for use with a 'lang' function call") val langLiteral: XsdLiteral = compareExpression.rightArg match { @@ -1257,25 +1337,33 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case _ => throw GravsearchException( - s"Right argument of comparison statement must be a string literal for use with 'lang' function call") + s"Right argument of comparison statement must be a string literal for use with 'lang' function call" + ) } // Generate a variable name representing the language of the text value val textValHasLanguage: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( langFunctionCall.textValueVar, - OntologyConstants.KnoraBase.ValueHasLanguage) + OntologyConstants.KnoraBase.ValueHasLanguage + ) // Add a statement to assign the literal to a variable, which we'll use in the transformed FILTER expression, // if that statement hasn't been added already. val statementToAddForValueHasLanguage = - if (addGeneratedVariableForValueLiteral(valueVar = langFunctionCall.textValueVar, - generatedVar = textValHasLanguage, - useInOrderBy = false)) { + if ( + addGeneratedVariableForValueLiteral( + valueVar = langFunctionCall.textValueVar, + generatedVar = textValHasLanguage, + useInOrderBy = false + ) + ) { Seq( // connects the value object with the value language code - StatementPattern.makeExplicit(subj = langFunctionCall.textValueVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasLanguage.toSmartIri), - textValHasLanguage) + StatementPattern.makeExplicit( + subj = langFunctionCall.textValueVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasLanguage.toSmartIri), + textValHasLanguage + ) ) } else { Seq.empty[StatementPattern] @@ -1289,16 +1377,16 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Handles the use of the SPARQL regex function in a [[FilterPattern]]. - * - * @param regexFunctionCall the regex function call to be handled. - * @param typeInspectionResult the type inspection results. - * @return a [[TransformedFilterPattern]]. - */ + * Handles the use of the SPARQL regex function in a [[FilterPattern]]. + * + * @param regexFunctionCall the regex function call to be handled. + * @param typeInspectionResult the type inspection results. + * @return a [[TransformedFilterPattern]]. + */ private def handleRegexFunctionCall( - regexFunctionCall: RegexFunction, - typeInspectionResult: GravsearchTypeInspectionResult): TransformedFilterPattern = { - + regexFunctionCall: RegexFunction, + typeInspectionResult: GravsearchTypeInspectionResult + ): TransformedFilterPattern = // If the query uses the API v2 complex schema, leave the function call as it is. if (querySchema == ApiV2Complex) { TransformedFilterPattern(Some(regexFunctionCall)) @@ -1343,7 +1431,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // No, it refers to a TextValue. Generate a variable name representing the string literal. val textValHasString: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = regexQueryVar, - propertyIri = OntologyConstants.KnoraBase.ValueHasString) + propertyIri = OntologyConstants.KnoraBase.ValueHasString + ) // Add a statement to assign the literal to a variable, which we'll use in the transformed FILTER expression, // if that statement hasn't been added already. @@ -1351,9 +1440,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, if (addGeneratedVariableForValueLiteral(regexQueryVar, textValHasString)) { Seq( // connects the value object with the value literal - StatementPattern.makeExplicit(subj = regexQueryVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), - textValHasString) + StatementPattern.makeExplicit( + subj = regexQueryVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), + textValHasString + ) ) } else { Seq.empty[StatementPattern] @@ -1366,24 +1457,26 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } } - } /** - * Handles the function `knora-api:matchText` in the simple schema. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleMatchTextFunctionInSimpleSchema(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Handles the function `knora-api:matchText` in the simple schema. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleMatchTextFunctionInSimpleSchema( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri if (querySchema == ApiV2Complex) { throw GravsearchException( - s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the complex schema; use ${OntologyConstants.KnoraApiV2Complex.MatchTextFunction.toSmartIri.toSparql} instead") + s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the complex schema; use ${OntologyConstants.KnoraApiV2Complex.MatchTextFunction.toSmartIri.toSparql} instead" + ) } // The match function must be the top-level expression, otherwise boolean logic won't work properly. @@ -1414,15 +1507,19 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val textValHasString: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = textValueVar, - propertyIri = OntologyConstants.KnoraBase.ValueHasString) + propertyIri = OntologyConstants.KnoraBase.ValueHasString + ) // Generate an optional statement to assign the literal to a variable, which we can pass to LuceneQueryPattern, // if that statement hasn't been added already. val valueHasStringStatement = if (addGeneratedVariableForValueLiteral(textValueVar, textValHasString)) { Some( - StatementPattern.makeExplicit(subj = textValueVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), - textValHasString)) + StatementPattern.makeExplicit( + subj = textValueVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), + textValHasString + ) + ) } else { None } @@ -1441,26 +1538,30 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, obj = textValHasString, queryString = searchTerms, literalStatement = valueHasStringStatement - )) + ) + ) ) } /** - * Handles the function `knora-api:matchText` in the complex schema. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleMatchTextFunctionInComplexSchema(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Handles the function `knora-api:matchText` in the complex schema. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleMatchTextFunctionInComplexSchema( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri if (querySchema == ApiV2Simple) { throw GravsearchException( - s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema; use ${OntologyConstants.KnoraApiV2Simple.MatchTextFunction.toSmartIri.toSparql} instead") + s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema; use ${OntologyConstants.KnoraApiV2Simple.MatchTextFunction.toSmartIri.toSparql} instead" + ) } // The match function must be the top-level expression, otherwise boolean logic won't work properly. @@ -1491,15 +1592,19 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val textValHasString: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = textValueVar, - propertyIri = OntologyConstants.KnoraBase.ValueHasString) + propertyIri = OntologyConstants.KnoraBase.ValueHasString + ) // Generate an optional statement to assign the literal to a variable, which we can pass to LuceneQueryPattern, // if that statement hasn't been added already. val valueHasStringStatement = if (addGeneratedVariableForValueLiteral(textValueVar, textValHasString)) { Some( - StatementPattern.makeExplicit(subj = textValueVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), - textValHasString)) + StatementPattern.makeExplicit( + subj = textValueVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), + textValHasString + ) + ) } else { None } @@ -1518,26 +1623,30 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, obj = textValHasString, queryString = searchTerms, literalStatement = valueHasStringStatement - )) + ) + ) ) } /** - * Handles the function `knora-api:matchTextInStandoff`. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleMatchTextInStandoffFunction(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Handles the function `knora-api:matchTextInStandoff`. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleMatchTextInStandoffFunction( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri if (querySchema == ApiV2Simple) { throw GravsearchException( - s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema") + s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema" + ) } if (!isTopLevel) { @@ -1569,14 +1678,18 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val textValHasString: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = textValueVar, - propertyIri = OntologyConstants.KnoraBase.ValueHasString) + propertyIri = OntologyConstants.KnoraBase.ValueHasString + ) // Generate a statement to assign the literal to a variable, if that statement hasn't been added already. val valueHasStringStatement = if (addGeneratedVariableForValueLiteral(textValueVar, textValHasString)) { Some( - StatementPattern.makeExplicit(subj = textValueVar, - pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), - textValHasString)) + StatementPattern.makeExplicit( + subj = textValueVar, + pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), + textValHasString + ) + ) } else { None } @@ -1595,7 +1708,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, obj = textValHasString, queryString = searchTerms, literalStatement = None // We have to add this statement ourselves, so LuceneQueryPattern doesn't need to. - )) + ) + ) // Generate query patterns to assign the text in the standoff tag to a variable, if we // haven't done so already. @@ -1611,13 +1725,17 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, Seq( // ?standoffTag knora-base:standoffTagHasStart ?standoffTag__start . - StatementPattern.makeExplicit(standoffTagVar, - IriRef(OntologyConstants.KnoraBase.StandoffTagHasStart.toSmartIri), - startVariable), + StatementPattern.makeExplicit( + standoffTagVar, + IriRef(OntologyConstants.KnoraBase.StandoffTagHasStart.toSmartIri), + startVariable + ), // ?standoffTag knora-base:standoffTagHasEnd ?standoffTag__end . - StatementPattern.makeExplicit(standoffTagVar, - IriRef(OntologyConstants.KnoraBase.StandoffTagHasEnd.toSmartIri), - endVariable) + StatementPattern.makeExplicit( + standoffTagVar, + IriRef(OntologyConstants.KnoraBase.StandoffTagHasEnd.toSmartIri), + endVariable + ) ) } else { Seq.empty[QueryPattern] @@ -1656,21 +1774,24 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Checks that the query is in the simple schema, then calls `handleMatchLabelFunction`. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleMatchLabelFunctionInSimpleSchema(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Checks that the query is in the simple schema, then calls `handleMatchLabelFunction`. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleMatchLabelFunctionInSimpleSchema( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri if (querySchema == ApiV2Complex) { throw GravsearchException( - s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the complex schema; use ${OntologyConstants.KnoraApiV2Complex.MatchLabelFunction.toSmartIri.toSparql} instead") + s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the complex schema; use ${OntologyConstants.KnoraApiV2Complex.MatchLabelFunction.toSmartIri.toSparql} instead" + ) } handleMatchLabelFunction( @@ -1681,21 +1802,24 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Checks that the query is in the complex schema, then calls `handleMatchLabelFunction`. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleMatchLabelFunctionInComplexSchema(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Checks that the query is in the complex schema, then calls `handleMatchLabelFunction`. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleMatchLabelFunctionInComplexSchema( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri if (querySchema == ApiV2Simple) { throw GravsearchException( - s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema; use ${OntologyConstants.KnoraApiV2Simple.MatchLabelFunction.toSmartIri.toSparql} instead") + s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema; use ${OntologyConstants.KnoraApiV2Simple.MatchLabelFunction.toSmartIri.toSparql} instead" + ) } handleMatchLabelFunction( @@ -1706,16 +1830,18 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Handles the function `knora-api:matchLabel` in either schema. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleMatchLabelFunction(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Handles the function `knora-api:matchLabel` in either schema. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleMatchLabelFunction( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri // The matchLabel function must be the top-level expression, otherwise boolean logic won't work properly. @@ -1743,12 +1869,14 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, val rdfsLabelVar: QueryVariable = SparqlTransformer.createUniqueVariableNameFromEntityAndProperty( base = resourceVar, - propertyIri = OntologyConstants.Rdfs.Label) + propertyIri = OntologyConstants.Rdfs.Label + ) val rdfsLabelStatement = if (addGeneratedVariableForValueLiteral(resourceVar, rdfsLabelVar)) { Some( StatementPattern - .makeExplicit(subj = resourceVar, pred = IriRef(OntologyConstants.Rdfs.Label.toSmartIri), rdfsLabelVar)) + .makeExplicit(subj = resourceVar, pred = IriRef(OntologyConstants.Rdfs.Label.toSmartIri), rdfsLabelVar) + ) } else { None } @@ -1766,26 +1894,30 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, obj = rdfsLabelVar, queryString = luceneQueryString, literalStatement = rdfsLabelStatement - )) + ) + ) ) } /** - * Handles the function `knora-api:StandoffLink`. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleStandoffLinkFunction(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Handles the function `knora-api:StandoffLink`. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleStandoffLinkFunction( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri if (querySchema == ApiV2Simple) { throw GravsearchException( - s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema") + s"Function ${functionIri.toSparql} cannot be used in a Gravsearch query written in the simple schema" + ) } if (!isTopLevel) { @@ -1821,7 +1953,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, case _ => throw GravsearchException( - s"The second argument of ${functionIri.toSparql} must represent a knora-api:StandoffTag") + s"The second argument of ${functionIri.toSparql} must represent a knora-api:StandoffTag" + ) } val linkTargetEntity = functionCallExpression.args(2) match { @@ -1844,9 +1977,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, // Generate statements linking the source resource and the standoff tag to the target resource. val linkStatements = Seq( StatementPattern.makeExplicit(subj = linkSourceEntity, pred = hasStandoffLinkToIriRef, obj = linkTargetEntity), - StatementPattern.makeInferred(subj = standoffTagVar, - pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri), - obj = linkTargetEntity) + StatementPattern.makeInferred( + subj = standoffTagVar, + pred = IriRef(OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri), + obj = linkTargetEntity + ) ) // Generate statements matching the link value that describes the standoff link between the source and target resources. @@ -1863,17 +1998,18 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * - * Handles a Gravsearch-specific function call in a [[FilterPattern]]. - * - * @param functionCallExpression the function call to be handled. - * @param typeInspectionResult the type inspection results. - * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. - * @return a [[TransformedFilterPattern]]. - */ - private def handleKnoraFunctionCall(functionCallExpression: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Handles a Gravsearch-specific function call in a [[FilterPattern]]. + * + * @param functionCallExpression the function call to be handled. + * @param typeInspectionResult the type inspection results. + * @param isTopLevel if `true`, this is the top-level expression in the `FILTER`. + * @return a [[TransformedFilterPattern]]. + */ + private def handleKnoraFunctionCall( + functionCallExpression: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { val functionIri: SmartIri = functionCallExpression.functionIri.iri // Get a Scala function that implements the Gravsearch function. @@ -1901,20 +2037,22 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, } /** - * Handles the `knora-api:toSimpleDate` function in a comparison. - * - * @param filterCompare the comparison expression. - * @param functionCallExpr the function call expression. - * @param typeInspectionResult the type inspection result. - * @return a [[TransformedFilterPattern]]. - */ + * Handles the `knora-api:toSimpleDate` function in a comparison. + * + * @param filterCompare the comparison expression. + * @param functionCallExpr the function call expression. + * @param typeInspectionResult the type inspection result. + * @return a [[TransformedFilterPattern]]. + */ private def handleToSimpleDateFunction( - filterCompare: CompareExpression, - functionCallExpr: FunctionCallExpression, - typeInspectionResult: GravsearchTypeInspectionResult): TransformedFilterPattern = { + filterCompare: CompareExpression, + functionCallExpr: FunctionCallExpression, + typeInspectionResult: GravsearchTypeInspectionResult + ): TransformedFilterPattern = { if (querySchema == ApiV2Simple) { throw GravsearchException( - s"Function ${functionCallExpr.functionIri.toSparql} cannot be used in a query written in the simple schema") + s"Function ${functionCallExpr.functionIri.toSparql} cannot be used in a query written in the simple schema" + ) } if (functionCallExpr.args.size != 1) @@ -1925,32 +2063,40 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, typeInspectionResult.getTypeOfEntity(dateBaseVar) match { case Some(nonPropInfo: NonPropertyTypeInfo) => - if (!(nonPropInfo.isStandoffTagType || nonPropInfo.typeIri.toString == OntologyConstants.KnoraApiV2Complex.DateValue)) { + if ( + !(nonPropInfo.isStandoffTagType || nonPropInfo.typeIri.toString == OntologyConstants.KnoraApiV2Complex.DateValue) + ) { throw GravsearchException( - s"${dateBaseVar.toSparql} must represent a knora-api:DateValue or a knora-api:StandoffDateTag") + s"${dateBaseVar.toSparql} must represent a knora-api:DateValue or a knora-api:StandoffDateTag" + ) } case _ => throw GravsearchException( - s"${dateBaseVar.toSparql} must represent a knora-api:DateValue or a knora-api:StandoffDateTag") + s"${dateBaseVar.toSparql} must represent a knora-api:DateValue or a knora-api:StandoffDateTag" + ) } - handleDateQueryVar(queryVar = dateBaseVar, - comparisonOperator = filterCompare.operator, - dateValueExpression = filterCompare.rightArg) + handleDateQueryVar( + queryVar = dateBaseVar, + comparisonOperator = filterCompare.operator, + dateValueExpression = filterCompare.rightArg + ) } /** - * Transforms a Filter expression provided in the input query (knora-api simple) into a knora-base compliant Filter expression. - * - * @param filterExpression the `FILTER` expression to be transformed. - * @param typeInspectionResult the results of type inspection. - * @param isTopLevel `true` if this is the top-level expression in the filter. - * @return a [[TransformedFilterPattern]]. - */ - protected def transformFilterPattern(filterExpression: Expression, - typeInspectionResult: GravsearchTypeInspectionResult, - isTopLevel: Boolean): TransformedFilterPattern = { + * Transforms a Filter expression provided in the input query (knora-api simple) into a knora-base compliant Filter expression. + * + * @param filterExpression the `FILTER` expression to be transformed. + * @param typeInspectionResult the results of type inspection. + * @param isTopLevel `true` if this is the top-level expression in the filter. + * @return a [[TransformedFilterPattern]]. + */ + protected def transformFilterPattern( + filterExpression: Expression, + typeInspectionResult: GravsearchTypeInspectionResult, + isTopLevel: Boolean + ): TransformedFilterPattern = { // Are we looking at a top-level filter expression in a UNION block? if (isTopLevel && inUnionBlock) { // Yes. Make sure that all the variables used in the FILTER have already been bound in the same block. @@ -1958,7 +2104,8 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, if (unboundVariables.nonEmpty) { throw GravsearchException( - s"One or more variables used in a filter have not been bound in the same UNION block: ${unboundVariables.map(_.toSparql).mkString(", ")}") + s"One or more variables used in a filter have not been bound in the same UNION block: ${unboundVariables.map(_.toSparql).mkString(", ")}" + ) } } @@ -1969,9 +2116,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, filterCompare.leftArg match { case queryVar: QueryVariable => - handleQueryVar(queryVar = queryVar, - compareExpression = filterCompare, - typeInspectionResult = typeInspectionResult) + handleQueryVar( + queryVar = queryVar, + compareExpression = filterCompare, + typeInspectionResult = typeInspectionResult + ) case functionCallExpr: FunctionCallExpression if functionCallExpr.functionIri.iri.toString == OntologyConstants.KnoraApiV2Complex.ToSimpleDateFunction => @@ -1982,9 +2131,11 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, ) case lang: LangFunction => - handleLangFunctionCall(langFunctionCall = lang, - compareExpression = filterCompare, - typeInspectionResult = typeInspectionResult) + handleLangFunctionCall( + langFunctionCall = lang, + compareExpression = filterCompare, + typeInspectionResult = typeInspectionResult + ) case other => throw GravsearchException(s"Invalid left argument ${other.toSparql} in comparison") } @@ -2000,11 +2151,14 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, TransformedFilterPattern( Some( OrExpression( - filterExpressionLeft.expression.getOrElse(throw DataConversionException( - "Expression was expected from previous FILTER conversion, but None given")), - filterExpressionRight.expression.getOrElse(throw DataConversionException( - "Expression was expected from previous FILTER conversion, but None given")) - )), + filterExpressionLeft.expression.getOrElse( + throw DataConversionException("Expression was expected from previous FILTER conversion, but None given") + ), + filterExpressionRight.expression.getOrElse( + throw DataConversionException("Expression was expected from previous FILTER conversion, but None given") + ) + ) + ), filterExpressionLeft.additionalPatterns ++ filterExpressionRight.additionalPatterns ) @@ -2019,11 +2173,14 @@ abstract class AbstractPrequeryGenerator(constructClause: ConstructClause, TransformedFilterPattern( Some( AndExpression( - filterExpressionLeft.expression.getOrElse(throw DataConversionException( - "Expression was expected from previous FILTER conversion, but None given")), - filterExpressionRight.expression.getOrElse(throw DataConversionException( - "Expression was expected from previous FILTER conversion, but None given")) - )), + filterExpressionLeft.expression.getOrElse( + throw DataConversionException("Expression was expected from previous FILTER conversion, but None given") + ), + filterExpressionRight.expression.getOrElse( + throw DataConversionException("Expression was expected from previous FILTER conversion, but None given") + ) + ) + ), filterExpressionLeft.additionalPatterns ++ filterExpressionRight.additionalPatterns ) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/GravsearchQueryOptimisationFactory.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/GravsearchQueryOptimisationFactory.scala index a9c9a4ec72..8bef8ee50b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/GravsearchQueryOptimisationFactory.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/GravsearchQueryOptimisationFactory.scala @@ -33,78 +33,84 @@ import scalax.collection.Graph import scalax.collection.GraphEdge.DiHyperEdge /** - * Represents optimisation algorithms that transform Gravsearch input queries. - * - * @param typeInspectionResult the type inspection result. - * @param querySchema the query schema. - */ -abstract class GravsearchQueryOptimisationFeature(protected val typeInspectionResult: GravsearchTypeInspectionResult, - protected val querySchema: ApiV2Schema) { + * Represents optimisation algorithms that transform Gravsearch input queries. + * + * @param typeInspectionResult the type inspection result. + * @param querySchema the query schema. + */ +abstract class GravsearchQueryOptimisationFeature( + protected val typeInspectionResult: GravsearchTypeInspectionResult, + protected val querySchema: ApiV2Schema +) { /** - * Performs the optimisation. - * - * @param patterns the query patterns. - * @return the optimised query patterns. - */ + * Performs the optimisation. + * + * @param patterns the query patterns. + * @return the optimised query patterns. + */ def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] } /** - * A feature factory that constructs Gravsearch query optimisation algorithms. - */ + * A feature factory that constructs Gravsearch query optimisation algorithms. + */ object GravsearchQueryOptimisationFactory extends FeatureFactory { /** - * Returns a [[GravsearchQueryOptimisationFeature]] implementing one or more optimisations, depending - * on the feature factory configuration. - * - * @param typeInspectionResult the type inspection result. - * @param querySchema the query schema. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[GravsearchQueryOptimisationFeature]] implementing one or more optimisations. - */ + * Returns a [[GravsearchQueryOptimisationFeature]] implementing one or more optimisations, depending + * on the feature factory configuration. + * + * @param typeInspectionResult the type inspection result. + * @param querySchema the query schema. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[GravsearchQueryOptimisationFeature]] implementing one or more optimisations. + */ def getGravsearchQueryOptimisationFeature( + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig + ): GravsearchQueryOptimisationFeature = + new GravsearchQueryOptimisationFeature( typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig): GravsearchQueryOptimisationFeature = { - new GravsearchQueryOptimisationFeature(typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema) { - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + querySchema: ApiV2Schema + ) { + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = if (featureFactoryConfig.getToggle("gravsearch-dependency-optimisation").isEnabled) { new ReorderPatternsByDependencyOptimisationFeature(typeInspectionResult, querySchema).optimiseQueryPatterns( new RemoveEntitiesInferredFromPropertyOptimisationFeature(typeInspectionResult, querySchema) .optimiseQueryPatterns( new RemoveRedundantKnoraApiResourceOptimisationFeature(typeInspectionResult, querySchema) - .optimiseQueryPatterns(patterns)) + .optimiseQueryPatterns(patterns) + ) ) } else { new RemoveEntitiesInferredFromPropertyOptimisationFeature(typeInspectionResult, querySchema) .optimiseQueryPatterns( new RemoveRedundantKnoraApiResourceOptimisationFeature(typeInspectionResult, querySchema) - .optimiseQueryPatterns(patterns)) + .optimiseQueryPatterns(patterns) + ) } - } } - } } /** - * Removes a statement with rdf:type knora-api:Resource if there is another rdf:type statement with the same subject - * and a different type. - * - * @param typeInspectionResult the type inspection result. - * @param querySchema the query schema. - */ -class RemoveRedundantKnoraApiResourceOptimisationFeature(typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema) - extends GravsearchQueryOptimisationFeature(typeInspectionResult, querySchema) + * Removes a statement with rdf:type knora-api:Resource if there is another rdf:type statement with the same subject + * and a different type. + * + * @param typeInspectionResult the type inspection result. + * @param querySchema the query schema. + */ +class RemoveRedundantKnoraApiResourceOptimisationFeature( + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema +) extends GravsearchQueryOptimisationFeature(typeInspectionResult, querySchema) with Feature { /** - * If the specified statement has rdf:type with an IRI as object, returns that IRI, otherwise None. - */ - private def getObjOfRdfType(statementPattern: StatementPattern): Option[SmartIri] = { + * If the specified statement has rdf:type with an IRI as object, returns that IRI, otherwise None. + */ + private def getObjOfRdfType(statementPattern: StatementPattern): Option[SmartIri] = statementPattern.pred match { case predicateIriRef: IriRef => if (predicateIriRef.iri.toString == OntologyConstants.Rdf.Type) { @@ -118,28 +124,26 @@ class RemoveRedundantKnoraApiResourceOptimisationFeature(typeInspectionResult: G case _ => None } - } override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { // Make a Set of subjects that have rdf:type statements whose objects are not knora-api:Resource. val rdfTypesBySubj: Set[Entity] = patterns - .foldLeft(Set.empty[Entity]) { - case (acc, queryPattern: QueryPattern) => - queryPattern match { - case statementPattern: StatementPattern => - getObjOfRdfType(statementPattern) match { - case Some(typeIri) => - if (!OntologyConstants.KnoraApi.KnoraApiV2ResourceIris.contains(typeIri.toString)) { - acc + statementPattern.subj - } else { - acc - } - - case None => acc - } + .foldLeft(Set.empty[Entity]) { case (acc, queryPattern: QueryPattern) => + queryPattern match { + case statementPattern: StatementPattern => + getObjOfRdfType(statementPattern) match { + case Some(typeIri) => + if (!OntologyConstants.KnoraApi.KnoraApiV2ResourceIris.contains(typeIri.toString)) { + acc + statementPattern.subj + } else { + acc + } + + case None => acc + } - case _ => acc - } + case _ => acc + } } patterns.filterNot { @@ -160,41 +164,40 @@ class RemoveRedundantKnoraApiResourceOptimisationFeature(typeInspectionResult: G } /** - * Optimises a query by removing `rdf:type` statements that are known to be redundant. A redundant - * `rdf:type` statement gives the type of a variable whose type is already restricted by its - * use with a property that can only be used with that type (unless the property - * statement is in an `OPTIONAL` block). - */ -class RemoveEntitiesInferredFromPropertyOptimisationFeature(typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema) - extends GravsearchQueryOptimisationFeature(typeInspectionResult, querySchema) + * Optimises a query by removing `rdf:type` statements that are known to be redundant. A redundant + * `rdf:type` statement gives the type of a variable whose type is already restricted by its + * use with a property that can only be used with that type (unless the property + * statement is in an `OPTIONAL` block). + */ +class RemoveEntitiesInferredFromPropertyOptimisationFeature( + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema +) extends GravsearchQueryOptimisationFeature(typeInspectionResult, querySchema) with Feature { /** - * Performs the optimisation. - * - * @param patterns the query patterns. - * @return the optimised query patterns. - */ + * Performs the optimisation. + * + * @param patterns the query patterns. + * @return the optimised query patterns. + */ override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { // Collect all entities which are used as subject or object of an OptionalPattern. - val optionalEntities: Seq[TypeableEntity] = patterns - .collect { - case optionalPattern: OptionalPattern => optionalPattern - } - .flatMap { - case optionalPattern: OptionalPattern => - optionalPattern.patterns.flatMap { - case pattern: StatementPattern => - GravsearchTypeInspectionUtil.maybeTypeableEntity(pattern.subj) ++ GravsearchTypeInspectionUtil - .maybeTypeableEntity(pattern.obj) - - case _ => None - } + val optionalEntities: Seq[TypeableEntity] = patterns.collect { case optionalPattern: OptionalPattern => + optionalPattern + }.flatMap { + case optionalPattern: OptionalPattern => + optionalPattern.patterns.flatMap { + case pattern: StatementPattern => + GravsearchTypeInspectionUtil.maybeTypeableEntity(pattern.subj) ++ GravsearchTypeInspectionUtil + .maybeTypeableEntity(pattern.obj) - case _ => None - } + case _ => None + } + + case _ => None + } // Remove statements whose predicate is rdf:type, type of subject is inferred from a property, // and the subject is not in optionalEntities. @@ -248,21 +251,22 @@ class RemoveEntitiesInferredFromPropertyOptimisationFeature(typeInspectionResult } /** - * Optimises query patterns by reordering them on the basis of dependencies between subjects and objects. - */ -class ReorderPatternsByDependencyOptimisationFeature(typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema) - extends GravsearchQueryOptimisationFeature(typeInspectionResult, querySchema) + * Optimises query patterns by reordering them on the basis of dependencies between subjects and objects. + */ +class ReorderPatternsByDependencyOptimisationFeature( + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema +) extends GravsearchQueryOptimisationFeature(typeInspectionResult, querySchema) with Feature { /** - * Converts a sequence of query patterns into DAG representing dependencies between - * the subjects and objects used, performs a topological sort of the graph, and reorders - * the query patterns according to the topological order. - * - * @param statementPatterns the query patterns to be reordered. - * @return the reordered query patterns. - */ + * Converts a sequence of query patterns into DAG representing dependencies between + * the subjects and objects used, performs a topological sort of the graph, and reorders + * the query patterns according to the topological order. + * + * @param statementPatterns the query patterns to be reordered. + * @return the reordered query patterns. + */ private def createAndSortGraph(statementPatterns: Seq[StatementPattern]): Seq[QueryPattern] = { @scala.annotation.tailrec def makeGraphWithoutCycles(graphComponents: Seq[(String, String)]): Graph[String, DiHyperEdge] = { @@ -301,29 +305,27 @@ class ReorderPatternsByDependencyOptimisationFeature(typeInspectionResult: Gravs } /** - * Finds topological orders that don't end with an object of rdf:type. - * - * @param orders the orders to be filtered. - * @param statementPatterns the statement patterns that the orders are based on. - * @return the filtered topological orders. - */ + * Finds topological orders that don't end with an object of rdf:type. + * + * @param orders the orders to be filtered. + * @param statementPatterns the statement patterns that the orders are based on. + * @return the filtered topological orders. + */ def findOrdersNotEndingWithObjectOfRdfType( - orders: Set[Vector[Graph[String, DiHyperEdge]#NodeT]], - statementPatterns: Seq[StatementPattern]): Set[Vector[Graph[String, DiHyperEdge]#NodeT]] = { + orders: Set[Vector[Graph[String, DiHyperEdge]#NodeT]], + statementPatterns: Seq[StatementPattern] + ): Set[Vector[Graph[String, DiHyperEdge]#NodeT]] = { type NodeT = Graph[String, DiHyperEdge]#NodeT // Find the nodes that are objects of rdf:type in the statement patterns. - val nodesThatAreObjectsOfRdfType: Set[String] = statementPatterns - .filter { statementPattern => - statementPattern.pred match { - case iriRef: IriRef => iriRef.iri.toString == OntologyConstants.Rdf.Type - case _ => false - } - } - .map { statementPattern => - statementPattern.obj.toSparql + val nodesThatAreObjectsOfRdfType: Set[String] = statementPatterns.filter { statementPattern => + statementPattern.pred match { + case iriRef: IriRef => iriRef.iri.toString == OntologyConstants.Rdf.Type + case _ => false } - .toSet + }.map { statementPattern => + statementPattern.obj.toSparql + }.toSet // Filter out the topological orders that end with any of those nodes. orders.filterNot { order: Vector[NodeT] => @@ -332,20 +334,22 @@ class ReorderPatternsByDependencyOptimisationFeature(typeInspectionResult: Gravs } /** - * Tries to find the best topological order for the graph, by finding all possible topological orders - * and eliminating those whose last node is the object of rdf:type. - * - * @param graph the graph to be ordered. - * @param statementPatterns the statement patterns that were used to create the graph. - * @return a topological order. - */ - def findBestTopologicalOrder(graph: Graph[String, DiHyperEdge], - statementPatterns: Seq[StatementPattern]): Vector[Graph[String, DiHyperEdge]#NodeT] = { + * Tries to find the best topological order for the graph, by finding all possible topological orders + * and eliminating those whose last node is the object of rdf:type. + * + * @param graph the graph to be ordered. + * @param statementPatterns the statement patterns that were used to create the graph. + * @return a topological order. + */ + def findBestTopologicalOrder( + graph: Graph[String, DiHyperEdge], + statementPatterns: Seq[StatementPattern] + ): Vector[Graph[String, DiHyperEdge]#NodeT] = { type NodeT = Graph[String, DiHyperEdge]#NodeT /** - * An ordering for sorting topological orders. - */ + * An ordering for sorting topological orders. + */ object TopologicalOrderOrdering extends Ordering[Vector[NodeT]] { private def orderToString(order: Vector[NodeT]) = order.map(_.value).mkString("|") @@ -385,8 +389,10 @@ class ReorderPatternsByDependencyOptimisationFeature(typeInspectionResult: Gravs } } - def sortStatementPatterns(createdGraph: Graph[String, DiHyperEdge], - statementPatterns: Seq[StatementPattern]): Seq[QueryPattern] = { + def sortStatementPatterns( + createdGraph: Graph[String, DiHyperEdge], + statementPatterns: Seq[StatementPattern] + ): Seq[QueryPattern] = { type NodeT = Graph[String, DiHyperEdge]#NodeT // Try to find the best topological order for the graph. @@ -413,11 +419,11 @@ class ReorderPatternsByDependencyOptimisationFeature(typeInspectionResult: Gravs } /** - * Performs the optimisation. - * - * @param patterns the query patterns. - * @return the optimised query patterns. - */ + * Performs the optimisation. + * + * @param patterns the query patterns. + * @return the optimised query patterns. + */ override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { // Separate the statement patterns from the other patterns. val (statementPatterns: Seq[StatementPattern], otherPatterns: Seq[QueryPattern]) = diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformer.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformer.scala index f5bce6532c..8d5d6ed7ce 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformer.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformer.scala @@ -25,37 +25,38 @@ import org.knora.webapi.messages.util.search._ import org.knora.webapi.messages.util.search.gravsearch.types.GravsearchTypeInspectionResult /** - * Transforms a preprocessed CONSTRUCT query into a SELECT query that returns only the IRIs and sort order of the main resources that matched - * the search criteria. This query will be used to get resource IRIs for a single page of results. These IRIs will be included in a CONSTRUCT - * query to get the actual results for the page. - * - * @param constructClause the CONSTRUCT clause from the input query. - * @param typeInspectionResult the result of type inspection of the input query. - * @param querySchema the ontology schema used in the input query. - * @param featureFactoryConfig the feature factory configuration. - */ -class NonTriplestoreSpecificGravsearchToCountPrequeryTransformer(constructClause: ConstructClause, - typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig) - extends AbstractPrequeryGenerator(constructClause = constructClause, - typeInspectionResult = typeInspectionResult, - querySchema = querySchema) + * Transforms a preprocessed CONSTRUCT query into a SELECT query that returns only the IRIs and sort order of the main resources that matched + * the search criteria. This query will be used to get resource IRIs for a single page of results. These IRIs will be included in a CONSTRUCT + * query to get the actual results for the page. + * + * @param constructClause the CONSTRUCT clause from the input query. + * @param typeInspectionResult the result of type inspection of the input query. + * @param querySchema the ontology schema used in the input query. + * @param featureFactoryConfig the feature factory configuration. + */ +class NonTriplestoreSpecificGravsearchToCountPrequeryTransformer( + constructClause: ConstructClause, + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig +) extends AbstractPrequeryGenerator( + constructClause = constructClause, + typeInspectionResult = typeInspectionResult, + querySchema = querySchema + ) with ConstructToSelectTransformer { - def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[QueryPattern] = { - + def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[QueryPattern] = // Include any statements needed to meet the user's search criteria, but not statements that would be needed for permission checking or // other information about the matching resources or values. - processStatementPatternFromWhereClause( statementPattern = statementPattern, inputOrderBy = inputOrderBy ) - } - def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = { val filterExpression: TransformedFilterPattern = transformFilterPattern(filterPattern.expression, typeInspectionResult = typeInspectionResult, isTopLevel = true) @@ -68,34 +69,31 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformer(constructClause } - def getSelectColumns: Seq[SelectQueryColumn] = { + def getSelectColumns: Seq[SelectQueryColumn] = // return count aggregation function for main variable Seq(Count(inputVariable = mainResourceVariable, distinct = true, outputVariableName = "count")) - } - def getGroupBy(orderByCriteria: TransformedOrderBy): Seq[QueryVariable] = { + def getGroupBy(orderByCriteria: TransformedOrderBy): Seq[QueryVariable] = Seq.empty[QueryVariable] - } - def getOrderBy(inputOrderBy: Seq[OrderCriterion]): TransformedOrderBy = { + def getOrderBy(inputOrderBy: Seq[OrderCriterion]): TransformedOrderBy = // empty by default TransformedOrderBy() - } def getLimit: Int = 1 // one row expected for count query - def getOffset(inputQueryOffset: Long, limit: Int): Long = { + def getOffset(inputQueryOffset: Long, limit: Int): Long = // count queries do not consider offsets since there is only one result row 0 - } - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = GravsearchQueryOptimisationFactory - .getGravsearchQueryOptimisationFeature(typeInspectionResult = typeInspectionResult, - querySchema = querySchema, - featureFactoryConfig = featureFactoryConfig) + .getGravsearchQueryOptimisationFeature( + typeInspectionResult = typeInspectionResult, + querySchema = querySchema, + featureFactoryConfig = featureFactoryConfig + ) .optimiseQueryPatterns(patterns) - } override def transformLuceneQueryPattern(luceneQueryPattern: LuceneQueryPattern): Seq[QueryPattern] = Seq(luceneQueryPattern) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformer.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformer.scala index c752e8864c..d66285da5e 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformer.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformer.scala @@ -32,22 +32,23 @@ import org.knora.webapi.messages.util.search.gravsearch.types.{ import org.knora.webapi.settings.KnoraSettingsImpl /** - * Transforms a preprocessed CONSTRUCT query into a SELECT query that returns only the IRIs and sort order of the main resources that matched - * the search criteria and are requested by client in the input query's WHERE clause. This query will be used to get resource IRIs for a single - * page of results. These IRIs will be included in a CONSTRUCT query to get the actual results for the page. - * - * @param constructClause the CONSTRUCT clause from the input query. - * @param typeInspectionResult the result of type inspection of the input query. - * @param querySchema the ontology schema used in the input query. - * @param settings application settings. - * @param featureFactoryConfig the feature factory configuration. - */ -class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: ConstructClause, - typeInspectionResult: GravsearchTypeInspectionResult, - querySchema: ApiV2Schema, - settings: KnoraSettingsImpl, - featureFactoryConfig: FeatureFactoryConfig) - extends AbstractPrequeryGenerator( + * Transforms a preprocessed CONSTRUCT query into a SELECT query that returns only the IRIs and sort order of the main resources that matched + * the search criteria and are requested by client in the input query's WHERE clause. This query will be used to get resource IRIs for a single + * page of results. These IRIs will be included in a CONSTRUCT query to get the actual results for the page. + * + * @param constructClause the CONSTRUCT clause from the input query. + * @param typeInspectionResult the result of type inspection of the input query. + * @param querySchema the ontology schema used in the input query. + * @param settings application settings. + * @param featureFactoryConfig the feature factory configuration. + */ +class NonTriplestoreSpecificGravsearchToPrequeryTransformer( + constructClause: ConstructClause, + typeInspectionResult: GravsearchTypeInspectionResult, + querySchema: ApiV2Schema, + settings: KnoraSettingsImpl, + featureFactoryConfig: FeatureFactoryConfig +) extends AbstractPrequeryGenerator( constructClause = constructClause, typeInspectionResult = typeInspectionResult, querySchema = querySchema @@ -57,29 +58,29 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con import AbstractPrequeryGenerator._ /** - * Transforms a [[org.knora.webapi.messages.util.search.StatementPattern]] in a WHERE clause into zero or more query patterns. - * - * @param statementPattern the statement to be transformed. - * @param inputOrderBy the ORDER BY clause in the input query. - * @return the result of the transformation. - */ - override def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[QueryPattern] = { + * Transforms a [[org.knora.webapi.messages.util.search.StatementPattern]] in a WHERE clause into zero or more query patterns. + * + * @param statementPattern the statement to be transformed. + * @param inputOrderBy the ORDER BY clause in the input query. + * @return the result of the transformation. + */ + override def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[QueryPattern] = // Include any statements needed to meet the user's search criteria, but not statements that would be needed for permission checking or // other information about the matching resources or values. - processStatementPatternFromWhereClause( statementPattern = statementPattern, inputOrderBy = inputOrderBy ) - } /** - * Transforms a [[org.knora.webapi.messages.util.search.FilterPattern]] in a WHERE clause into zero or more statement patterns. - * - * @param filterPattern the filter to be transformed. - * @return the result of the transformation. - */ + * Transforms a [[org.knora.webapi.messages.util.search.FilterPattern]] in a WHERE clause into zero or more statement patterns. + * + * @param filterPattern the filter to be transformed. + * @return the result of the transformation. + */ override def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = { val filterExpression: TransformedFilterPattern = @@ -94,13 +95,13 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con } /** - * Determines whether an entity has a property type that meets the specified condition. - * - * @param entity the entity. - * @param condition the condition. - * @return `true` if the variable has a property type and the condition is met. - */ - private def entityHasPropertyType(entity: Entity, condition: PropertyTypeInfo => Boolean): Boolean = { + * Determines whether an entity has a property type that meets the specified condition. + * + * @param entity the entity. + * @param condition the condition. + * @return `true` if the variable has a property type and the condition is met. + */ + private def entityHasPropertyType(entity: Entity, condition: PropertyTypeInfo => Boolean): Boolean = GravsearchTypeInspectionUtil.maybeTypeableEntity(entity) match { case Some(typeableEntity) => typeInspectionResult.entities.get(typeableEntity) match { @@ -111,16 +112,15 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con case None => false } - } /** - * Determines whether an entity has a non-property type that meets the specified condition. - * - * @param entity the entity. - * @param condition the condition. - * @return `true` if the variable has a non-property type and the condition is met. - */ - private def entityHasNonPropertyType(entity: Entity, condition: NonPropertyTypeInfo => Boolean): Boolean = { + * Determines whether an entity has a non-property type that meets the specified condition. + * + * @param entity the entity. + * @param condition the condition. + * @return `true` if the variable has a non-property type and the condition is met. + */ + private def entityHasNonPropertyType(entity: Entity, condition: NonPropertyTypeInfo => Boolean): Boolean = GravsearchTypeInspectionUtil.maybeTypeableEntity(entity) match { case Some(typeableEntity) => typeInspectionResult.entities.get(typeableEntity) match { @@ -131,24 +131,22 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con case None => false } - } /** - * Checks that an [[Entity]] is a [[QueryVariable]]. - * - * @param entity the entity. - * @return the entity as a [[QueryVariable]]. - */ - private def entityToQueryVariable(entity: Entity): QueryVariable = { + * Checks that an [[Entity]] is a [[QueryVariable]]. + * + * @param entity the entity. + * @return the entity as a [[QueryVariable]]. + */ + private def entityToQueryVariable(entity: Entity): QueryVariable = entity match { case queryVariable: QueryVariable => queryVariable case other => throw GravsearchException(s"Expected a variable in CONSTRUCT clause, but found ${other.toSparql}") } - } /** - * All the variables used in the Gravsearch CONSTRUCT clause. - */ + * All the variables used in the Gravsearch CONSTRUCT clause. + */ private val variablesInConstruct: Set[QueryVariable] = constructClause.statements.flatMap { statementPattern: StatementPattern => Seq(statementPattern.subj, statementPattern.obj).flatMap { @@ -158,32 +156,27 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con }.toSet /** - * The variables representing values in the CONSTRUCT clause, grouped by resource. - */ + * The variables representing values in the CONSTRUCT clause, grouped by resource. + */ private val valueVariablesPerResourceInConstruct: Map[Entity, Set[QueryVariable]] = - constructClause.statements - .filter { statementPattern: StatementPattern => - // Find statements in which the subject is a resource, the predicate is a value property, - // and the object is a value. - entityHasNonPropertyType(entity = statementPattern.subj, condition = _.isResourceType) && - entityHasPropertyType(entity = statementPattern.pred, condition = _.objectIsValueType) && - entityHasNonPropertyType(entity = statementPattern.obj, condition = _.isValueType) - } - .map { statementPattern => - statementPattern.subj -> entityToQueryVariable(statementPattern.obj) - } - .groupBy { - case (resourceEntity, _) => resourceEntity - } - .map { - case (resourceEntity, resourceValueTuples) => - // Simplify the result of groupBy by replacing each tuple with its second element. - resourceEntity -> resourceValueTuples.map(_._2).toSet - } + constructClause.statements.filter { statementPattern: StatementPattern => + // Find statements in which the subject is a resource, the predicate is a value property, + // and the object is a value. + entityHasNonPropertyType(entity = statementPattern.subj, condition = _.isResourceType) && + entityHasPropertyType(entity = statementPattern.pred, condition = _.objectIsValueType) && + entityHasNonPropertyType(entity = statementPattern.obj, condition = _.isValueType) + }.map { statementPattern => + statementPattern.subj -> entityToQueryVariable(statementPattern.obj) + }.groupBy { case (resourceEntity, _) => + resourceEntity + }.map { case (resourceEntity, resourceValueTuples) => + // Simplify the result of groupBy by replacing each tuple with its second element. + resourceEntity -> resourceValueTuples.map(_._2).toSet + } /** - * The variables representing resources in the CONSTRUCT clause. - */ + * The variables representing resources in the CONSTRUCT clause. + */ private val resourceVariablesInConstruct: Set[QueryVariable] = variablesInConstruct.filter { queryVariable => entityHasNonPropertyType(entity = queryVariable, condition = _.isResourceType) } @@ -193,8 +186,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con private val valueVariablesInConstruct: Set[QueryVariable] = valueVariablesPerResourceInConstruct.values.flatten.toSet - private val invalidVariablesInConstruct - : Set[QueryVariable] = variablesInConstruct -- valueVariablesInConstruct -- resourceVariablesInConstruct + private val invalidVariablesInConstruct: Set[QueryVariable] = + variablesInConstruct -- valueVariablesInConstruct -- resourceVariablesInConstruct if (invalidVariablesInConstruct.nonEmpty) { val invalidVariablesWithTypes: Set[String] = invalidVariablesInConstruct.map { queryVariable => @@ -213,48 +206,47 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con throw GravsearchException( s"One or more variables in the Gravsearch CONSTRUCT clause have unknown or invalid types: ${invalidVariablesWithTypes - .mkString(", ")}") + .mkString(", ")}" + ) } /** - * The [[GroupConcat]] expressions generated for values in the prequery, grouped by resource entity. - */ + * The [[GroupConcat]] expressions generated for values in the prequery, grouped by resource entity. + */ private val valueGroupConcatsPerResource: Map[Entity, Set[GroupConcat]] = { // Generate variables representing link values and group them by containing resource entity. - val linkValueVariablesPerResourceGeneratedForConstruct: Map[Entity, Set[QueryVariable]] = constructClause.statements - .filter { statementPattern: StatementPattern => + val linkValueVariablesPerResourceGeneratedForConstruct: Map[Entity, Set[QueryVariable]] = + constructClause.statements.filter { statementPattern: StatementPattern => // Find statements in which the subject is a resource, the predicate is a link property, // and the object is a resource. entityHasNonPropertyType(entity = statementPattern.subj, condition = _.isResourceType) && entityHasPropertyType(entity = statementPattern.pred, condition = _.objectIsResourceType) && entityHasNonPropertyType(entity = statementPattern.obj, condition = _.isResourceType) - } - .map { statementPattern => + }.map { statementPattern => // For each of those statements, make a variable representing a link value. statementPattern.subj -> SparqlTransformer.createUniqueVariableFromStatementForLinkValue( baseStatement = statementPattern ) - } - .groupBy { - case (resourceEntity, _) => resourceEntity - } - .map { - case (resourceEntity, resourceValueTuples) => - // Simplify the result of groupBy by replacing each tuple with its second element. - resourceEntity -> resourceValueTuples.map(_._2).toSet + }.groupBy { case (resourceEntity, _) => + resourceEntity + }.map { case (resourceEntity, resourceValueTuples) => + // Simplify the result of groupBy by replacing each tuple with its second element. + resourceEntity -> resourceValueTuples.map(_._2).toSet } // Make a GroupConcat for each value variable. (valueVariablesPerResourceInConstruct.keySet ++ linkValueVariablesPerResourceGeneratedForConstruct.keySet).map { resourceEntity: Entity => - val valueVariables: Set[QueryVariable] = valueVariablesPerResourceInConstruct.getOrElse(resourceEntity, - Set.empty) ++ - linkValueVariablesPerResourceGeneratedForConstruct.getOrElse(resourceEntity, Set.empty) + val valueVariables: Set[QueryVariable] = + valueVariablesPerResourceInConstruct.getOrElse(resourceEntity, Set.empty) ++ + linkValueVariablesPerResourceGeneratedForConstruct.getOrElse(resourceEntity, Set.empty) val groupConcats: Set[GroupConcat] = valueVariables.map { valueObjVar: QueryVariable => - GroupConcat(inputVariable = valueObjVar, - separator = groupConcatSeparator, - outputVariableName = valueObjVar.variableName + groupConcatVariableSuffix) + GroupConcat( + inputVariable = valueObjVar, + separator = groupConcatSeparator, + outputVariableName = valueObjVar.variableName + groupConcatVariableSuffix + ) } resourceEntity -> groupConcats @@ -262,29 +254,28 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con }.toMap /** - * The variables used in [[GroupConcat]] expressions in the prequery, grouped by resource entity. - */ + * The variables used in [[GroupConcat]] expressions in the prequery, grouped by resource entity. + */ private val valueGroupConcatVariablesPerResource: Map[Entity, Set[QueryVariable]] = { - valueGroupConcatsPerResource.map { - case (resourceEntity: Entity, groupConcats: Set[GroupConcat]) => - resourceEntity -> groupConcats.map(_.outputVariable) + valueGroupConcatsPerResource.map { case (resourceEntity: Entity, groupConcats: Set[GroupConcat]) => + resourceEntity -> groupConcats.map(_.outputVariable) } } /** - * A GROUP_CONCAT expression for each value variable. - */ + * A GROUP_CONCAT expression for each value variable. + */ private val valueObjectGroupConcat: Set[GroupConcat] = valueGroupConcatsPerResource.values.flatten.toSet /** - * Variables representing dependent resources in the CONSTRUCT clause. - */ - private val dependentResourceVariablesInConstruct - : Set[QueryVariable] = resourceVariablesInConstruct - mainResourceVariable + * Variables representing dependent resources in the CONSTRUCT clause. + */ + private val dependentResourceVariablesInConstruct: Set[QueryVariable] = + resourceVariablesInConstruct - mainResourceVariable /** - * A GROUP_CONCAT expression for each dependent resource variable. - */ + * A GROUP_CONCAT expression for each dependent resource variable. + */ private val dependentResourceGroupConcat: Set[GroupConcat] = dependentResourceVariablesInConstruct.map { dependentResVar: QueryVariable => GroupConcat( @@ -295,58 +286,56 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con } /** - * The variable names used in the GROUP_CONCAT expressions for dependent resources. - */ + * The variable names used in the GROUP_CONCAT expressions for dependent resources. + */ // TODO only used by GravsearchMainQueryGenerator and not clear why val dependentResourceVariablesGroupConcat: Set[QueryVariable] = dependentResourceGroupConcat.map(_.outputVariable) /** - * The variable names used in the GROUP_CONCAT expressions for values. - */ + * The variable names used in the GROUP_CONCAT expressions for values. + */ // TODO same as above val valueObjectVariablesGroupConcat: Set[QueryVariable] = valueGroupConcatVariablesPerResource.values.flatten.toSet /** - * Returns the columns to be specified in the SELECT query. - */ - override def getSelectColumns: Seq[SelectQueryColumn] = { + * Returns the columns to be specified in the SELECT query. + */ + override def getSelectColumns: Seq[SelectQueryColumn] = Seq(mainResourceVariable) ++ dependentResourceGroupConcat ++ valueObjectGroupConcat - } /** - * Returns the variables that were used in [[GroupConcat]] expressions in the prequery to represent values - * that were mentioned in the CONSTRUCT clause of the input query, for the given entity representing a resource. - */ - def getValueGroupConcatVariablesForResource(resourceEntity: Entity): Set[QueryVariable] = { + * Returns the variables that were used in [[GroupConcat]] expressions in the prequery to represent values + * that were mentioned in the CONSTRUCT clause of the input query, for the given entity representing a resource. + */ + def getValueGroupConcatVariablesForResource(resourceEntity: Entity): Set[QueryVariable] = valueGroupConcatVariablesPerResource.getOrElse(resourceEntity, Set.empty) - } /** - * Returns the criteria, if any, that should be used in the ORDER BY clause of the SELECT query. This method will be called - * by [[org.knora.webapi.messages.util.search.QueryTraverser]] after the whole input query has been traversed. - * - * @return the ORDER BY criteria, if any. - */ + * Returns the criteria, if any, that should be used in the ORDER BY clause of the SELECT query. This method will be called + * by [[org.knora.webapi.messages.util.search.QueryTraverser]] after the whole input query has been traversed. + * + * @return the ORDER BY criteria, if any. + */ override def getOrderBy(inputOrderBy: Seq[OrderCriterion]): TransformedOrderBy = { - val transformedOrderBy = inputOrderBy.foldLeft(TransformedOrderBy()) { - case (acc, criterion) => - // A unique variable for the literal value of this value object should already have been created + val transformedOrderBy = inputOrderBy.foldLeft(TransformedOrderBy()) { case (acc, criterion) => + // A unique variable for the literal value of this value object should already have been created - getGeneratedVariableForValueLiteralInOrderBy(criterion.queryVariable) match { - case Some(generatedVariable) => - // Yes. Use the already generated variable in the ORDER BY. - acc.copy( - orderBy = acc.orderBy :+ OrderCriterion(queryVariable = generatedVariable, - isAscending = criterion.isAscending) - ) + getGeneratedVariableForValueLiteralInOrderBy(criterion.queryVariable) match { + case Some(generatedVariable) => + // Yes. Use the already generated variable in the ORDER BY. + acc.copy( + orderBy = + acc.orderBy :+ OrderCriterion(queryVariable = generatedVariable, isAscending = criterion.isAscending) + ) - case None => - // No. - throw GravsearchException( - s"Variable ${criterion.queryVariable.toSparql} is used in ORDER by, but is not bound at the top level of the WHERE clause") + case None => + // No. + throw GravsearchException( + s"Variable ${criterion.queryVariable.toSparql} is used in ORDER by, but is not bound at the top level of the WHERE clause" + ) - } + } } // main resource variable as order by criterion @@ -363,34 +352,32 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con } /** - * Creates the GROUP BY statement based on the ORDER BY statement. - * - * @param orderByCriteria the criteria used to sort the query results. They have to be included in the GROUP BY statement, otherwise they are unbound. - * @return a list of variables that the result rows are grouped by. - */ - def getGroupBy(orderByCriteria: TransformedOrderBy): Seq[QueryVariable] = { + * Creates the GROUP BY statement based on the ORDER BY statement. + * + * @param orderByCriteria the criteria used to sort the query results. They have to be included in the GROUP BY statement, otherwise they are unbound. + * @return a list of variables that the result rows are grouped by. + */ + def getGroupBy(orderByCriteria: TransformedOrderBy): Seq[QueryVariable] = // get they query variables form the order by criteria and return them in reverse order: // main resource variable first, followed by other sorting criteria, if any. orderByCriteria.orderBy.map(_.queryVariable).reverse - } /** - * Gets the maximal amount of result rows to be returned by the prequery. - * - * @return the LIMIT, if any. - */ - def getLimit: Int = { + * Gets the maximal amount of result rows to be returned by the prequery. + * + * @return the LIMIT, if any. + */ + def getLimit: Int = // get LIMIT from settings settings.v2ResultsPerPage - } /** - * Gets the OFFSET to be used in the prequery (needed for paging). - * - * @param inputQueryOffset the OFFSET provided in the input query. - * @param limit the maximum amount of result rows to be returned by the prequery. - * @return the OFFSET. - */ + * Gets the OFFSET to be used in the prequery (needed for paging). + * + * @param inputQueryOffset the OFFSET provided in the input query. + * @param limit the maximum amount of result rows to be returned by the prequery. + * @return the OFFSET. + */ def getOffset(inputQueryOffset: Long, limit: Int): Long = { if (inputQueryOffset < 0) throw AssertionException("Negative OFFSET is illegal.") @@ -404,17 +391,18 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformer(constructClause: Con Seq(luceneQueryPattern) /** - * Runs optimisations that take a Gravsearch query as input. An optimisation needs to be run here if - * it uses the type inspection result that refers to the Gravsearch query. - * - * @param patterns the query patterns to be optimised. - * @return the optimised query patterns. - */ - override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = { + * Runs optimisations that take a Gravsearch query as input. An optimisation needs to be run here if + * it uses the type inspection result that refers to the Gravsearch query. + * + * @param patterns the query patterns to be optimised. + * @return the optimised query patterns. + */ + override def optimiseQueryPatterns(patterns: Seq[QueryPattern]): Seq[QueryPattern] = GravsearchQueryOptimisationFactory - .getGravsearchQueryOptimisationFeature(typeInspectionResult = typeInspectionResult, - querySchema = querySchema, - featureFactoryConfig = featureFactoryConfig) + .getGravsearchQueryOptimisationFeature( + typeInspectionResult = typeInspectionResult, + querySchema = querySchema, + featureFactoryConfig = featureFactoryConfig + ) .optimiseQueryPatterns(patterns) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtil.scala index 8e42d468a6..3360b040c8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtil.scala @@ -25,35 +25,35 @@ import scalax.collection.GraphEdge.DiHyperEdge import scala.collection.mutable /** - * A utility for finding all topological orders of a graph. - * Based on [[https://github.com/scala-graph/scala-graph/issues/129#issuecomment-485398400]]. - */ + * A utility for finding all topological orders of a graph. + * Based on [[https://github.com/scala-graph/scala-graph/issues/129#issuecomment-485398400]]. + */ object TopologicalSortUtil { /** - * Finds all possible topological order permutations of a graph. If the graph is cyclical, returns an empty set. - * - * @param graph the graph to be sorted. - * @tparam T the type of the nodes in the graph. - */ + * Finds all possible topological order permutations of a graph. If the graph is cyclical, returns an empty set. + * + * @param graph the graph to be sorted. + * @tparam T the type of the nodes in the graph. + */ def findAllTopologicalOrderPermutations[T](graph: Graph[T, DiHyperEdge]): Set[Vector[Graph[T, DiHyperEdge]#NodeT]] = { type NodeT = Graph[T, DiHyperEdge]#NodeT /** - * Finds all possible topological order permutations of a graph using layer information. This method considers all - * permutations of the topological order regarding the leaf nodes. - * - * First, for each permutation of leaf nodes, find the correct order of parent nodes w.r.t incoming edges. - * For example, consider a graph that its topological order has 3 layers; i.e. layer 0 contains root nodes, and layer 2 contains leaf nodes. - * Permutations of leaf nodes consist the possible topological orders. Iterate over these set of ordered nodes to - * add nodes of lower layers to each set by considering the edges. That means for nodes of each layer, e.g. layer 1, - * find the outgoing edges to layer 2. If there is an edge for node n in layer 1 to node m in layer 2, then add this - * node to the set of order. After adding these nodes that are origins of edges, add the remaining nodes of layer 1 - * to the set of order. Proceed to layer 0 and add nodes of it to the order in the same manner. - * - * @param layeredOrder the topological order of the graph with layer information i.e. (layer number, layer nodes). - * @return a list of all permutations of topological order. - */ + * Finds all possible topological order permutations of a graph using layer information. This method considers all + * permutations of the topological order regarding the leaf nodes. + * + * First, for each permutation of leaf nodes, find the correct order of parent nodes w.r.t incoming edges. + * For example, consider a graph that its topological order has 3 layers; i.e. layer 0 contains root nodes, and layer 2 contains leaf nodes. + * Permutations of leaf nodes consist the possible topological orders. Iterate over these set of ordered nodes to + * add nodes of lower layers to each set by considering the edges. That means for nodes of each layer, e.g. layer 1, + * find the outgoing edges to layer 2. If there is an edge for node n in layer 1 to node m in layer 2, then add this + * node to the set of order. After adding these nodes that are origins of edges, add the remaining nodes of layer 1 + * to the set of order. Proceed to layer 0 and add nodes of it to the order in the same manner. + * + * @param layeredOrder the topological order of the graph with layer information i.e. (layer number, layer nodes). + * @return a list of all permutations of topological order. + */ def findPermutations(layeredOrder: graph.LayeredTopologicalOrder[NodeT]): List[Vector[NodeT]] = { val lastLayerNodes: Vector[NodeT] = layeredOrder.last._2.toVector val allLowerLayers: Iterable[(Int, Iterable[NodeT])] = layeredOrder.dropRight(1) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/AnnotationReadingGravsearchTypeInspector.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/AnnotationReadingGravsearchTypeInspector.scala index c2b296066c..b18afa7ede 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/AnnotationReadingGravsearchTypeInspector.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/AnnotationReadingGravsearchTypeInspector.scala @@ -33,46 +33,50 @@ import org.knora.webapi.messages.util.search.gravsearch.types.GravsearchTypeInsp import scala.concurrent.Future /** - * A [[GravsearchTypeInspector]] that relies on Gravsearch type annotations. There are two kinds of type annotations: - * - * 1. For a variable or IRI representing a resource or value, a type annotation is a triple whose subject is the variable - * or IRI, whose predicate is `rdf:type`, and whose object is `knora-api:Resource`, another `knora-api` type - * such as `knora-api:date`, or an XSD type such as `xsd:integer`. - * 2. For a variable or IRI representing a property, a type annotation is a triple whose subject is the variable or - * property IRI, whose predicate is `knora-api:objectType`, and whose object is an IRI representing the type - * of object that is required by the property. - */ -class AnnotationReadingGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspector], - responderData: ResponderData) - extends GravsearchTypeInspector(nextInspector = nextInspector, responderData = responderData) { + * A [[GravsearchTypeInspector]] that relies on Gravsearch type annotations. There are two kinds of type annotations: + * + * 1. For a variable or IRI representing a resource or value, a type annotation is a triple whose subject is the variable + * or IRI, whose predicate is `rdf:type`, and whose object is `knora-api:Resource`, another `knora-api` type + * such as `knora-api:date`, or an XSD type such as `xsd:integer`. + * 2. For a variable or IRI representing a property, a type annotation is a triple whose subject is the variable or + * property IRI, whose predicate is `knora-api:objectType`, and whose object is an IRI representing the type + * of object that is required by the property. + */ +class AnnotationReadingGravsearchTypeInspector( + nextInspector: Option[GravsearchTypeInspector], + responderData: ResponderData +) extends GravsearchTypeInspector(nextInspector = nextInspector, responderData = responderData) { /** - * Represents a Gravsearch type annotation. - * - * @param typeableEntity the entity whose type was annotated. - * @param annotationProp the annotation property. - * @param typeIri the type IRI that was given in the annotation. - * @param isResource the given type IRI is that of a resource. - * @param isValue the given type IRI is that of a value. - */ + * Represents a Gravsearch type annotation. + * + * @param typeableEntity the entity whose type was annotated. + * @param annotationProp the annotation property. + * @param typeIri the type IRI that was given in the annotation. + * @param isResource the given type IRI is that of a resource. + * @param isValue the given type IRI is that of a value. + */ private case class GravsearchTypeAnnotation( - typeableEntity: TypeableEntity, - annotationProp: TypeAnnotationProperty, - typeIri: SmartIri, - isResource: Boolean = false, - isValue: Boolean = false + typeableEntity: TypeableEntity, + annotationProp: TypeAnnotationProperty, + typeIri: SmartIri, + isResource: Boolean = false, + isValue: Boolean = false ) - override def inspectTypes(previousResult: IntermediateTypeInspectionResult, - whereClause: WhereClause, - requestingUser: UserADM): Future[IntermediateTypeInspectionResult] = { + override def inspectTypes( + previousResult: IntermediateTypeInspectionResult, + whereClause: WhereClause, + requestingUser: UserADM + ): Future[IntermediateTypeInspectionResult] = for { // Get all the type annotations. typeAnnotations: Seq[GravsearchTypeAnnotation] <- Future { QueryTraverser.visitWherePatterns( patterns = whereClause.patterns, whereVisitor = new AnnotationCollectingWhereVisitor( - whereClause.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema"))), + whereClause.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema")) + ), initialAcc = Vector.empty[GravsearchTypeAnnotation] ) } @@ -88,18 +92,24 @@ class AnnotationReadingGravsearchTypeInspector(nextInspector: Option[GravsearchT GravsearchTypeInspectionUtil.GravsearchValueTypeIris.contains(typeAnnotation.typeIri.toString) acc.addTypes( typeAnnotation.typeableEntity, - Set(NonPropertyTypeInfo(typeAnnotation.typeIri, isResourceType = isResource, isValueType = isValue))) + Set(NonPropertyTypeInfo(typeAnnotation.typeIri, isResourceType = isResource, isValueType = isValue)) + ) case TypeAnnotationProperties.ObjectType => val isResource = OntologyConstants.KnoraApi.KnoraApiV2ResourceIris.contains(typeAnnotation.typeIri.toString) val isValue = GravsearchTypeInspectionUtil.GravsearchValueTypeIris.contains(typeAnnotation.typeIri.toString) - acc.addTypes(typeAnnotation.typeableEntity, - Set( - PropertyTypeInfo(typeAnnotation.typeIri, - objectIsResourceType = isResource, - objectIsValueType = isValue))) + acc.addTypes( + typeAnnotation.typeableEntity, + Set( + PropertyTypeInfo( + typeAnnotation.typeIri, + objectIsResourceType = isResource, + objectIsValueType = isValue + ) + ) + ) } } @@ -110,35 +120,39 @@ class AnnotationReadingGravsearchTypeInspector(nextInspector: Option[GravsearchT requestingUser = requestingUser ) } yield lastResult - } /** - * A [[WhereVisitor]] that collects type annotations. - */ + * A [[WhereVisitor]] that collects type annotations. + */ private class AnnotationCollectingWhereVisitor(querySchema: ApiV2Schema) extends WhereVisitor[Vector[GravsearchTypeAnnotation]] { - override def visitStatementInWhere(statementPattern: StatementPattern, - acc: Vector[GravsearchTypeAnnotation]): Vector[GravsearchTypeAnnotation] = { + override def visitStatementInWhere( + statementPattern: StatementPattern, + acc: Vector[GravsearchTypeAnnotation] + ): Vector[GravsearchTypeAnnotation] = if (GravsearchTypeInspectionUtil.canBeAnnotationStatement(statementPattern)) { acc :+ annotationStatementToAnnotation(statementPattern, querySchema) } else { acc } - } - override def visitFilter(filterPattern: FilterPattern, - acc: Vector[GravsearchTypeAnnotation]): Vector[GravsearchTypeAnnotation] = acc + override def visitFilter( + filterPattern: FilterPattern, + acc: Vector[GravsearchTypeAnnotation] + ): Vector[GravsearchTypeAnnotation] = acc } /** - * Given a statement pattern that is known to represent a Gravsearch type annotation, converts it to - * a [[GravsearchTypeAnnotation]]. - * - * @param statementPattern the statement pattern. - * @return an [[GravsearchTypeAnnotation]]. - */ - private def annotationStatementToAnnotation(statementPattern: StatementPattern, - querySchema: ApiV2Schema): GravsearchTypeAnnotation = { + * Given a statement pattern that is known to represent a Gravsearch type annotation, converts it to + * a [[GravsearchTypeAnnotation]]. + * + * @param statementPattern the statement pattern. + * @return an [[GravsearchTypeAnnotation]]. + */ + private def annotationStatementToAnnotation( + statementPattern: StatementPattern, + querySchema: ApiV2Schema + ): GravsearchTypeAnnotation = { val typeableEntity: TypeableEntity = GravsearchTypeInspectionUtil.toTypeableEntity(statementPattern.subj) val annotationPropIri: SmartIri = statementPattern.pred match { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionResult.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionResult.scala index 38215e2b00..eb1727d2f9 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionResult.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionResult.scala @@ -23,28 +23,29 @@ import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.util.search.{Entity, IriRef, QueryVariable} /** - * Represents the type information that was found concerning a Gravsearch entity. - */ + * Represents the type information that was found concerning a Gravsearch entity. + */ sealed trait GravsearchEntityTypeInfo /** - * Represents type information about a property. - * - * @param objectTypeIri an IRI representing the type of the objects of the property. - * @param objectIsResourceType `true` if the property's object type is a resource type. Property is a link. - * @param objectIsValueType `true` if the property's object type is a value type. Property is not a link. - * @param objectIsStandoffTagType `true` if the property's object type is a standoff tag type. Property is not a link. - */ -case class PropertyTypeInfo(objectTypeIri: SmartIri, - objectIsResourceType: Boolean = false, - objectIsValueType: Boolean = false, - objectIsStandoffTagType: Boolean = false) - extends GravsearchEntityTypeInfo { + * Represents type information about a property. + * + * @param objectTypeIri an IRI representing the type of the objects of the property. + * @param objectIsResourceType `true` if the property's object type is a resource type. Property is a link. + * @param objectIsValueType `true` if the property's object type is a value type. Property is not a link. + * @param objectIsStandoffTagType `true` if the property's object type is a standoff tag type. Property is not a link. + */ +case class PropertyTypeInfo( + objectTypeIri: SmartIri, + objectIsResourceType: Boolean = false, + objectIsValueType: Boolean = false, + objectIsStandoffTagType: Boolean = false +) extends GravsearchEntityTypeInfo { override def toString: String = s"knora-api:objectType ${IriRef(objectTypeIri).toSparql}" /** - * Converts this [[PropertyTypeInfo]] to a [[NonPropertyTypeInfo]]. - */ + * Converts this [[PropertyTypeInfo]] to a [[NonPropertyTypeInfo]]. + */ def toNonPropertyTypeInfo: NonPropertyTypeInfo = NonPropertyTypeInfo( typeIri = objectTypeIri, isResourceType = objectIsResourceType, @@ -54,24 +55,25 @@ case class PropertyTypeInfo(objectTypeIri: SmartIri, } /** - * Represents type information about a SPARQL entity that's not a property, meaning that it is either a variable - * or an IRI. - * - * @param typeIri an IRI representing the entity's type. - * @param isResourceType `true` if this is a resource type. - * @param isValueType `true` if this is a value type. - * @param isStandoffTagType `true` if this is a standoff tag type. - */ -case class NonPropertyTypeInfo(typeIri: SmartIri, - isResourceType: Boolean = false, - isValueType: Boolean = false, - isStandoffTagType: Boolean = false) - extends GravsearchEntityTypeInfo { + * Represents type information about a SPARQL entity that's not a property, meaning that it is either a variable + * or an IRI. + * + * @param typeIri an IRI representing the entity's type. + * @param isResourceType `true` if this is a resource type. + * @param isValueType `true` if this is a value type. + * @param isStandoffTagType `true` if this is a standoff tag type. + */ +case class NonPropertyTypeInfo( + typeIri: SmartIri, + isResourceType: Boolean = false, + isValueType: Boolean = false, + isStandoffTagType: Boolean = false +) extends GravsearchEntityTypeInfo { override def toString: String = s"rdf:type ${IriRef(typeIri).toSparql}" /** - * Converts this [[NonPropertyTypeInfo]] to a [[PropertyTypeInfo]]. - */ + * Converts this [[NonPropertyTypeInfo]] to a [[PropertyTypeInfo]]. + */ def toPropertyTypeInfo: PropertyTypeInfo = PropertyTypeInfo( objectTypeIri = typeIri, objectIsResourceType = isResourceType, @@ -81,44 +83,44 @@ case class NonPropertyTypeInfo(typeIri: SmartIri, } /** - * Represents a SPARQL entity that we can get type information about. - */ + * Represents a SPARQL entity that we can get type information about. + */ sealed trait TypeableEntity /** - * Represents a Gravsearch variable. - * - * @param variableName the name of the variable. - */ + * Represents a Gravsearch variable. + * + * @param variableName the name of the variable. + */ case class TypeableVariable(variableName: String) extends TypeableEntity { override def toString: String = QueryVariable(variableName).toSparql } /** - * Represents an IRI that we need type information about. - * - * @param iri the IRI. - */ + * Represents an IRI that we need type information about. + * + * @param iri the IRI. + */ case class TypeableIri(iri: SmartIri) extends TypeableEntity { override def toString: String = IriRef(iri).toSparql } /** - * Represents the result of type inspection. - * - * @param entities a map of Gravsearch entities to the types that were determined for them. - */ + * Represents the result of type inspection. + * + * @param entities a map of Gravsearch entities to the types that were determined for them. + */ case class GravsearchTypeInspectionResult( - entities: Map[TypeableEntity, GravsearchEntityTypeInfo], - entitiesInferredFromProperties: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = Map.empty) { + entities: Map[TypeableEntity, GravsearchEntityTypeInfo], + entitiesInferredFromProperties: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = Map.empty +) { /** - * Given an [[Entity]], returns its type, if the entity is typeable and its type is available. - * - * @param entity the entity whose type is to be checked. - * @return the entity's type, if available. - */ - def getTypeOfEntity(entity: Entity): Option[GravsearchEntityTypeInfo] = { + * Given an [[Entity]], returns its type, if the entity is typeable and its type is available. + * + * @param entity the entity whose type is to be checked. + * @return the entity's type, if available. + */ + def getTypeOfEntity(entity: Entity): Option[GravsearchEntityTypeInfo] = GravsearchTypeInspectionUtil.maybeTypeableEntity(entity).flatMap(typeableEntity => entities.get(typeableEntity)) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionRunner.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionRunner.scala index 3937067b58..e23c85f48d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionRunner.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionRunner.scala @@ -29,11 +29,11 @@ import org.knora.webapi.settings.KnoraDispatchers import scala.concurrent.{ExecutionContext, Future} /** - * Runs Gravsearch type inspection using one or more type inspector implementations. - * - * @param responderData the Knora [[ResponderData]]. - * @param inferTypes if true, use type inference. - */ + * Runs Gravsearch type inspection using one or more type inspector implementations. + * + * @param responderData the Knora [[ResponderData]]. + * @param inferTypes if true, use type inference. + */ class GravsearchTypeInspectionRunner(responderData: ResponderData, inferTypes: Boolean = true) { private implicit val executionContext: ExecutionContext = responderData.system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) @@ -57,13 +57,13 @@ class GravsearchTypeInspectionRunner(responderData: ResponderData, inferTypes: B ) /** - * Given the WHERE clause from a parsed Gravsearch query, returns information about the types found - * in the query. - * - * @param whereClause the Gravsearch WHERE clause. - * @param requestingUser the requesting user. - * @return the result of the type inspection. - */ + * Given the WHERE clause from a parsed Gravsearch query, returns information about the types found + * in the query. + * + * @param whereClause the Gravsearch WHERE clause. + * @param requestingUser the requesting user. + * @return the result of the type inspection. + */ def inspectTypes(whereClause: WhereClause, requestingUser: UserADM): Future[GravsearchTypeInspectionResult] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -90,45 +90,47 @@ class GravsearchTypeInspectionRunner(responderData: ResponderData, inferTypes: B // Are any entities still untyped? untypedEntities: Set[TypeableEntity] = lastResult.untypedEntities - _ = if (untypedEntities.nonEmpty) { - // Yes. Return an error. - throw GravsearchException( - s"Types could not be determined for one or more entities: ${untypedEntities.mkString(", ")}") - } else { - // No. Are there any entities with multiple types? - val inconsistentEntities: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = - lastResult.entitiesWithInconsistentTypes - - if (inconsistentEntities.nonEmpty) { - // Yes. Return an error. - - val inconsistentStr: String = inconsistentEntities - .map { - case (entity, entityTypes) => - s"$entity ${entityTypes.mkString(" ; ")} ." + _ = + if (untypedEntities.nonEmpty) { + // Yes. Return an error. + throw GravsearchException( + s"Types could not be determined for one or more entities: ${untypedEntities.mkString(", ")}" + ) + } else { + // No. Are there any entities with multiple types? + val inconsistentEntities: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = + lastResult.entitiesWithInconsistentTypes + + if (inconsistentEntities.nonEmpty) { + // Yes. Return an error. + + val inconsistentStr: String = inconsistentEntities.map { case (entity, entityTypes) => + s"$entity ${entityTypes.mkString(" ; ")} ." } - .mkString(" ") + .mkString(" ") - throw GravsearchException(s"One or more entities have inconsistent types: $inconsistentStr") + throw GravsearchException(s"One or more entities have inconsistent types: $inconsistentStr") + } } - } } yield lastResult.toFinalResult } /** - * A [[WhereVisitor]] that collects typeable entities from a Gravsearch WHERE clause. - */ + * A [[WhereVisitor]] that collects typeable entities from a Gravsearch WHERE clause. + */ private class TypeableEntityCollectingWhereVisitor extends WhereVisitor[Set[TypeableEntity]] { /** - * Collects typeable entities from a statement. - * - * @param statementPattern the pattern to be visited. - * @param acc the accumulator. - * @return the accumulator. - */ - override def visitStatementInWhere(statementPattern: StatementPattern, - acc: Set[TypeableEntity]): Set[TypeableEntity] = { + * Collects typeable entities from a statement. + * + * @param statementPattern the pattern to be visited. + * @param acc the accumulator. + * @return the accumulator. + */ + override def visitStatementInWhere( + statementPattern: StatementPattern, + acc: Set[TypeableEntity] + ): Set[TypeableEntity] = statementPattern.pred match { case iriRef: IriRef if iriRef.iri.toString == OntologyConstants.Rdf.Type => // If the predicate is rdf:type, only the subject can be typeable. @@ -137,29 +139,28 @@ class GravsearchTypeInspectionRunner(responderData: ResponderData, inferTypes: B case _ => // Otherwise, the subject, the predicate, and the object could all be typeable. acc ++ GravsearchTypeInspectionUtil.toTypeableEntities( - Seq(statementPattern.subj, statementPattern.pred, statementPattern.obj)) + Seq(statementPattern.subj, statementPattern.pred, statementPattern.obj) + ) } - } /** - * Collects typeable entities from a `FILTER`. - * - * @param filterPattern the pattern to be visited. - * @param acc the accumulator. - * @return the accumulator. - */ - override def visitFilter(filterPattern: FilterPattern, acc: Set[TypeableEntity]): Set[TypeableEntity] = { + * Collects typeable entities from a `FILTER`. + * + * @param filterPattern the pattern to be visited. + * @param acc the accumulator. + * @return the accumulator. + */ + override def visitFilter(filterPattern: FilterPattern, acc: Set[TypeableEntity]): Set[TypeableEntity] = visitFilterExpression(filterPattern.expression, acc) - } /** - * Collects typeable entities from a filter expression. - * - * @param filterExpression the filter expression to be visited. - * @param acc the accumulator. - * @return the accumulator. - */ - private def visitFilterExpression(filterExpression: Expression, acc: Set[TypeableEntity]): Set[TypeableEntity] = { + * Collects typeable entities from a filter expression. + * + * @param filterExpression the filter expression to be visited. + * @param acc the accumulator. + * @return the accumulator. + */ + private def visitFilterExpression(filterExpression: Expression, acc: Set[TypeableEntity]): Set[TypeableEntity] = filterExpression match { case compareExpr: CompareExpression => compareExpr match { @@ -196,7 +197,6 @@ class GravsearchTypeInspectionRunner(responderData: ResponderData, inferTypes: B case _ => acc } - } } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionUtil.scala index 0f26748676..7014510d88 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectionUtil.scala @@ -25,28 +25,28 @@ import org.knora.webapi.messages.util.search._ import org.knora.webapi.messages.{OntologyConstants, SmartIri} /** - * Utilities for Gravsearch type inspection. - */ + * Utilities for Gravsearch type inspection. + */ object GravsearchTypeInspectionUtil { /** - * A trait for case objects representing type annotation properties. - */ + * A trait for case objects representing type annotation properties. + */ sealed trait TypeAnnotationProperty /** - * Contains instances of [[TypeAnnotationProperty]]. - */ + * Contains instances of [[TypeAnnotationProperty]]. + */ object TypeAnnotationProperties { /** - * Represents a type annotation that uses `rdf:type`. - */ + * Represents a type annotation that uses `rdf:type`. + */ case object RdfType extends TypeAnnotationProperty /** - * Represents a type annotation that uses `knora-api:objectType` (in the simple or complex schema). - */ + * Represents a type annotation that uses `knora-api:objectType` (in the simple or complex schema). + */ case object ObjectType extends TypeAnnotationProperty private val valueMap = Map( @@ -56,22 +56,21 @@ object GravsearchTypeInspectionUtil { ) /** - * Converts an IRI to a [[TypeAnnotationProperty]]. - * - * @param iri the IRI to be converted. - * @return a [[TypeAnnotationProperty]], or `None` if the IRI does not correspond to a type - * annotation property. - */ - def fromIri(iri: SmartIri): Option[TypeAnnotationProperty] = { + * Converts an IRI to a [[TypeAnnotationProperty]]. + * + * @param iri the IRI to be converted. + * @return a [[TypeAnnotationProperty]], or `None` if the IRI does not correspond to a type + * annotation property. + */ + def fromIri(iri: SmartIri): Option[TypeAnnotationProperty] = valueMap.get(iri.toString) - } val allTypeAnnotationIris: Set[IRI] = valueMap.keySet.map(_.toString) } /** - * The IRIs of value types that Gravsearch type inspectors return. - */ + * The IRIs of value types that Gravsearch type inspectors return. + */ val GravsearchValueTypeIris: Set[IRI] = Set( OntologyConstants.Xsd.Boolean, OntologyConstants.Xsd.String, @@ -103,8 +102,8 @@ object GravsearchTypeInspectionUtil { ) /** - * The IRIs of non-property types that Gravsearch type inspectors return. - */ + * The IRIs of non-property types that Gravsearch type inspectors return. + */ val GravsearchAnnotationTypeIris: Set[IRI] = GravsearchValueTypeIris ++ Set( OntologyConstants.KnoraApiV2Simple.Resource, OntologyConstants.KnoraApiV2Complex.Resource, @@ -113,8 +112,8 @@ object GravsearchTypeInspectionUtil { ) /** - * IRIs that are used to set Gravsearch options. - */ + * IRIs that are used to set Gravsearch options. + */ val GravsearchOptionIris: Set[IRI] = Set( OntologyConstants.KnoraApiV2Simple.GravsearchOptions, OntologyConstants.KnoraApiV2Complex.GravsearchOptions, @@ -123,62 +122,60 @@ object GravsearchTypeInspectionUtil { ) /** - * IRIs that do not need to be annotated to specify their types. - */ + * IRIs that do not need to be annotated to specify their types. + */ val ApiV2NonTypeableIris: Set[IRI] = GravsearchAnnotationTypeIris ++ TypeAnnotationProperties.allTypeAnnotationIris ++ GravsearchOptionIris /** - * Given a Gravsearch entity that is known to need type information, converts it to a [[TypeableEntity]]. - * - * @param entity a Gravsearch entity that is known to need type information. - * @return a [[TypeableEntity]]. - */ - def toTypeableEntity(entity: Entity): TypeableEntity = { + * Given a Gravsearch entity that is known to need type information, converts it to a [[TypeableEntity]]. + * + * @param entity a Gravsearch entity that is known to need type information. + * @return a [[TypeableEntity]]. + */ + def toTypeableEntity(entity: Entity): TypeableEntity = maybeTypeableEntity(entity) match { case Some(typeableEntity) => typeableEntity case None => throw AssertionException(s"Entity cannot be typed: $entity") } - } /** - * Given a Gravsearch entity, converts it to a [[TypeableEntity]] if possible. - * - * @param entity the entity to be converted. - * @return a [[TypeableEntity]], or `None` if the entity does not need type information. - */ - def maybeTypeableEntity(entity: Entity): Option[TypeableEntity] = { + * Given a Gravsearch entity, converts it to a [[TypeableEntity]] if possible. + * + * @param entity the entity to be converted. + * @return a [[TypeableEntity]], or `None` if the entity does not need type information. + */ + def maybeTypeableEntity(entity: Entity): Option[TypeableEntity] = entity match { case QueryVariable(variableName) => Some(TypeableVariable(variableName)) case IriRef(iri, _) if !ApiV2NonTypeableIris.contains(iri.toString) => Some(TypeableIri(iri)) case _ => None } - } /** - * Given a sequence of entities, finds the ones that need type information and returns them as - * [[TypeableEntity]] objects. - * - * @param entities the entities to be checked. - * @return a sequence of typeable entities. - */ - def toTypeableEntities(entities: Seq[Entity]): Set[TypeableEntity] = { + * Given a sequence of entities, finds the ones that need type information and returns them as + * [[TypeableEntity]] objects. + * + * @param entities the entities to be checked. + * @return a sequence of typeable entities. + */ + def toTypeableEntities(entities: Seq[Entity]): Set[TypeableEntity] = entities.flatMap(entity => maybeTypeableEntity(entity)).toSet - } /** - * A [[WhereTransformer]] for removing Gravsearch type annotations from a WHERE clause. - */ + * A [[WhereTransformer]] for removing Gravsearch type annotations from a WHERE clause. + */ private class AnnotationRemovingWhereTransformer extends WhereTransformer { - override def transformStatementInWhere(statementPattern: StatementPattern, - inputOrderBy: Seq[OrderCriterion]): Seq[QueryPattern] = { + override def transformStatementInWhere( + statementPattern: StatementPattern, + inputOrderBy: Seq[OrderCriterion] + ): Seq[QueryPattern] = if (mustBeAnnotationStatement(statementPattern)) { Seq.empty[QueryPattern] } else { Seq(statementPattern) } - } override def transformFilter(filterPattern: FilterPattern): Seq[QueryPattern] = Seq(filterPattern) @@ -193,12 +190,12 @@ object GravsearchTypeInspectionUtil { } /** - * Removes Gravsearch type annotations from a WHERE clause. - * - * @param whereClause the WHERE clause. - * @return the same WHERE clause, minus any type annotations. - */ - def removeTypeAnnotations(whereClause: WhereClause): WhereClause = { + * Removes Gravsearch type annotations from a WHERE clause. + * + * @param whereClause the WHERE clause. + * @return the same WHERE clause, minus any type annotations. + */ + def removeTypeAnnotations(whereClause: WhereClause): WhereClause = whereClause.copy( patterns = QueryTraverser.transformWherePatterns( patterns = whereClause.patterns, @@ -206,17 +203,16 @@ object GravsearchTypeInspectionUtil { whereTransformer = new AnnotationRemovingWhereTransformer ) ) - } /** - * Determines whether a statement pattern must represent a Gravsearch type annotation. - * - * @param statementPattern the statement pattern. - * @return `true` if the statement pattern must represent a type annotation. - */ + * Determines whether a statement pattern must represent a Gravsearch type annotation. + * + * @param statementPattern the statement pattern. + * @return `true` if the statement pattern must represent a type annotation. + */ def mustBeAnnotationStatement(statementPattern: StatementPattern): Boolean = { // Does the statement have rdf:type knora-api:Resource (which is not necessarily a Gravsearch type annotation)? - def hasRdfTypeKnoraApiResource: Boolean = { + def hasRdfTypeKnoraApiResource: Boolean = statementPattern.pred match { case predIriRef: IriRef => if (predIriRef.iri.toString == OntologyConstants.Rdf.Type) { @@ -232,7 +228,6 @@ object GravsearchTypeInspectionUtil { case _ => false } - } // If the statement can be a type annotation and doesn't have rdf:type knora-api:Resource, return true. // Otherwise, return false. @@ -240,24 +235,23 @@ object GravsearchTypeInspectionUtil { } /** - * Determines whether a statement pattern can represent a Gravsearch type annotation. - * - * @param statementPattern the statement pattern. - * @return `true` if the statement pattern can represent a type annotation. - */ + * Determines whether a statement pattern can represent a Gravsearch type annotation. + * + * @param statementPattern the statement pattern. + * @return `true` if the statement pattern can represent a type annotation. + */ def canBeAnnotationStatement(statementPattern: StatementPattern): Boolean = { /** - * Returns `true` if an entity is an IRI representing a type that is valid for use in a type annotation. - * - * @param entity the entity to be checked. - */ - def isValidTypeInAnnotation(entity: Entity): Boolean = { + * Returns `true` if an entity is an IRI representing a type that is valid for use in a type annotation. + * + * @param entity the entity to be checked. + */ + def isValidTypeInAnnotation(entity: Entity): Boolean = entity match { case IriRef(objIri, _) if GravsearchAnnotationTypeIris.contains(objIri.toString) => true case _ => false } - } statementPattern.pred match { case IriRef(predIri, _) => @@ -275,7 +269,8 @@ object GravsearchTypeInspectionUtil { // for anything other than type annotations in Gravsearch queries. if (!isValidTypeInAnnotation(statementPattern.obj)) { throw GravsearchException( - s"Object of ${statementPattern.pred} is not a valid type: ${statementPattern.obj}") + s"Object of ${statementPattern.pred} is not a valid type: ${statementPattern.obj}" + ) } true diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspector.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspector.scala index 67226ffa26..c748133c2f 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspector.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspector.scala @@ -29,15 +29,17 @@ import org.knora.webapi.settings.{KnoraDispatchers, KnoraSettings, KnoraSettings import scala.concurrent.{ExecutionContext, Future} /** - * An trait whose implementations can get type information from a parsed Gravsearch query in different ways. - * Type inspectors are run in a pipeline. Each inspector tries to determine the types of all the typeable - * entities in the WHERE clause of a Gravsearch query, then runs the next inspector in the pipeline. - * - * @param nextInspector the next type inspector in the pipeline, or `None` if this is the last one. - * @param responderData the Knora responder data. - */ -abstract class GravsearchTypeInspector(protected val nextInspector: Option[GravsearchTypeInspector], - responderData: ResponderData) { + * An trait whose implementations can get type information from a parsed Gravsearch query in different ways. + * Type inspectors are run in a pipeline. Each inspector tries to determine the types of all the typeable + * entities in the WHERE clause of a Gravsearch query, then runs the next inspector in the pipeline. + * + * @param nextInspector the next type inspector in the pipeline, or `None` if this is the last one. + * @param responderData the Knora responder data. + */ +abstract class GravsearchTypeInspector( + protected val nextInspector: Option[GravsearchTypeInspector], + responderData: ResponderData +) { protected val system: ActorSystem = responderData.system protected val settings: KnoraSettingsImpl = KnoraSettings(system) @@ -46,28 +48,32 @@ abstract class GravsearchTypeInspector(protected val nextInspector: Option[Gravs protected implicit val timeout: Timeout = settings.defaultTimeout /** - * Given the WHERE clause from a parsed Gravsearch query, returns information about the types found - * in the query. Each implementation must end by calling `runNextInspector`. - * - * @param previousResult the result of previous type inspection. - * @param whereClause the Gravsearch WHERE clause. - * @param requestingUser the requesting user. - * @return the result returned by the pipeline. - */ - def inspectTypes(previousResult: IntermediateTypeInspectionResult, - whereClause: WhereClause, - requestingUser: UserADM): Future[IntermediateTypeInspectionResult] + * Given the WHERE clause from a parsed Gravsearch query, returns information about the types found + * in the query. Each implementation must end by calling `runNextInspector`. + * + * @param previousResult the result of previous type inspection. + * @param whereClause the Gravsearch WHERE clause. + * @param requestingUser the requesting user. + * @return the result returned by the pipeline. + */ + def inspectTypes( + previousResult: IntermediateTypeInspectionResult, + whereClause: WhereClause, + requestingUser: UserADM + ): Future[IntermediateTypeInspectionResult] /** - * Runs the next type inspector in the pipeline. - * - * @param intermediateResult the intermediate result produced by this type inspector. - * @param whereClause the Gravsearch WHERE clause. - * @return the result returned by the pipeline. - */ - protected def runNextInspector(intermediateResult: IntermediateTypeInspectionResult, - whereClause: WhereClause, - requestingUser: UserADM): Future[IntermediateTypeInspectionResult] = { + * Runs the next type inspector in the pipeline. + * + * @param intermediateResult the intermediate result produced by this type inspector. + * @param whereClause the Gravsearch WHERE clause. + * @return the result returned by the pipeline. + */ + protected def runNextInspector( + intermediateResult: IntermediateTypeInspectionResult, + whereClause: WhereClause, + requestingUser: UserADM + ): Future[IntermediateTypeInspectionResult] = // Is there another inspector in the pipeline? nextInspector match { case Some(next) => @@ -82,5 +88,4 @@ abstract class GravsearchTypeInspector(protected val nextInspector: Option[Gravs // There are no more inspectors. Return the result we have. Future(intermediateResult) } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/InferringGravsearchTypeInspector.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/InferringGravsearchTypeInspector.scala index 75325ec7cf..495124cd8e 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/InferringGravsearchTypeInspector.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/InferringGravsearchTypeInspector.scala @@ -40,8 +40,8 @@ import scala.annotation.tailrec import scala.concurrent.Future /** - * A Gravsearch type inspector that infers types, relying on information from the relevant ontologies. - */ + * A Gravsearch type inspector that infers types, relying on information from the relevant ontologies. + */ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspector], responderData: ResponderData) extends GravsearchTypeInspector(nextInspector = nextInspector, responderData = responderData) { @@ -57,41 +57,45 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe private val MAX_ITERATIONS = 50 /** - * Represents an inference rule in a pipeline. Each rule in the pipeline tries to determine type information - * about a typeable entity, then calls the next rule in the pipeline. - * - * @param nextRule the next rule in the pipeline. - */ + * Represents an inference rule in a pipeline. Each rule in the pipeline tries to determine type information + * about a typeable entity, then calls the next rule in the pipeline. + * + * @param nextRule the next rule in the pipeline. + */ private abstract class InferenceRule(protected val nextRule: Option[InferenceRule]) { /** - * Attempts to determine the type of a single entity. Each implementation must end by calling - * `runNextRule`. - * - * @param entityToType the entity whose type needs to be determined. - * @param intermediateResult the current intermediate type inference result. - * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. - * @param usageIndex an index of entity usage in the query. - * @return the types that the rule inferred for the entity, or an empty set if no type could be determined. - */ - def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult + * Attempts to determine the type of a single entity. Each implementation must end by calling + * `runNextRule`. + * + * @param entityToType the entity whose type needs to be determined. + * @param intermediateResult the current intermediate type inference result. + * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. + * @param usageIndex an index of entity usage in the query. + * @return the types that the rule inferred for the entity, or an empty set if no type could be determined. + */ + def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult /** - * Runs the next rule in the pipeline. - * - * @param entityToType the entity whose type needs to be determined. - * @param intermediateResult this rule's intermediate result. - * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. - * @param usageIndex an index of entity usage in the query. - * @return the types that the rule inferred for the entity, or an empty set if no type could be determined. - */ - protected def runNextRule(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + * Runs the next rule in the pipeline. + * + * @param entityToType the entity whose type needs to be determined. + * @param intermediateResult this rule's intermediate result. + * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. + * @param usageIndex an index of entity usage in the query. + * @return the types that the rule inferred for the entity, or an empty set if no type could be determined. + */ + protected def runNextRule( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = // Is there another rule in the pipeline? nextRule match { case Some(rule) => @@ -107,18 +111,19 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe // No. Return the result we have. intermediateResult } - } } /** - * Infers the type of an entity if there is an `rdf:type` statement about it. - */ + * Infers the type of an entity if there is an `rdf:type` statement about it. + */ private class InferTypeOfSubjectOfRdfTypePredicate(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // Has this entity been used as a subject? val inferredTypes: Set[GravsearchEntityTypeInfo] = usageIndex.subjectIndex.get(entityToType) match { @@ -192,14 +197,16 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers the `knora-api:objectType` of a property if the property's IRI is used as a predicate. - */ + * Infers the `knora-api:objectType` of a property if the property's IRI is used as a predicate. + */ private class InferTypeOfPropertyFromItsIri(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // Is this entity an IRI? val inferredTypes: Set[GravsearchEntityTypeInfo] = entityToType match { @@ -241,15 +248,17 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers an entity's type if the entity is used as the object of a statement and the predicate's - * `knora-api:objectType` is known. - */ + * Infers an entity's type if the entity is used as the object of a statement and the predicate's + * `knora-api:objectType` is known. + */ private class InferTypeOfObjectFromPredicate(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // for standoff links it is necessary to refine the determined types first. TODO: why in this rule and not in all rules? val updatedIntermediateResult: IntermediateTypeInspectionResult = refineDeterminedTypes( intermediateResult = intermediateResult, @@ -257,9 +266,9 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe ) /** - * Performs the inference for this rule on a set of statements. - */ - def inferFromStatements(statements: Set[StatementPattern]): Set[GravsearchEntityTypeInfo] = { + * Performs the inference for this rule on a set of statements. + */ + def inferFromStatements(statements: Set[StatementPattern]): Set[GravsearchEntityTypeInfo] = statements.flatMap { statement => // Is the predicate typeable? GravsearchTypeInspectionUtil.maybeTypeableEntity(statement.pred) match { @@ -286,7 +295,6 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe Set.empty[GravsearchEntityTypeInfo] } } - } // Has this entity been used as the object of one or more statements? usageIndex.objectIndex.get(entityToType) match { @@ -307,17 +315,21 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe // predicates are variables. val typesInferredFromPropertyIris: Set[GravsearchEntityTypeInfo] = inferFromStatements( - statementsWithPropertyIris) + statementsWithPropertyIris + ) val typesInferredFromVariablesAsPredicates: Set[GravsearchEntityTypeInfo] = inferFromStatements( - statementsWithVariablesAsPredicates) + statementsWithVariablesAsPredicates + ) // If any types were inferred from statements whose predicates are IRIs, update the // intermediate type inspection result with that information. val intermediateResultWithTypesInferredFromPropertyIris = if (typesInferredFromPropertyIris.nonEmpty) { - updatedIntermediateResult.addTypes(entityToType, - typesInferredFromPropertyIris, - inferredFromPropertyIri = true) + updatedIntermediateResult.addTypes( + entityToType, + typesInferredFromPropertyIris, + inferredFromPropertyIri = true + ) } else { updatedIntermediateResult } @@ -348,15 +360,17 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers an entity's type if the entity is used as the subject of a statement in which the predicate is a - * property IRI whose `knora-api:subjectType` is known. - */ + * Infers an entity's type if the entity is used as the subject of a statement in which the predicate is a + * property IRI whose `knora-api:subjectType` is known. + */ private class InferTypeOfSubjectFromPredicateIri(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // Has this entity been used as the subject of one or more statements? val inferredTypes: Set[GravsearchEntityTypeInfo] = usageIndex.subjectIndex.get(entityToType) match { @@ -403,14 +417,16 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers the `knora-api:objectType` of a property variable or IRI if it's used with an object whose type is known. - */ + * Infers the `knora-api:objectType` of a property variable or IRI if it's used with an object whose type is known. + */ private class InferTypeOfPredicateFromObject(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // for standoff links it is necessary to refine the types first. TODO: why in this rule and not in all rules? val updatedIntermediateResult: IntermediateTypeInspectionResult = refineDeterminedTypes( intermediateResult = intermediateResult, @@ -489,15 +505,17 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers the types of entities if their type was already determined by examining a FILTER expression when - * constructing the usage index. - */ + * Infers the types of entities if their type was already determined by examining a FILTER expression when + * constructing the usage index. + */ private class InferTypeOfEntityFromKnownTypeInFilter(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // Do we have one or more types for this entity from a FILTER? val typesFromFilters: Set[GravsearchEntityTypeInfo] = usageIndex.typedEntitiesInFilters.get(entityToType) match { case Some(typesFromFilters: Set[SmartIri]) => @@ -506,10 +524,12 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe val isValueType = GravsearchTypeInspectionUtil.GravsearchValueTypeIris.contains(typeFromFilter.toString) val isStandoffTagType = typeFromFilter.toString == OntologyConstants.KnoraApiV2Complex.StandoffTag val isResourceType = !(isValueType || isStandoffTagType) - val inferredType = NonPropertyTypeInfo(typeFromFilter, - isResourceType = isResourceType, - isValueType = isValueType, - isStandoffTagType = isStandoffTagType) + val inferredType = NonPropertyTypeInfo( + typeFromFilter, + isResourceType = isResourceType, + isValueType = isValueType, + isStandoffTagType = isStandoffTagType + ) log.debug("InferTypeOfEntityFromKnownTypeInFilter: {} {} .", entityToType, inferredType) inferredType } @@ -529,14 +549,16 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers a variable's type if it has been compared with a property IRI in a FILTER expression. - */ + * Infers a variable's type if it has been compared with a property IRI in a FILTER expression. + */ private class InferTypeOfVariableFromComparisonWithPropertyIriInFilter(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { val typesFromComparisons: Set[GravsearchEntityTypeInfo] = entityToType match { // Is this entity a variable? @@ -589,14 +611,16 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Infers the type of a variable or IRI that has been compared with another variable or IRI in a FILTER expression. - */ + * Infers the type of a variable or IRI that has been compared with another variable or IRI in a FILTER expression. + */ private class InferTypeOfEntityFromComparisonWithOtherEntityInFilter(nextRule: Option[InferenceRule]) extends InferenceRule(nextRule = nextRule) { - override def infer(entityToType: TypeableEntity, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + override def infer( + entityToType: TypeableEntity, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { // Has this entity been compared with one or more other entities in a FILTER? val typesFromComparisons: Set[GravsearchEntityTypeInfo] = usageIndex.entitiesComparedInFilters.get(entityToType) match { @@ -625,49 +649,51 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Utility functions for type inference rules. - */ + * Utility functions for type inference rules. + */ private object InferenceRuleUtil { /** - * Returns knora-api:Resource in the specified schema. - * - * @param querySchema the ontology schema that the query is written in. - */ - def getResourceTypeIriForSchema(querySchema: ApiV2Schema): SmartIri = { + * Returns knora-api:Resource in the specified schema. + * + * @param querySchema the ontology schema that the query is written in. + */ + def getResourceTypeIriForSchema(querySchema: ApiV2Schema): SmartIri = querySchema match { case ApiV2Simple => OntologyConstants.KnoraApiV2Simple.Resource.toSmartIri case ApiV2Complex => OntologyConstants.KnoraApiV2Complex.Resource.toSmartIri } - } /** - * Returns knora-api:File (if the simple schema is given) or knora-api:FileValue (if the complex schema is given). - * - * @param querySchema the ontology schema that the query is written in. - */ - def getFileTypeForSchema(querySchema: ApiV2Schema): SmartIri = { + * Returns knora-api:File (if the simple schema is given) or knora-api:FileValue (if the complex schema is given). + * + * @param querySchema the ontology schema that the query is written in. + */ + def getFileTypeForSchema(querySchema: ApiV2Schema): SmartIri = querySchema match { case ApiV2Simple => OntologyConstants.KnoraApiV2Simple.File.toSmartIri case ApiV2Complex => OntologyConstants.KnoraApiV2Complex.FileValue.toSmartIri } - } /** - * Given a [[ReadPropertyInfoV2]], returns the IRI of the inferred `knora-api:subjectType` of the property, if any. - * - * @param readPropertyInfo the property definition. - * @param querySchema the query schema. - * @return the IRI of the inferred `knora-api:subjectType` of the property, or `None` if it could not inferred. - */ - def readPropertyInfoToSubjectType(readPropertyInfo: ReadPropertyInfoV2, - entityInfo: EntityInfoGetResponseV2, - querySchema: ApiV2Schema): Option[PropertyTypeInfo] = { + * Given a [[ReadPropertyInfoV2]], returns the IRI of the inferred `knora-api:subjectType` of the property, if any. + * + * @param readPropertyInfo the property definition. + * @param querySchema the query schema. + * @return the IRI of the inferred `knora-api:subjectType` of the property, or `None` if it could not inferred. + */ + def readPropertyInfoToSubjectType( + readPropertyInfo: ReadPropertyInfoV2, + entityInfo: EntityInfoGetResponseV2, + querySchema: ApiV2Schema + ): Option[PropertyTypeInfo] = // Get the knora-api:subjectType that the ontology responder provided. readPropertyInfo.entityInfoContent .getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.SubjectType.toSmartIri) - .orElse(readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri)) match { + .orElse( + readPropertyInfo.entityInfoContent + .getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.SubjectType.toSmartIri) + ) match { case Some(subjectType: SmartIri) => val subjectTypeStr = subjectType.toString @@ -675,8 +701,10 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe if (readPropertyInfo.isResourceProp) { // Yes. Use it. Some(PropertyTypeInfo(subjectType, objectIsResourceType = true)) - } else if (subjectTypeStr == OntologyConstants.KnoraApiV2Complex.Value || OntologyConstants.KnoraApiV2Complex.ValueBaseClasses - .contains(subjectTypeStr)) { + } else if ( + subjectTypeStr == OntologyConstants.KnoraApiV2Complex.Value || OntologyConstants.KnoraApiV2Complex.ValueBaseClasses + .contains(subjectTypeStr) + ) { // If it's knora-api:Value or one of the knora-api:ValueBase classes, don't use it. None } else if (OntologyConstants.KnoraApiV2Complex.FileValueClasses.contains(subjectTypeStr)) { @@ -698,7 +726,8 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } else { // It's not valid in a type inspection result. This must mean it's not allowed in Gravsearch queries. throw GravsearchException( - s"Type not allowed in Gravsearch queries: ${readPropertyInfo.entityInfoContent.propertyIri} knora-api:subjectType $subjectType") + s"Type not allowed in Gravsearch queries: ${readPropertyInfo.entityInfoContent.propertyIri} knora-api:subjectType $subjectType" + ) } } @@ -709,18 +738,19 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe Some(PropertyTypeInfo(getResourceTypeIriForSchema(querySchema), objectIsResourceType = true)) } else None } - } /** - * Given a [[ReadPropertyInfoV2]], returns the IRI of the inferred `knora-api:objectType` of the property, if any. - * - * @param readPropertyInfo the property definition. - * @param querySchema the ontology schema that the query is written in. - * @return the IRI of the inferred `knora-api:objectType` of the property, or `None` if it could not inferred. - */ - def readPropertyInfoToObjectType(readPropertyInfo: ReadPropertyInfoV2, - entityInfo: EntityInfoGetResponseV2, - querySchema: ApiV2Schema): Option[PropertyTypeInfo] = { + * Given a [[ReadPropertyInfoV2]], returns the IRI of the inferred `knora-api:objectType` of the property, if any. + * + * @param readPropertyInfo the property definition. + * @param querySchema the ontology schema that the query is written in. + * @return the IRI of the inferred `knora-api:objectType` of the property, or `None` if it could not inferred. + */ + def readPropertyInfoToObjectType( + readPropertyInfo: ReadPropertyInfoV2, + entityInfo: EntityInfoGetResponseV2, + querySchema: ApiV2Schema + ): Option[PropertyTypeInfo] = // Is this a file value property? if (readPropertyInfo.isFileValueProp) { // Yes, return the representation of file values in the specified schema. @@ -729,8 +759,10 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe // It's not a link property. Get the knora-api:objectType that the ontology responder provided. readPropertyInfo.entityInfoContent .getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri) - .orElse(readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri)) match { + .orElse( + readPropertyInfo.entityInfoContent + .getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri) + ) match { case Some(objectType: SmartIri) => val objectTypeStr = objectType.toString @@ -757,14 +789,14 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } else { // No. This must mean it's not allowed in Gravsearch queries. throw GravsearchException( - s"Type not allowed in Gravsearch queries: ${readPropertyInfo.entityInfoContent.propertyIri} knora-api:objectType $objectType") + s"Type not allowed in Gravsearch queries: ${readPropertyInfo.entityInfoContent.propertyIri} knora-api:objectType $objectType" + ) } } case None => None } } - } } // The inference rule pipeline for the first iteration. Includes rules that cannot return additional @@ -772,41 +804,61 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe // is run before InferTypeOfPredicateFromObject, so that the latter doesn't add a subtype of a type // already added by the former. private val firstIterationRulePipeline = new InferTypeOfSubjectOfRdfTypePredicate( - Some(new InferTypeOfPropertyFromItsIri(Some(new InferTypeOfSubjectFromPredicateIri( - Some(new InferTypeOfEntityFromKnownTypeInFilter(Some(new InferTypeOfVariableFromComparisonWithPropertyIriInFilter( - Some(new InferTypeOfObjectFromPredicate(Some(new InferTypeOfPredicateFromObject(None))))))))))))) + Some( + new InferTypeOfPropertyFromItsIri( + Some( + new InferTypeOfSubjectFromPredicateIri( + Some( + new InferTypeOfEntityFromKnownTypeInFilter( + Some( + new InferTypeOfVariableFromComparisonWithPropertyIriInFilter( + Some(new InferTypeOfObjectFromPredicate(Some(new InferTypeOfPredicateFromObject(None)))) + ) + ) + ) + ) + ) + ) + ) + ) + ) // The inference rule pipeline for subsequent iterations. Excludes rules that cannot return additional // information if they are run more than once. private val subsequentIterationRulePipeline = new InferTypeOfObjectFromPredicate( - Some(new InferTypeOfPredicateFromObject(Some(new InferTypeOfEntityFromComparisonWithOtherEntityInFilter(None))))) + Some(new InferTypeOfPredicateFromObject(Some(new InferTypeOfEntityFromComparisonWithOtherEntityInFilter(None)))) + ) /** - * An index of entity usage in a Gravsearch query. - * - * @param knoraClassIris the Knora class IRIs that are used in the query. - * @param knoraPropertyIris the Knora property IRIs that are used in the query. - * @param subjectIndex a map of all statement subjects to the statements they occur in. - * @param predicateIndex map of all statement predicates to the statements they occur in. - * @param objectIndex a map of all statement objects to the statements they occur in. - * @param knoraPropertyVariablesInFilters a map of query variables to Knora property IRIs that they are compared to in - * FILTER expressions. - * @param typedEntitiesInFilters a map of entities to types found for them in FILTER expressions. - * @param entitiesComparedInFilters variables or IRIs that are compared to other variables or IRIs in FILTER expressions. - */ - case class UsageIndex(knoraClassIris: Set[SmartIri] = Set.empty, - knoraPropertyIris: Set[SmartIri] = Set.empty, - subjectIndex: Map[TypeableEntity, Set[StatementPattern]] = Map.empty, - predicateIndex: Map[TypeableEntity, Set[StatementPattern]] = Map.empty, - objectIndex: Map[TypeableEntity, Set[StatementPattern]] = Map.empty, - knoraPropertyVariablesInFilters: Map[TypeableVariable, Set[SmartIri]] = Map.empty, - typedEntitiesInFilters: Map[TypeableEntity, Set[SmartIri]] = Map.empty, - entitiesComparedInFilters: Map[TypeableEntity, Set[TypeableEntity]] = Map.empty, - querySchema: ApiV2Schema) - - override def inspectTypes(previousResult: IntermediateTypeInspectionResult, - whereClause: WhereClause, - requestingUser: UserADM): Future[IntermediateTypeInspectionResult] = { + * An index of entity usage in a Gravsearch query. + * + * @param knoraClassIris the Knora class IRIs that are used in the query. + * @param knoraPropertyIris the Knora property IRIs that are used in the query. + * @param subjectIndex a map of all statement subjects to the statements they occur in. + * @param predicateIndex map of all statement predicates to the statements they occur in. + * @param objectIndex a map of all statement objects to the statements they occur in. + * @param knoraPropertyVariablesInFilters a map of query variables to Knora property IRIs that they are compared to in + * FILTER expressions. + * @param typedEntitiesInFilters a map of entities to types found for them in FILTER expressions. + * @param entitiesComparedInFilters variables or IRIs that are compared to other variables or IRIs in FILTER expressions. + */ + case class UsageIndex( + knoraClassIris: Set[SmartIri] = Set.empty, + knoraPropertyIris: Set[SmartIri] = Set.empty, + subjectIndex: Map[TypeableEntity, Set[StatementPattern]] = Map.empty, + predicateIndex: Map[TypeableEntity, Set[StatementPattern]] = Map.empty, + objectIndex: Map[TypeableEntity, Set[StatementPattern]] = Map.empty, + knoraPropertyVariablesInFilters: Map[TypeableVariable, Set[SmartIri]] = Map.empty, + typedEntitiesInFilters: Map[TypeableEntity, Set[SmartIri]] = Map.empty, + entitiesComparedInFilters: Map[TypeableEntity, Set[TypeableEntity]] = Map.empty, + querySchema: ApiV2Schema + ) + + override def inspectTypes( + previousResult: IntermediateTypeInspectionResult, + whereClause: WhereClause, + requestingUser: UserADM + ): Future[IntermediateTypeInspectionResult] = { log.debug("========== Starting type inference ==========") for { @@ -844,14 +896,16 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Get index of entity usage in the where clause of query and all ontology information about all the Knora class and property IRIs mentioned in the query. - * - * @param whereClause the query where clause. - * @param requestingUser the user requesting the query. - * @return a tuple containing the usage index and all entity information acquired from the ontology. - */ - def getUsageIndexAndEntityInfos(whereClause: WhereClause, - requestingUser: UserADM): Future[(UsageIndex, EntityInfoGetResponseV2)] = { + * Get index of entity usage in the where clause of query and all ontology information about all the Knora class and property IRIs mentioned in the query. + * + * @param whereClause the query where clause. + * @param requestingUser the user requesting the query. + * @return a tuple containing the usage index and all entity information acquired from the ontology. + */ + def getUsageIndexAndEntityInfos( + whereClause: WhereClause, + requestingUser: UserADM + ): Future[(UsageIndex, EntityInfoGetResponseV2)] = for { // Make an index of entity usage in the query. usageIndex <- Future(makeUsageIndex(whereClause)) @@ -878,26 +932,28 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe // properties returned. subjectAndObjectTypes: Set[SmartIri] = initialEntityInfoInInputSchemas.propertyInfoMap.foldLeft( - Set.empty[SmartIri]) { - case (acc, (propertyIri, propertyDef)) => - val propertyIriSchema = propertyIri.getOntologySchema match { - case Some(apiV2Schema: ApiV2Schema) => apiV2Schema - case other => throw AssertionException(s"Expected an ApiV2Schema, got $other") - } + Set.empty[SmartIri] + ) { case (acc, (propertyIri, propertyDef)) => + val propertyIriSchema = propertyIri.getOntologySchema match { + case Some(apiV2Schema: ApiV2Schema) => apiV2Schema + case other => throw AssertionException(s"Expected an ApiV2Schema, got $other") + } - val maybeSubjectType: Option[SmartIri] = propertyDef.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraApi.getSubjectTypePredicate(propertyIriSchema).toSmartIri) match { - case Some(subjectType) if subjectType.isKnoraEntityIri => Some(subjectType) - case _ => None - } + val maybeSubjectType: Option[SmartIri] = propertyDef.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraApi.getSubjectTypePredicate(propertyIriSchema).toSmartIri + ) match { + case Some(subjectType) if subjectType.isKnoraEntityIri => Some(subjectType) + case _ => None + } - val maybeObjectType: Option[SmartIri] = propertyDef.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraApi.getObjectTypePredicate(propertyIriSchema).toSmartIri) match { - case Some(objectType) if objectType.isKnoraEntityIri => Some(objectType) - case _ => None - } + val maybeObjectType: Option[SmartIri] = propertyDef.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraApi.getObjectTypePredicate(propertyIriSchema).toSmartIri + ) match { + case Some(objectType) if objectType.isKnoraEntityIri => Some(objectType) + case _ => None + } - acc ++ maybeSubjectType ++ maybeObjectType + acc ++ maybeSubjectType ++ maybeObjectType } additionalEntityInfoRequest = EntityInfoGetRequestV2( @@ -926,31 +982,34 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe ) } yield (usageIndexWithAdditionalClasses, allEntityInfo) - } /** - * Given an [[EntityInfoGetResponseV2]], converts each class and property back to the input schema - * found in the usage index. - * - * @param usageIndex the usage index. - * @param entityInfo the [[EntityInfoGetResponseV2]] that was returned by the ontology responder. - * @return an [[EntityInfoGetResponseV2]] in which the classes and properties are represented in the schemas - * found in the usage index. - */ - private def convertEntityInfoResponseToInputSchemas(usageIndex: UsageIndex, - entityInfo: EntityInfoGetResponseV2): EntityInfoGetResponseV2 = { + * Given an [[EntityInfoGetResponseV2]], converts each class and property back to the input schema + * found in the usage index. + * + * @param usageIndex the usage index. + * @param entityInfo the [[EntityInfoGetResponseV2]] that was returned by the ontology responder. + * @return an [[EntityInfoGetResponseV2]] in which the classes and properties are represented in the schemas + * found in the usage index. + */ + private def convertEntityInfoResponseToInputSchemas( + usageIndex: UsageIndex, + entityInfo: EntityInfoGetResponseV2 + ): EntityInfoGetResponseV2 = { /** - * Given a map of Knora class or property definitions, converts each one to the schema in which it was requested. - * - * @param inputEntityIris the IRIs of the entities that were requested. - * @param entityMap a map of entity IRIs to entity definitions as returned by the ontology responder. - * @tparam C the entity definition type, which can be [[ReadClassInfoV2]] or [[ReadPropertyInfoV2]]. - * @return a map of entity IRIs to entity definitions, with each IRI and definition represented in the - * schema in which it was requested. - */ - def toInputSchema[C <: KnoraReadV2[C]](inputEntityIris: Set[SmartIri], - entityMap: Map[SmartIri, C]): Map[SmartIri, C] = { + * Given a map of Knora class or property definitions, converts each one to the schema in which it was requested. + * + * @param inputEntityIris the IRIs of the entities that were requested. + * @param entityMap a map of entity IRIs to entity definitions as returned by the ontology responder. + * @tparam C the entity definition type, which can be [[ReadClassInfoV2]] or [[ReadPropertyInfoV2]]. + * @return a map of entity IRIs to entity definitions, with each IRI and definition represented in the + * schema in which it was requested. + */ + def toInputSchema[C <: KnoraReadV2[C]]( + inputEntityIris: Set[SmartIri], + entityMap: Map[SmartIri, C] + ): Map[SmartIri, C] = inputEntityIris.flatMap { inputEntityIri => val inputSchema = inputEntityIri.getOntologySchema.get match { case apiV2Schema: ApiV2Schema => apiV2Schema @@ -965,7 +1024,6 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe inputEntityIri -> readEntityInfo.toOntologySchema(inputSchema) } }.toMap - } EntityInfoGetResponseV2( classInfoMap = toInputSchema(usageIndex.knoraClassIris, entityInfo.classInfoMap), @@ -974,38 +1032,41 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Gets the iri of the type information. - * - * @param typeInfo a GravsearchEntityTypeInfo. - * @return the IRI of the typeInfo as a [[SmartIri]]. - */ - private def iriOfGravsearchTypeInfo(typeInfo: GravsearchEntityTypeInfo): SmartIri = { + * Gets the iri of the type information. + * + * @param typeInfo a GravsearchEntityTypeInfo. + * @return the IRI of the typeInfo as a [[SmartIri]]. + */ + private def iriOfGravsearchTypeInfo(typeInfo: GravsearchEntityTypeInfo): SmartIri = typeInfo match { case propertyTypeInfo: PropertyTypeInfo => propertyTypeInfo.objectTypeIri case nonPropertyTypeInfo: NonPropertyTypeInfo => nonPropertyTypeInfo.typeIri case _ => throw GravsearchException(s"There is an invalid type") } - } /** - * Sanitize determined results. If there were multiple resource types, replace with common base class - * - * @param lastResults this type inspection results. - * @param querySchema the ontology schema that the query is written in. - **/ - def sanitizeInconsistentResourceTypes(lastResults: IntermediateTypeInspectionResult, - querySchema: ApiV2Schema, - entityInfo: EntityInfoGetResponseV2): IntermediateTypeInspectionResult = { + * Sanitize determined results. If there were multiple resource types, replace with common base class + * + * @param lastResults this type inspection results. + * @param querySchema the ontology schema that the query is written in. + */ + def sanitizeInconsistentResourceTypes( + lastResults: IntermediateTypeInspectionResult, + querySchema: ApiV2Schema, + entityInfo: EntityInfoGetResponseV2 + ): IntermediateTypeInspectionResult = { /** - * Given a set of classes, this method finds a common base class. - * - * @param typesToBeChecked a set of classes. - * @param defaultBaseClassIri the default base class IRI if none is found. - * @return the IRI of a common base class. - */ - def findCommonBaseClass(typesToBeChecked: Set[GravsearchEntityTypeInfo], - defaultBaseClassIri: SmartIri): SmartIri = { + * Given a set of classes, this method finds a common base class. + * + * @param typesToBeChecked a set of classes. + * @param defaultBaseClassIri the default base class IRI if none is found. + * @return the IRI of a common base class. + */ + def findCommonBaseClass( + typesToBeChecked: Set[GravsearchEntityTypeInfo], + defaultBaseClassIri: SmartIri + ): SmartIri = { val baseClassesOfFirstType: Seq[SmartIri] = entityInfo.classInfoMap.get(iriOfGravsearchTypeInfo(typesToBeChecked.head)) match { case Some(classDef: ReadClassInfoV2) => classDef.allBaseClasses @@ -1036,12 +1097,14 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Replaces inconsistent types with a common base class. - */ - def replaceInconsistentTypes(acc: IntermediateTypeInspectionResult, - typedEntity: TypeableEntity, - typesToBeChecked: Set[GravsearchEntityTypeInfo], - newType: GravsearchEntityTypeInfo): IntermediateTypeInspectionResult = { + * Replaces inconsistent types with a common base class. + */ + def replaceInconsistentTypes( + acc: IntermediateTypeInspectionResult, + typedEntity: TypeableEntity, + typesToBeChecked: Set[GravsearchEntityTypeInfo], + newType: GravsearchEntityTypeInfo + ): IntermediateTypeInspectionResult = { val withoutInconsistentTypes: IntermediateTypeInspectionResult = typesToBeChecked.foldLeft(acc) { (sanitizeResults: IntermediateTypeInspectionResult, currType: GravsearchEntityTypeInfo) => sanitizeResults.removeType(entity = typedEntity, typeToRemove = currType) @@ -1060,58 +1123,74 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe val typesToBeChecked: Set[GravsearchEntityTypeInfo] = inconsistentEntities.getOrElse(typedEntity, Set.empty) // Are all inconsistent types NonPropertyTypeInfo representing resource classes? - if (typesToBeChecked.forall { - case nonPropertyTypeInfo: NonPropertyTypeInfo => nonPropertyTypeInfo.isResourceType - case _ => false - }) { + if ( + typesToBeChecked.forall { + case nonPropertyTypeInfo: NonPropertyTypeInfo => nonPropertyTypeInfo.isResourceType + case _ => false + } + ) { // Yes. Remove inconsistent types and replace with a common base class. val commonBaseClassIri: SmartIri = findCommonBaseClass(typesToBeChecked, InferenceRuleUtil.getResourceTypeIriForSchema(querySchema)) val newResourceType = NonPropertyTypeInfo(commonBaseClassIri, isResourceType = true) - replaceInconsistentTypes(acc = acc, - typedEntity = typedEntity, - typesToBeChecked = typesToBeChecked, - newType = newResourceType) - } else if (typesToBeChecked.forall { - case nonPropertyTypeInfo: NonPropertyTypeInfo => nonPropertyTypeInfo.isStandoffTagType - case _ => false - }) { + replaceInconsistentTypes( + acc = acc, + typedEntity = typedEntity, + typesToBeChecked = typesToBeChecked, + newType = newResourceType + ) + } else if ( + typesToBeChecked.forall { + case nonPropertyTypeInfo: NonPropertyTypeInfo => nonPropertyTypeInfo.isStandoffTagType + case _ => false + } + ) { // No, they're NonPropertyTypeInfo representing standoff tag classes. // Yes. Remove inconsistent types and replace with a common base class. val commonBaseClassIri: SmartIri = findCommonBaseClass(typesToBeChecked, OntologyConstants.KnoraApiV2Complex.StandoffTag.toSmartIri) val newStandoffTagType = NonPropertyTypeInfo(commonBaseClassIri, isStandoffTagType = true) - replaceInconsistentTypes(acc = acc, - typedEntity = typedEntity, - typesToBeChecked = typesToBeChecked, - newType = newStandoffTagType) - } else if (typesToBeChecked.forall { - case nonPropertyTypeInfo: PropertyTypeInfo => nonPropertyTypeInfo.objectIsResourceType - case _ => false - }) { + replaceInconsistentTypes( + acc = acc, + typedEntity = typedEntity, + typesToBeChecked = typesToBeChecked, + newType = newStandoffTagType + ) + } else if ( + typesToBeChecked.forall { + case nonPropertyTypeInfo: PropertyTypeInfo => nonPropertyTypeInfo.objectIsResourceType + case _ => false + } + ) { // No, they're PropertyTypeInfo types with object types representing resource classes. // Remove inconsistent types and replace with a common base class. val commonBaseClassIri: SmartIri = findCommonBaseClass(typesToBeChecked, InferenceRuleUtil.getResourceTypeIriForSchema(querySchema)) val newObjectType = PropertyTypeInfo(commonBaseClassIri, objectIsResourceType = true) - replaceInconsistentTypes(acc = acc, - typedEntity = typedEntity, - typesToBeChecked = typesToBeChecked, - newType = newObjectType) - - } else if (typesToBeChecked.forall { - case nonPropertyTypeInfo: PropertyTypeInfo => nonPropertyTypeInfo.objectIsStandoffTagType - case _ => false - }) { + replaceInconsistentTypes( + acc = acc, + typedEntity = typedEntity, + typesToBeChecked = typesToBeChecked, + newType = newObjectType + ) + + } else if ( + typesToBeChecked.forall { + case nonPropertyTypeInfo: PropertyTypeInfo => nonPropertyTypeInfo.objectIsStandoffTagType + case _ => false + } + ) { // No, they're PropertyTypeInfo types with object types representing standoff tag classes. // Remove inconsistent types and replace with a common base class. val commonBaseClassIri: SmartIri = findCommonBaseClass(typesToBeChecked, OntologyConstants.KnoraApiV2Complex.StandoffTag.toSmartIri) val newObjectType = PropertyTypeInfo(commonBaseClassIri, objectIsStandoffTagType = true) - replaceInconsistentTypes(acc = acc, - typedEntity = typedEntity, - typesToBeChecked = typesToBeChecked, - newType = newObjectType) + replaceInconsistentTypes( + acc = acc, + typedEntity = typedEntity, + typesToBeChecked = typesToBeChecked, + newType = newObjectType + ) } else { // None of the above. Don't touch the determined inconsistent types, later an error is returned for this. acc @@ -1120,26 +1199,27 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Make sure that the most specific type is stored for each typeable entity. - * - * @param intermediateResult this rule's intermediate result. - * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. - */ - def refineDeterminedTypes(intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2): IntermediateTypeInspectionResult = { + * Make sure that the most specific type is stored for each typeable entity. + * + * @param intermediateResult this rule's intermediate result. + * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. + */ + def refineDeterminedTypes( + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2 + ): IntermediateTypeInspectionResult = { /** - * Returns `true` if the specified type is a base class of any of the other types in a set. - */ + * Returns `true` if the specified type is a base class of any of the other types in a set. + */ def typeInBaseClasses(currType: GravsearchEntityTypeInfo, allTypes: Set[GravsearchEntityTypeInfo]): Boolean = { val currTypeIri = iriOfGravsearchTypeInfo(currType) - allTypes.exists( - aType => - entityInfo.classInfoMap.get(iriOfGravsearchTypeInfo(aType)) match { - case Some(classDef: ReadClassInfoV2) => - classDef.allBaseClasses.contains(currTypeIri) - case _ => false + allTypes.exists(aType => + entityInfo.classInfoMap.get(iriOfGravsearchTypeInfo(aType)) match { + case Some(classDef: ReadClassInfoV2) => + classDef.allBaseClasses.contains(currTypeIri) + case _ => false } ) } @@ -1160,19 +1240,21 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Runs all the inference rules repeatedly until no new type information can be found. - * - * @param iterationNumber the current iteration number. - * @param intermediateResult the current intermediate result. - * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. - * @param usageIndex an index of entity usage in the query. - * @return a new intermediate result. - */ + * Runs all the inference rules repeatedly until no new type information can be found. + * + * @param iterationNumber the current iteration number. + * @param intermediateResult the current intermediate result. + * @param entityInfo information about Knora ontology entities mentioned in the Gravsearch query. + * @param usageIndex an index of entity usage in the query. + * @return a new intermediate result. + */ @tailrec - private def doIterations(iterationNumber: Int, - intermediateResult: IntermediateTypeInspectionResult, - entityInfo: EntityInfoGetResponseV2, - usageIndex: UsageIndex): IntermediateTypeInspectionResult = { + private def doIterations( + iterationNumber: Int, + intermediateResult: IntermediateTypeInspectionResult, + entityInfo: EntityInfoGetResponseV2, + usageIndex: UsageIndex + ): IntermediateTypeInspectionResult = { if (iterationNumber > MAX_ITERATIONS) { throw GravsearchException(s"Too many type inference iterations") } @@ -1180,7 +1262,8 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe // Run an iteration of type inference and get its result. log.debug( - s"****** Inference iteration $iterationNumber (untyped ${intermediateResult.untypedEntities.size}, inconsistent ${intermediateResult.entitiesWithInconsistentTypes.size})") + s"****** Inference iteration $iterationNumber (untyped ${intermediateResult.untypedEntities.size}, inconsistent ${intermediateResult.entitiesWithInconsistentTypes.size})" + ) val iterationResult: IntermediateTypeInspectionResult = intermediateResult.entities.keySet.foldLeft(intermediateResult) { @@ -1214,17 +1297,17 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Creates a usage index from a Gravsearch WHERE clause. - */ + * Creates a usage index from a Gravsearch WHERE clause. + */ private class UsageIndexCollectingWhereVisitor extends WhereVisitor[UsageIndex] { /** - * Collects information for the usage index from statements. - * - * @param statementPattern the pattern to be visited. - * @param usageIndex the usage index being constructed. - * @return an updated usage index. - */ + * Collects information for the usage index from statements. + * + * @param statementPattern the pattern to be visited. + * @param usageIndex the usage index being constructed. + * @return an updated usage index. + */ override def visitStatementInWhere(statementPattern: StatementPattern, usageIndex: UsageIndex): UsageIndex = { // Index the statement by subject. val subjectIndex: Map[TypeableEntity, Set[StatementPattern]] = addStatementIndexEntry( @@ -1266,7 +1349,9 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe val knoraPropertyIris: Set[SmartIri] = usageIndex.knoraPropertyIris ++ (statementPattern.pred match { case IriRef(predIri, _) if predIri.isKnoraEntityIri && - !(GravsearchTypeInspectionUtil.TypeAnnotationProperties.allTypeAnnotationIris.contains(predIri.toString) || + !(GravsearchTypeInspectionUtil.TypeAnnotationProperties.allTypeAnnotationIris.contains( + predIri.toString + ) || GravsearchTypeInspectionUtil.GravsearchOptionIris.contains(predIri.toString)) => Some(predIri) @@ -1283,18 +1368,19 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Given a statement pattern and an entity contained in it, checks whether the entity is typeable, and makes an index - * entry if so. - * - * @param statementEntity the entity (subject, predicate, or object). - * @param statementPattern the statement pattern. - * @param statementIndex an accumulator for a statement index. - * @return an updated index entry. - */ + * Given a statement pattern and an entity contained in it, checks whether the entity is typeable, and makes an index + * entry if so. + * + * @param statementEntity the entity (subject, predicate, or object). + * @param statementPattern the statement pattern. + * @param statementIndex an accumulator for a statement index. + * @return an updated index entry. + */ private def addStatementIndexEntry( - statementEntity: Entity, - statementPattern: StatementPattern, - statementIndex: Map[TypeableEntity, Set[StatementPattern]]): Map[TypeableEntity, Set[StatementPattern]] = { + statementEntity: Entity, + statementPattern: StatementPattern, + statementIndex: Map[TypeableEntity, Set[StatementPattern]] + ): Map[TypeableEntity, Set[StatementPattern]] = GravsearchTypeInspectionUtil.maybeTypeableEntity(statementEntity) match { case Some(typeableEntity) => val currentPatterns: Set[StatementPattern] = statementIndex.getOrElse(typeableEntity, Set.empty) @@ -1302,30 +1388,30 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe case None => statementIndex } - } /** - * Collects information for the usage index from filters. - * - * @param filterPattern the pattern to be visited. - * @param usageIndex the usage index being constructed. - * @return an updated usage index. - */ - override def visitFilter(filterPattern: FilterPattern, usageIndex: UsageIndex): UsageIndex = { + * Collects information for the usage index from filters. + * + * @param filterPattern the pattern to be visited. + * @param usageIndex the usage index being constructed. + * @return an updated usage index. + */ + override def visitFilter(filterPattern: FilterPattern, usageIndex: UsageIndex): UsageIndex = visitFilterExpression(filterPattern.expression, usageIndex) - } /** - * Indexes two entities that are compared in a FILTER expression. - * - * @param leftQueryVariable the query variable that is the left argument of the comparison. - * @param rightEntity the query variable or IRI that is the right argument of the comparison. - * @param usageIndex the usage index being constructed. - * @return an an updated usage index. - */ - private def addEntityComparisonIndexEntry(leftQueryVariable: QueryVariable, - rightEntity: Entity, - usageIndex: UsageIndex): UsageIndex = { + * Indexes two entities that are compared in a FILTER expression. + * + * @param leftQueryVariable the query variable that is the left argument of the comparison. + * @param rightEntity the query variable or IRI that is the right argument of the comparison. + * @param usageIndex the usage index being constructed. + * @return an an updated usage index. + */ + private def addEntityComparisonIndexEntry( + leftQueryVariable: QueryVariable, + rightEntity: Entity, + usageIndex: UsageIndex + ): UsageIndex = { val leftTypeableVariable = TypeableVariable(leftQueryVariable.variableName) val rightTypeableEntity: TypeableEntity = GravsearchTypeInspectionUtil.maybeTypeableEntity(rightEntity) match { @@ -1347,17 +1433,19 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe } /** - * Visits a [[CompareExpression]] in a [[FilterPattern]]. - * - * @param compareExpression the comparison expression to be visited. - * @param usageIndex the usage index being constructed. - * @return an updated usage index. - */ - private def visitCompareExpression(compareExpression: CompareExpression, usageIndex: UsageIndex): UsageIndex = { + * Visits a [[CompareExpression]] in a [[FilterPattern]]. + * + * @param compareExpression the comparison expression to be visited. + * @param usageIndex the usage index being constructed. + * @return an updated usage index. + */ + private def visitCompareExpression(compareExpression: CompareExpression, usageIndex: UsageIndex): UsageIndex = compareExpression match { - case CompareExpression(leftQueryVariable: QueryVariable, - operator: CompareExpressionOperator.Value, - rightEntity: Entity) => + case CompareExpression( + leftQueryVariable: QueryVariable, + operator: CompareExpressionOperator.Value, + rightEntity: Entity + ) => rightEntity match { case xsdLiteral: XsdLiteral => // A variable is compared to an XSD literal. Index the variable and the literal's type. @@ -1366,7 +1454,8 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe usageIndex.typedEntitiesInFilters.getOrElse(typeableVariable, Set.empty) usageIndex.copy( - typedEntitiesInFilters = usageIndex.typedEntitiesInFilters + (typeableVariable -> (currentVarTypesFromFilters + xsdLiteral.datatype)) + typedEntitiesInFilters = + usageIndex.typedEntitiesInFilters + (typeableVariable -> (currentVarTypesFromFilters + xsdLiteral.datatype)) ) case rightIriRef: IriRef if rightIriRef.iri.isKnoraEntityIri => @@ -1383,7 +1472,8 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe usageIndex.copy( knoraPropertyIris = usageIndex.knoraPropertyIris + rightIriRef.iri, - knoraPropertyVariablesInFilters = usageIndex.knoraPropertyVariablesInFilters + (typeableVariable -> (currentIris + rightIriRef.iri)) + knoraPropertyVariablesInFilters = + usageIndex.knoraPropertyVariablesInFilters + (typeableVariable -> (currentIris + rightIriRef.iri)) ) case rightQueryVariable: QueryVariable => @@ -1400,7 +1490,8 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe if (!rightIriRef.iri.isKnoraResourceIri) { throw GravsearchException( - s"IRI ${rightIriRef.toSparql}, used in a comparison, is not a Knora resource IRI") + s"IRI ${rightIriRef.toSparql}, used in a comparison, is not a Knora resource IRI" + ) } addEntityComparisonIndexEntry( @@ -1414,17 +1505,18 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe val usageIndexFromLeft = visitFilterExpression(compareExpression.leftArg, usageIndex) visitFilterExpression(compareExpression.rightArg, usageIndexFromLeft) } - } /** - * Visits a [[FunctionCallExpression]] in a [[FilterPattern]]. - * - * @param functionCallExpression the function call to be visited. - * @param usageIndex the usage index being constructed. - * @return an updated usage index. - */ - private def visitFunctionCallExpression(functionCallExpression: FunctionCallExpression, - usageIndex: UsageIndex): UsageIndex = { + * Visits a [[FunctionCallExpression]] in a [[FilterPattern]]. + * + * @param functionCallExpression the function call to be visited. + * @param usageIndex the usage index being constructed. + * @return an updated usage index. + */ + private def visitFunctionCallExpression( + functionCallExpression: FunctionCallExpression, + usageIndex: UsageIndex + ): UsageIndex = functionCallExpression.functionIri.iri.toString match { case OntologyConstants.KnoraApiV2Simple.MatchTextFunction => // The first argument is a variable representing a string. @@ -1490,19 +1582,18 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe case OntologyConstants.KnoraApiV2Complex.StandoffLinkFunction => if (functionCallExpression.args.size != 3) throw GravsearchException( - s"Three arguments are expected for ${functionCallExpression.functionIri.toSparql}") + s"Three arguments are expected for ${functionCallExpression.functionIri.toSparql}" + ) // The first and third arguments are variables or IRIs representing resources. val resourceEntitiesAndTypes: Seq[(TypeableEntity, Set[SmartIri])] = - Seq(functionCallExpression.args.head, functionCallExpression.args(2)) - .flatMap { entity => - GravsearchTypeInspectionUtil.maybeTypeableEntity(entity) - } - .map { typeableEntity => - val currentVarTypesFromFilters: Set[SmartIri] = - usageIndex.typedEntitiesInFilters.getOrElse(typeableEntity, Set.empty) - typeableEntity -> (currentVarTypesFromFilters + OntologyConstants.KnoraApiV2Complex.Resource.toSmartIri) - } + Seq(functionCallExpression.args.head, functionCallExpression.args(2)).flatMap { entity => + GravsearchTypeInspectionUtil.maybeTypeableEntity(entity) + }.map { typeableEntity => + val currentVarTypesFromFilters: Set[SmartIri] = + usageIndex.typedEntitiesInFilters.getOrElse(typeableEntity, Set.empty) + typeableEntity -> (currentVarTypesFromFilters + OntologyConstants.KnoraApiV2Complex.Resource.toSmartIri) + } // The second argument is a variable representing a standoff tag. val standoffTagVar = TypeableVariable(functionCallExpression.getArgAsQueryVar(1).variableName) @@ -1521,16 +1612,15 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe case _ => throw GravsearchException(s"Unrecognised function: ${functionCallExpression.functionIri.toSparql}") } - } /** - * Collects information for the usage index from filter expressions. - * - * @param filterExpression the filter expression to be visited. - * @param usageIndex the usage index being constructed. - * @return an updated usage index. - */ - private def visitFilterExpression(filterExpression: Expression, usageIndex: UsageIndex): UsageIndex = { + * Collects information for the usage index from filter expressions. + * + * @param filterExpression the filter expression to be visited. + * @param usageIndex the usage index being constructed. + * @return an updated usage index. + */ + private def visitFilterExpression(filterExpression: Expression, usageIndex: UsageIndex): UsageIndex = filterExpression match { case compareExpression: CompareExpression => visitCompareExpression(compareExpression = compareExpression, usageIndex = usageIndex) @@ -1548,32 +1638,31 @@ class InferringGravsearchTypeInspector(nextInspector: Option[GravsearchTypeInspe case _ => usageIndex } - } } /** - * Makes an index of entity usage in the query. - * - * @param whereClause the WHERE clause in the query. - * @return an index of entity usage in the query. - */ - private def makeUsageIndex(whereClause: WhereClause): UsageIndex = { + * Makes an index of entity usage in the query. + * + * @param whereClause the WHERE clause in the query. + * @return an index of entity usage in the query. + */ + private def makeUsageIndex(whereClause: WhereClause): UsageIndex = // Traverse the query, collecting information for the usage index. QueryTraverser.visitWherePatterns( patterns = whereClause.patterns, whereVisitor = new UsageIndexCollectingWhereVisitor, initialAcc = UsageIndex( - querySchema = whereClause.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema"))) + querySchema = whereClause.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema")) + ) ) - } } object InferringGravsearchTypeInspector { /** - * Provides the string representation of the companion class in log messages. - * - * See [[https://doc.akka.io/docs/akka/current/logging.html#translating-log-source-to-string-and-class]]. - */ + * Provides the string representation of the companion class in log messages. + * + * See [[https://doc.akka.io/docs/akka/current/logging.html#translating-log-source-to-string-and-class]]. + */ implicit val logSource: LogSource[AnyRef] = (o: AnyRef) => o.getClass.getName } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/IntermediateTypeInspectionResult.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/IntermediateTypeInspectionResult.scala index eedcfe4c51..f6d39962ed 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/IntermediateTypeInspectionResult.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/search/gravsearch/types/IntermediateTypeInspectionResult.scala @@ -25,35 +25,37 @@ import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.{OntologyConstants, StringFormatter} /** - * Represents an intermediate result during type inspection. This is different from [[GravsearchTypeInspectionResult]] - * in that an entity can have multiple types, which means that the entity has been used inconsistently. - * - * @param entities a map of Gravsearch entities to the types that were determined for them. If an entity - * has more than one type, this means that it has been used with inconsistent types. - * @param entitiesInferredFromPropertyIris entities whose types were inferred from their use with a property IRI. - */ + * Represents an intermediate result during type inspection. This is different from [[GravsearchTypeInspectionResult]] + * in that an entity can have multiple types, which means that the entity has been used inconsistently. + * + * @param entities a map of Gravsearch entities to the types that were determined for them. If an entity + * has more than one type, this means that it has been used with inconsistent types. + * @param entitiesInferredFromPropertyIris entities whose types were inferred from their use with a property IRI. + */ case class IntermediateTypeInspectionResult( - entities: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]], - entitiesInferredFromPropertyIris: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = Map.empty) { + entities: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]], + entitiesInferredFromPropertyIris: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = Map.empty +) { /** - * Adds types for an entity. - * - * @param entity the entity for which types have been found. - * @param entityTypes the types to be added. - * @param inferredFromPropertyIri `true` if any of the types of this entity were inferred from its use with a property IRI. - * @return a new [[IntermediateTypeInspectionResult]] containing the additional type information. - */ - def addTypes(entity: TypeableEntity, - entityTypes: Set[GravsearchEntityTypeInfo], - inferredFromPropertyIri: Boolean = false): IntermediateTypeInspectionResult = { + * Adds types for an entity. + * + * @param entity the entity for which types have been found. + * @param entityTypes the types to be added. + * @param inferredFromPropertyIri `true` if any of the types of this entity were inferred from its use with a property IRI. + * @return a new [[IntermediateTypeInspectionResult]] containing the additional type information. + */ + def addTypes( + entity: TypeableEntity, + entityTypes: Set[GravsearchEntityTypeInfo], + inferredFromPropertyIri: Boolean = false + ): IntermediateTypeInspectionResult = if (entityTypes.nonEmpty) { val newTypes = entities.getOrElse(entity, Set.empty[GravsearchEntityTypeInfo]) ++ entityTypes val newEntitiesInferredFromPropertyIris = if (inferredFromPropertyIri && entityTypes.nonEmpty) { - val newTypesInferredFromPropertyIris = entitiesInferredFromPropertyIris.getOrElse( - entity, - Set.empty[GravsearchEntityTypeInfo]) ++ entityTypes + val newTypesInferredFromPropertyIris = + entitiesInferredFromPropertyIris.getOrElse(entity, Set.empty[GravsearchEntityTypeInfo]) ++ entityTypes entitiesInferredFromPropertyIris + (entity -> newTypesInferredFromPropertyIris) } else { entitiesInferredFromPropertyIris @@ -66,15 +68,14 @@ case class IntermediateTypeInspectionResult( } else { this } - } /** - * removes types of an entity. - * - * @param entity the entity for which types must be removed. - * @param typeToRemove the type to be removed. - * @return a new [[IntermediateTypeInspectionResult]] without the specified type information assigned to the entity. - */ + * removes types of an entity. + * + * @param entity the entity for which types must be removed. + * @param typeToRemove the type to be removed. + * @return a new [[IntermediateTypeInspectionResult]] without the specified type information assigned to the entity. + */ def removeType(entity: TypeableEntity, typeToRemove: GravsearchEntityTypeInfo): IntermediateTypeInspectionResult = { val remainingTypes = entities.getOrElse(entity, Set.empty[GravsearchEntityTypeInfo]) - typeToRemove @@ -98,59 +99,57 @@ case class IntermediateTypeInspectionResult( } /** - * Returns the entities for which types have not been found. - */ - def untypedEntities: Set[TypeableEntity] = { + * Returns the entities for which types have not been found. + */ + def untypedEntities: Set[TypeableEntity] = entities.collect { case (entity: TypeableEntity, entityTypes: Set[GravsearchEntityTypeInfo]) if entityTypes.isEmpty => entity }.toSet - } /** - * Returns the entities that have been used with inconsistent types. - */ - def entitiesWithInconsistentTypes: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = { - entities.filter { - case (_, entityTypes) => entityTypes.size > 1 + * Returns the entities that have been used with inconsistent types. + */ + def entitiesWithInconsistentTypes: Map[TypeableEntity, Set[GravsearchEntityTypeInfo]] = + entities.filter { case (_, entityTypes) => + entityTypes.size > 1 } - } /** - * Converts this [[IntermediateTypeInspectionResult]] to a [[GravsearchTypeInspectionResult]]. Before calling - * this method, ensure that `entitiesWithInconsistentTypes` returns an empty map. - */ - def toFinalResult: GravsearchTypeInspectionResult = { + * Converts this [[IntermediateTypeInspectionResult]] to a [[GravsearchTypeInspectionResult]]. Before calling + * this method, ensure that `entitiesWithInconsistentTypes` returns an empty map. + */ + def toFinalResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( - entities = entities.map { - case (entity, entityTypes) => - if (entityTypes.size == 1) { - entity -> entityTypes.head - } else { - throw AssertionException(s"Cannot generate final type inspection result because of inconsistent types") - } + entities = entities.map { case (entity, entityTypes) => + if (entityTypes.size == 1) { + entity -> entityTypes.head + } else { + throw AssertionException(s"Cannot generate final type inspection result because of inconsistent types") + } }, entitiesInferredFromProperties = entitiesInferredFromPropertyIris ) - } } object IntermediateTypeInspectionResult { /** - * Constructs an [[IntermediateTypeInspectionResult]] for the given set of typeable entities, with built-in - * types specified (e.g. for `rdfs:label`). - * - * @param entities the set of typeable entities found in the WHERE clause of a Gravsearch query. - */ - def apply(entities: Set[TypeableEntity])( - implicit stringFormatter: StringFormatter): IntermediateTypeInspectionResult = { + * Constructs an [[IntermediateTypeInspectionResult]] for the given set of typeable entities, with built-in + * types specified (e.g. for `rdfs:label`). + * + * @param entities the set of typeable entities found in the WHERE clause of a Gravsearch query. + */ + def apply( + entities: Set[TypeableEntity] + )(implicit stringFormatter: StringFormatter): IntermediateTypeInspectionResult = { // Make an IntermediateTypeInspectionResult in which each typeable entity has no types. val emptyResult = new IntermediateTypeInspectionResult( - entities = entities.map(entity => entity -> Set.empty[GravsearchEntityTypeInfo]).toMap) + entities = entities.map(entity => entity -> Set.empty[GravsearchEntityTypeInfo]).toMap + ) // Collect the typeable IRIs used. - val irisUsed: Set[IRI] = entities.collect { - case typeableIri: TypeableIri => typeableIri.iri.toString + val irisUsed: Set[IRI] = entities.collect { case typeableIri: TypeableIri => + typeableIri.iri.toString } // Find the IRIs that represent resource metadata properties, and get their object types. @@ -158,12 +157,13 @@ object IntermediateTypeInspectionResult { OntologyConstants.ResourceMetadataPropertyAxioms.view .filterKeys(irisUsed) .toMap - .map { - case (propertyIri: IRI, objectTypeIri: IRI) => - val isValue: Boolean = GravsearchTypeInspectionUtil.GravsearchValueTypeIris.contains(objectTypeIri) - TypeableIri(propertyIri.toSmartIri) -> PropertyTypeInfo(objectTypeIri = objectTypeIri.toSmartIri, - objectIsResourceType = !isValue, - objectIsValueType = isValue) + .map { case (propertyIri: IRI, objectTypeIri: IRI) => + val isValue: Boolean = GravsearchTypeInspectionUtil.GravsearchValueTypeIris.contains(objectTypeIri) + TypeableIri(propertyIri.toSmartIri) -> PropertyTypeInfo( + objectTypeIri = objectTypeIri.toSmartIri, + objectIsResourceType = !isValue, + objectIsValueType = isValue + ) } // Add those types to the IntermediateTypeInspectionResult. diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2.scala index 317c80907b..9a528563b8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2.scala @@ -64,20 +64,23 @@ object StandoffTagUtilV2 { private val fixedUuid = UUID.fromString("00000000-0000-0000-0000-000000000000") /** - * Tries to find a data type attribute in the XML attributes of a given standoff node. Throws an appropriate error if information is inconsistent or missing. - * - * @param xmlToStandoffMapping the mapping from XML to standoff classes and properties for the given standoff node. - * @param dataType the expected data type of the given standoff node. - * @param standoffNodeFromXML the given standoff node. - * @return the value of the attribute. - */ - private def getDataTypeAttribute(xmlToStandoffMapping: XMLTagToStandoffClass, - dataType: StandoffDataTypeClasses.Value, - standoffNodeFromXML: StandoffTag): String = { + * Tries to find a data type attribute in the XML attributes of a given standoff node. Throws an appropriate error if information is inconsistent or missing. + * + * @param xmlToStandoffMapping the mapping from XML to standoff classes and properties for the given standoff node. + * @param dataType the expected data type of the given standoff node. + * @param standoffNodeFromXML the given standoff node. + * @return the value of the attribute. + */ + private def getDataTypeAttribute( + xmlToStandoffMapping: XMLTagToStandoffClass, + dataType: StandoffDataTypeClasses.Value, + standoffNodeFromXML: StandoffTag + ): String = { if (xmlToStandoffMapping.dataType.isEmpty || xmlToStandoffMapping.dataType.get.standoffDataTypeClass != dataType) { throw BadRequestException( - s"no or wrong data type definition provided in mapping for standoff class ${xmlToStandoffMapping.standoffClassIri}") + s"no or wrong data type definition provided in mapping for standoff class ${xmlToStandoffMapping.standoffClassIri}" + ) } val attrName = xmlToStandoffMapping.dataType.get.dataTypeXMLAttribute @@ -94,20 +97,21 @@ object StandoffTagUtilV2 { } /** - * Creates a sequence of [[StandoffTagAttributeV2]] for the given standoff node. - * - * @param xmlToStandoffMapping the mapping from XML to standoff classes and properties for the given standoff node. - * @param classSpecificProps the properties that may or have to be created (cardinalities) for the given standoff node. - * @param standoffNodeFromXML the given standoff node. - * @param standoffPropertyEntities the ontology information about the standoff properties. - * @return a sequence of [[StandoffTagAttributeV2]] - */ + * Creates a sequence of [[StandoffTagAttributeV2]] for the given standoff node. + * + * @param xmlToStandoffMapping the mapping from XML to standoff classes and properties for the given standoff node. + * @param classSpecificProps the properties that may or have to be created (cardinalities) for the given standoff node. + * @param standoffNodeFromXML the given standoff node. + * @param standoffPropertyEntities the ontology information about the standoff properties. + * @return a sequence of [[StandoffTagAttributeV2]] + */ private def createAttributes( - xmlToStandoffMapping: XMLTagToStandoffClass, - classSpecificProps: Map[SmartIri, KnoraCardinalityInfo], - existingXMLIDs: Seq[String], - standoffNodeFromXML: StandoffTag, - standoffPropertyEntities: Map[SmartIri, ReadPropertyInfoV2]): Seq[StandoffTagAttributeV2] = { + xmlToStandoffMapping: XMLTagToStandoffClass, + classSpecificProps: Map[SmartIri, KnoraCardinalityInfo], + existingXMLIDs: Seq[String], + standoffNodeFromXML: StandoffTag, + standoffPropertyEntities: Map[SmartIri, ReadPropertyInfoV2] + ): Seq[StandoffTagAttributeV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -117,7 +121,8 @@ object StandoffTagUtilV2 { // map over all non data type attributes, ignore the "class" attribute ("class" is only used in the mapping to allow for the reuse of the same tag name, not to store actual data). val attrs: Seq[StandoffTagAttributeV2] = standoffNodeFromXML.attributes .filterNot(attr => - (xmlToStandoffMapping.dataType.nonEmpty && xmlToStandoffMapping.dataType.get.dataTypeXMLAttribute == attr.key) || attr.key == classAttribute) + (xmlToStandoffMapping.dataType.nonEmpty && xmlToStandoffMapping.dataType.get.dataTypeXMLAttribute == attr.key) || attr.key == classAttribute + ) .map { attr: StandoffTagAttribute => // get the standoff property IRI for this XML attribute @@ -129,7 +134,8 @@ object StandoffTagUtilV2 { val standoffTagPropIri: SmartIri = xmlToStandoffMapping.attributesToProps .getOrElse( xmlNamespace, - throw BadRequestException(s"namespace $xmlNamespace unknown for attribute ${attr.key} in mapping")) + throw BadRequestException(s"namespace $xmlNamespace unknown for attribute ${attr.key} in mapping") + ) .getOrElse(attr.key, throw BadRequestException(s"mapping for attr '${attr.key}' not provided")) .toSmartIri val propPredicates: Map[SmartIri, PredicateInfoV2] = @@ -151,7 +157,8 @@ object StandoffTagUtilV2 { standoffPropertyIri = standoffTagPropIri, value = stringFormatter.toSparqlEncodedString( attr.value, - throw BadRequestException(s"Invalid string attribute: '${attr.value}'")) + throw BadRequestException(s"Invalid string attribute: '${attr.value}'") + ) ) case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Integer))) => @@ -166,7 +173,8 @@ object StandoffTagUtilV2 { standoffPropertyIri = standoffTagPropIri, value = stringFormatter.validateBigDecimal( attr.value, - throw BadRequestException(s"Invalid decimal attribute: '${attr.value}'")) + throw BadRequestException(s"Invalid decimal attribute: '${attr.value}'") + ) ) case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Boolean))) => @@ -174,7 +182,8 @@ object StandoffTagUtilV2 { standoffPropertyIri = standoffTagPropIri, value = stringFormatter.validateBoolean( attr.value, - throw BadRequestException(s"Invalid boolean attribute: '${attr.value}'")) + throw BadRequestException(s"Invalid boolean attribute: '${attr.value}'") + ) ) case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.DateTime))) => @@ -182,22 +191,26 @@ object StandoffTagUtilV2 { standoffPropertyIri = standoffTagPropIri, value = stringFormatter.xsdDateTimeStampToInstant( attr.value, - throw BadRequestException(s"Invalid timestamp attribute: '${attr.value}'")) + throw BadRequestException(s"Invalid timestamp attribute: '${attr.value}'") + ) ) case None => throw InconsistentRepositoryDataException( - s"did not find ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} for $standoffTagPropIri") + s"did not find ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} for $standoffTagPropIri" + ) case other => throw InconsistentRepositoryDataException( - s"triplestore returned unknown ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} '$other' for $standoffTagPropIri") + s"triplestore returned unknown ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} '$other' for $standoffTagPropIri" + ) } } else { // only properties with a `ObjectDatatypeConstraint` are allowed here (linking properties have to be created via data type standoff classes) throw InconsistentRepositoryDataException( - s"no ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} given for property '$standoffTagPropIri'") + s"no ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} given for property '$standoffTagPropIri'" + ) } } @@ -207,9 +220,8 @@ object StandoffTagUtilV2 { attrs.groupBy(attr => attr.standoffPropertyIri) // filter all the required props - val mustExistOnce: Set[SmartIri] = classSpecificProps.filter { - case (propIri, card) => - card.cardinality == Cardinality.MustHaveOne || card.cardinality == Cardinality.MustHaveSome + val mustExistOnce: Set[SmartIri] = classSpecificProps.filter { case (propIri, card) => + card.cardinality == Cardinality.MustHaveOne || card.cardinality == Cardinality.MustHaveSome }.keySet // check if all the min cardinalities are respected @@ -219,14 +231,14 @@ object StandoffTagUtilV2 { case None => throw BadRequestException( - s"the min cardinalities were not respected for the property $propIri (missing attribute for element ${standoffNodeFromXML.tagName})") + s"the min cardinalities were not respected for the property $propIri (missing attribute for element ${standoffNodeFromXML.tagName})" + ) } } // filter all the props that have a limited occurrence - val mayExistOnce = classSpecificProps.filter { - case (propIri, card) => - card.cardinality == Cardinality.MustHaveOne || card.cardinality == Cardinality.MayHaveOne + val mayExistOnce = classSpecificProps.filter { case (propIri, card) => + card.cardinality == Cardinality.MustHaveOne || card.cardinality == Cardinality.MayHaveOne }.keySet // check if all the max cardinalities are respected @@ -235,7 +247,8 @@ object StandoffTagUtilV2 { case Some(attrs: Seq[StandoffTagAttributeV2]) => if (attrs.size > 1) { throw BadRequestException( - s"the max cardinalities were not respected for $propIri (for element ${standoffNodeFromXML.tagName})") + s"the max cardinalities were not respected for $propIri (for element ${standoffNodeFromXML.tagName})" + ) } case None => () } @@ -247,15 +260,15 @@ object StandoffTagUtilV2 { // possibly data type properties are required, but no other standoff properties // check that there no other attributes than data type attributes and 'class' - val unsupportedAttributes: Set[String] = standoffNodeFromXML.attributes - .filterNot { attr => - (xmlToStandoffMapping.dataType.nonEmpty && xmlToStandoffMapping.dataType.get.dataTypeXMLAttribute == attr.key) || attr.key == classAttribute - } + val unsupportedAttributes: Set[String] = standoffNodeFromXML.attributes.filterNot { attr => + (xmlToStandoffMapping.dataType.nonEmpty && xmlToStandoffMapping.dataType.get.dataTypeXMLAttribute == attr.key) || attr.key == classAttribute + } .map(attr => attr.key) if (unsupportedAttributes.nonEmpty) throw BadRequestException( - s"Attributes found that are not defined in the mapping: ${unsupportedAttributes.mkString(", ")}") + s"Attributes found that are not defined in the mapping: ${unsupportedAttributes.mkString(", ")}" + ) Seq.empty[StandoffTagAttributeV2] } @@ -263,31 +276,35 @@ object StandoffTagUtilV2 { } /** - * Represents a text with standoff markup including the mapping. - * - * @param text the text as a mere sequence of characters. - * @param language the language of the text, if known. - * @param standoffTagV2 the text's standoff markup. - */ - case class TextWithStandoffTagsV2(text: String, - language: Option[String] = None, - standoffTagV2: Seq[StandoffTagV2], - mapping: GetMappingResponseV2) + * Represents a text with standoff markup including the mapping. + * + * @param text the text as a mere sequence of characters. + * @param language the language of the text, if known. + * @param standoffTagV2 the text's standoff markup. + */ + case class TextWithStandoffTagsV2( + text: String, + language: Option[String] = None, + standoffTagV2: Seq[StandoffTagV2], + mapping: GetMappingResponseV2 + ) /** - * Converts XML to a [[TextWithStandoffTagsV2]]. - * - * @param xml the XML representing text with markup. - * @param mapping the mapping used to convert XML to standoff. - * @param acceptStandoffLinksToClientIDs if `true`, allow standoff link tags to use the client's IDs for target - * resources. In a bulk import, this allows standoff links to resources - * that are to be created by the import. - * @return a [[TextWithStandoffTagsV2]]. - */ - def convertXMLtoStandoffTagV2(xml: String, - mapping: GetMappingResponseV2, - acceptStandoffLinksToClientIDs: Boolean, - log: LoggingAdapter): TextWithStandoffTagsV2 = { + * Converts XML to a [[TextWithStandoffTagsV2]]. + * + * @param xml the XML representing text with markup. + * @param mapping the mapping used to convert XML to standoff. + * @param acceptStandoffLinksToClientIDs if `true`, allow standoff link tags to use the client's IDs for target + * resources. In a bulk import, this allows standoff links to resources + * that are to be created by the import. + * @return a [[TextWithStandoffTagsV2]]. + */ + def convertXMLtoStandoffTagV2( + xml: String, + mapping: GetMappingResponseV2, + acceptStandoffLinksToClientIDs: Boolean, + log: LoggingAdapter + ): TextWithStandoffTagsV2 = { // collect all the `XMLTag` from the given mapping that require a separator // and create a `XMLTagSeparatorRequired` for each of them @@ -295,33 +312,29 @@ object StandoffTagUtilV2 { // namespace = Map("myXMLNamespace" -> Map("myXMLTagName" -> Map("myXMLClassname" -> XMLTag(...)))) val elementsSeparatorRequired: Vector[XMLTagSeparatorRequired] = mapping.mapping.namespace.flatMap { case (namespace: String, elesForNamespace: Map[String, Map[String, XMLTag]]) => - elesForNamespace.flatMap { - case (tagname: String, classWithTag: Map[String, XMLTag]) => - val tagsWithSeparator: Iterable[XMLTagSeparatorRequired] = classWithTag - .filter { - // filter out all `XMLTag` that require a separator - case (classname: String, tag: XMLTag) => - tag.separatorRequired + elesForNamespace.flatMap { case (tagname: String, classWithTag: Map[String, XMLTag]) => + val tagsWithSeparator: Iterable[XMLTagSeparatorRequired] = classWithTag.filter { + // filter out all `XMLTag` that require a separator + case (classname: String, tag: XMLTag) => + tag.separatorRequired + }.map { case (classname: String, tag: XMLTag) => + // create a `XMLTagSeparatorRequired` with the current's element + // namespace and class, if any + XMLTagSeparatorRequired( + maybeNamespace = namespace match { + // check for namespace + case `noNamespace` => None + case nspace: String => Some(nspace) + }, + tagname = tag.name, + maybeClassname = classname match { + // check for classname + case `noClass` => None + case clname: String => Some(clname) } - .map { - case (classname: String, tag: XMLTag) => - // create a `XMLTagSeparatorRequired` with the current's element - // namespace and class, if any - XMLTagSeparatorRequired( - maybeNamespace = namespace match { - // check for namespace - case `noNamespace` => None - case nspace: String => Some(nspace) - }, - tagname = tag.name, - maybeClassname = classname match { - // check for classname - case `noClass` => None - case clname: String => Some(clname) - } - ) - } - tagsWithSeparator + ) + } + tagsWithSeparator } }.toVector @@ -343,22 +356,23 @@ object StandoffTagUtilV2 { } /** - * - * Turns a sequence of [[StandoffTag]] returned by [[XMLToStandoffUtil.xml2TextWithStandoff]] into a sequence of [[StandoffTagV2]]. - * This method handles the creation of data type specific properties (e.g. for a date value) on the basis of the provided mapping. - * - * @param textWithStandoff sequence of [[StandoffTag]] returned by [[XMLToStandoffUtil.xml2TextWithStandoff]]. - * @param mappingXMLtoStandoff the mapping to be used. - * @param standoffEntities the standoff entities (classes and properties) to be used. - * @param acceptStandoffLinksToClientIDs if `true`, allow standoff link tags to use the client's IDs for target - * resources. In a bulk import, this allows standoff links to resources - * that are to be created by the import. - * @return a sequence of [[StandoffTagV2]]. - */ - private def convertXMLStandoffTagToStandoffTagV2(textWithStandoff: TextWithStandoff, - mappingXMLtoStandoff: MappingXMLtoStandoff, - standoffEntities: StandoffEntityInfoGetResponseV2, - acceptStandoffLinksToClientIDs: Boolean): Seq[StandoffTagV2] = { + * Turns a sequence of [[StandoffTag]] returned by [[XMLToStandoffUtil.xml2TextWithStandoff]] into a sequence of [[StandoffTagV2]]. + * This method handles the creation of data type specific properties (e.g. for a date value) on the basis of the provided mapping. + * + * @param textWithStandoff sequence of [[StandoffTag]] returned by [[XMLToStandoffUtil.xml2TextWithStandoff]]. + * @param mappingXMLtoStandoff the mapping to be used. + * @param standoffEntities the standoff entities (classes and properties) to be used. + * @param acceptStandoffLinksToClientIDs if `true`, allow standoff link tags to use the client's IDs for target + * resources. In a bulk import, this allows standoff links to resources + * that are to be created by the import. + * @return a sequence of [[StandoffTagV2]]. + */ + private def convertXMLStandoffTagToStandoffTagV2( + textWithStandoff: TextWithStandoff, + mappingXMLtoStandoff: MappingXMLtoStandoff, + standoffEntities: StandoffEntityInfoGetResponseV2, + acceptStandoffLinksToClientIDs: Boolean + ): Seq[StandoffTagV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // collect al the existing ids from the standoff tags @@ -374,7 +388,8 @@ object StandoffTagUtilV2 { // make sure that the internal reference starts with a '#' if (internalReference.charAt(0) != internalLinkMarker) throw BadRequestException( - s"invalid internal reference (should start with a $internalLinkMarker): '$internalReference'") + s"invalid internal reference (should start with a $internalLinkMarker): '$internalReference'" + ) val refTarget = internalReference.substring(1) @@ -403,12 +418,14 @@ object StandoffTagUtilV2 { .getOrElse( standoffNodeFromXML.tagName, throw BadRequestException( - s"the standoff class for the tag '${standoffNodeFromXML.tagName}' could not be found in the provided mapping") + s"the standoff class for the tag '${standoffNodeFromXML.tagName}' could not be found in the provided mapping" + ) ) .getOrElse( classname, throw BadRequestException( - s"the standoff class for the classname $classname in combination with the tag '${standoffNodeFromXML.tagName}' could not be found in the provided mapping") + s"the standoff class for the classname $classname in combination with the tag '${standoffNodeFromXML.tagName}' could not be found in the provided mapping" + ) ) .mapping @@ -418,7 +435,8 @@ object StandoffTagUtilV2 { val cardinalities: Map[SmartIri, KnoraCardinalityInfo] = standoffEntities.standoffClassInfoMap .getOrElse( standoffClassIri, - throw NotFoundException(s"information about standoff class $standoffClassIri was not found in ontology")) + throw NotFoundException(s"information about standoff class $standoffClassIri was not found in ontology") + ) .allCardinalities // create a standoff base tag with the information available from standoff util @@ -434,7 +452,9 @@ object StandoffTagUtilV2 { Some( stringFormatter.toSparqlEncodedString( id, - throw BadRequestException(s"XML id $id cannot be converted to a Sparql conform string"))) + throw BadRequestException(s"XML id $id cannot be converted to a Sparql conform string") + ) + ) case None => None }, startIndex = hierarchicalStandoffTag.index, @@ -454,7 +474,9 @@ object StandoffTagUtilV2 { Some( stringFormatter.toSparqlEncodedString( id, - throw BadRequestException(s"XML id $id cannot be converted to a Sparql conform string"))) + throw BadRequestException(s"XML id $id cannot be converted to a Sparql conform string") + ) + ) case None => None }, startIndex = freeStandoffTag.startIndex, @@ -466,7 +488,8 @@ object StandoffTagUtilV2 { case _ => throw InvalidStandoffException( - "StandoffUtil did neither return a HierarchicalStandoff tag nor a FreeStandoffTag") + "StandoffUtil did neither return a HierarchicalStandoff tag nor a FreeStandoffTag" + ) } // check the data type of the given standoff class @@ -481,17 +504,21 @@ object StandoffTagUtilV2 { value = stringFormatter.validateStandoffLinkResourceReference( linkString, acceptStandoffLinksToClientIDs, - throw BadRequestException(s"Invalid standoff resource reference: $linkString")) + throw BadRequestException(s"Invalid standoff resource reference: $linkString") + ) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.linkProperties - .map(_.toSmartIri) + val classSpecificProps = + cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.linkProperties + .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), @@ -509,23 +536,29 @@ object StandoffTagUtilV2 { case Some(StandoffDataTypeClasses.StandoffInternalReferenceTag) => val internalReferenceString: String = - getDataTypeAttribute(standoffDefFromMapping, - StandoffDataTypeClasses.StandoffInternalReferenceTag, - standoffNodeFromXML) + getDataTypeAttribute( + standoffDefFromMapping, + StandoffDataTypeClasses.StandoffInternalReferenceTag, + standoffNodeFromXML + ) val internalReference = StandoffTagInternalReferenceAttributeV2( standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasInternalReference.toSmartIri, value = getTargetIDFromInternalReference(internalReferenceString) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.internalReferenceProperties + val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map( + _.toSmartIri + ) -- StandoffProperties.internalReferenceProperties .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffInternalReferenceTag), @@ -551,14 +584,17 @@ object StandoffTagUtilV2 { stringFormatter.validateColor(colorString, throw BadRequestException(s"Color invalid: $colorString")) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.colorProperties - .map(_.toSmartIri) + val classSpecificProps = + cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.colorProperties + .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffColorTag), @@ -584,14 +620,17 @@ object StandoffTagUtilV2 { stringFormatter.validateAndEscapeIri(uriString, throw BadRequestException(s"URI invalid: $uriString")) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.uriProperties - .map(_.toSmartIri) + val classSpecificProps = + cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.uriProperties + .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffUriTag), @@ -608,24 +647,32 @@ object StandoffTagUtilV2 { ) case Some(StandoffDataTypeClasses.StandoffIntegerTag) => - val integerString: String = getDataTypeAttribute(standoffDefFromMapping, - StandoffDataTypeClasses.StandoffIntegerTag, - standoffNodeFromXML) + val integerString: String = getDataTypeAttribute( + standoffDefFromMapping, + StandoffDataTypeClasses.StandoffIntegerTag, + standoffNodeFromXML + ) val integerValue = StandoffTagIntegerAttributeV2( standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasInteger.toSmartIri, - value = stringFormatter.validateInt(integerString, - throw BadRequestException(s"Integer value invalid: $integerString")) + value = stringFormatter.validateInt( + integerString, + throw BadRequestException(s"Integer value invalid: $integerString") + ) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.integerProperties + val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map( + _.toSmartIri + ) -- StandoffProperties.integerProperties .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffIntegerTag), @@ -642,25 +689,32 @@ object StandoffTagUtilV2 { ) case Some(StandoffDataTypeClasses.StandoffDecimalTag) => - val decimalString: String = getDataTypeAttribute(standoffDefFromMapping, - StandoffDataTypeClasses.StandoffDecimalTag, - standoffNodeFromXML) + val decimalString: String = getDataTypeAttribute( + standoffDefFromMapping, + StandoffDataTypeClasses.StandoffDecimalTag, + standoffNodeFromXML + ) val decimalValue = StandoffTagDecimalAttributeV2( standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasDecimal.toSmartIri, - value = - stringFormatter.validateBigDecimal(decimalString, - throw BadRequestException(s"Decimal value invalid: $decimalString")) + value = stringFormatter.validateBigDecimal( + decimalString, + throw BadRequestException(s"Decimal value invalid: $decimalString") + ) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.decimalProperties + val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map( + _.toSmartIri + ) -- StandoffProperties.decimalProperties .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffDecimalTag), @@ -677,24 +731,32 @@ object StandoffTagUtilV2 { ) case Some(StandoffDataTypeClasses.StandoffBooleanTag) => - val booleanString: String = getDataTypeAttribute(standoffDefFromMapping, - StandoffDataTypeClasses.StandoffBooleanTag, - standoffNodeFromXML) + val booleanString: String = getDataTypeAttribute( + standoffDefFromMapping, + StandoffDataTypeClasses.StandoffBooleanTag, + standoffNodeFromXML + ) val booleanValue = StandoffTagBooleanAttributeV2( standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasBoolean.toSmartIri, - value = stringFormatter.validateBoolean(booleanString, - throw BadRequestException(s"Boolean value invalid: $booleanString")) + value = stringFormatter.validateBoolean( + booleanString, + throw BadRequestException(s"Boolean value invalid: $booleanString") + ) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.booleanProperties + val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map( + _.toSmartIri + ) -- StandoffProperties.booleanProperties .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffBooleanTag), @@ -711,39 +773,48 @@ object StandoffTagUtilV2 { ) case Some(StandoffDataTypeClasses.StandoffIntervalTag) => - val intervalString: String = getDataTypeAttribute(standoffDefFromMapping, - StandoffDataTypeClasses.StandoffIntervalTag, - standoffNodeFromXML) + val intervalString: String = getDataTypeAttribute( + standoffDefFromMapping, + StandoffDataTypeClasses.StandoffIntervalTag, + standoffNodeFromXML + ) // interval String contains two decimals separated by a comma val interval: Array[String] = intervalString.split(",") if (interval.length != 2) { throw BadRequestException( - s"interval string $intervalString is invalid, it should contain two decimals separated by a comma") + s"interval string $intervalString is invalid, it should contain two decimals separated by a comma" + ) } val intervalStart = StandoffTagDecimalAttributeV2( standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasIntervalStart.toSmartIri, - value = - stringFormatter.validateBigDecimal(interval(0), - throw BadRequestException(s"Decimal value invalid: ${interval(0)}")) + value = stringFormatter.validateBigDecimal( + interval(0), + throw BadRequestException(s"Decimal value invalid: ${interval(0)}") + ) ) val intervalEnd = StandoffTagDecimalAttributeV2( standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasIntervalEnd.toSmartIri, - value = - stringFormatter.validateBigDecimal(interval(1), - throw BadRequestException(s"Decimal value invalid: ${interval(1)}")) + value = stringFormatter.validateBigDecimal( + interval(1), + throw BadRequestException(s"Decimal value invalid: ${interval(1)}") + ) ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.intervalProperties + val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map( + _.toSmartIri + ) -- StandoffProperties.intervalProperties .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffIntervalTag), @@ -763,20 +834,26 @@ object StandoffTagUtilV2 { val timeStampString: String = getDataTypeAttribute(standoffDefFromMapping, StandoffDataTypeClasses.StandoffTimeTag, standoffNodeFromXML) val timeStamp: Instant = - stringFormatter.xsdDateTimeStampToInstant(timeStampString, - throw BadRequestException(s"Invalid timestamp: $timeStampString")) - val timeStampAttr = StandoffTagTimeAttributeV2(standoffPropertyIri = - OntologyConstants.KnoraBase.ValueHasTimeStamp.toSmartIri, - value = timeStamp) + stringFormatter.xsdDateTimeStampToInstant( + timeStampString, + throw BadRequestException(s"Invalid timestamp: $timeStampString") + ) + val timeStampAttr = StandoffTagTimeAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasTimeStamp.toSmartIri, + value = timeStamp + ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.timeProperties - .map(_.toSmartIri) + val classSpecificProps = + cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.timeProperties + .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffTimeTag), @@ -798,36 +875,44 @@ object StandoffTagUtilV2 { val dateValue = DateUtilV1.createJDNValueV1FromDateString(dateString) - val dateCalendar = StandoffTagStringAttributeV2(standoffPropertyIri = - OntologyConstants.KnoraBase.ValueHasCalendar.toSmartIri, - value = dateValue.calendar.toString) + val dateCalendar = StandoffTagStringAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasCalendar.toSmartIri, + value = dateValue.calendar.toString + ) - val dateStart = StandoffTagIntegerAttributeV2(standoffPropertyIri = - OntologyConstants.KnoraBase.ValueHasStartJDN.toSmartIri, - value = dateValue.dateval1) + val dateStart = StandoffTagIntegerAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasStartJDN.toSmartIri, + value = dateValue.dateval1 + ) - val dateEnd = StandoffTagIntegerAttributeV2(standoffPropertyIri = - OntologyConstants.KnoraBase.ValueHasEndJDN.toSmartIri, - value = dateValue.dateval2) + val dateEnd = StandoffTagIntegerAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasEndJDN.toSmartIri, + value = dateValue.dateval2 + ) val dateStartPrecision = - StandoffTagStringAttributeV2(standoffPropertyIri = - OntologyConstants.KnoraBase.ValueHasStartPrecision.toSmartIri, - value = dateValue.dateprecision1.toString) + StandoffTagStringAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasStartPrecision.toSmartIri, + value = dateValue.dateprecision1.toString + ) val dateEndPrecision = - StandoffTagStringAttributeV2(standoffPropertyIri = - OntologyConstants.KnoraBase.ValueHasEndPrecision.toSmartIri, - value = dateValue.dateprecision2.toString) + StandoffTagStringAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.ValueHasEndPrecision.toSmartIri, + value = dateValue.dateprecision2.toString + ) - val classSpecificProps = cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.dateProperties - .map(_.toSmartIri) + val classSpecificProps = + cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.dateProperties + .map(_.toSmartIri) - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) StandoffTagV2( dataType = Some(StandoffDataTypeClasses.StandoffDateTag), @@ -845,15 +930,16 @@ object StandoffTagUtilV2 { case None => // ignore the system properties since they are provided by StandoffUtil - val classSpecificProps - : Map[SmartIri, KnoraCardinalityInfo] = cardinalities -- StandoffProperties.systemProperties.map( - _.toSmartIri) - - val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes(standoffDefFromMapping, - classSpecificProps, - existingXMLIDs, - standoffNodeFromXML, - standoffEntities.standoffPropertyInfoMap) + val classSpecificProps: Map[SmartIri, KnoraCardinalityInfo] = + cardinalities -- StandoffProperties.systemProperties.map(_.toSmartIri) + + val attributesV2: Seq[StandoffTagAttributeV2] = createAttributes( + standoffDefFromMapping, + classSpecificProps, + existingXMLIDs, + standoffNodeFromXML, + standoffEntities.standoffPropertyInfoMap + ) standoffBaseTagV2.copy( attributes = attributesV2 @@ -861,31 +947,34 @@ object StandoffTagUtilV2 { case unknownDataType => throw InconsistentRepositoryDataException( - s"the triplestore returned the data type $unknownDataType for $standoffClassIri that could be handled") + s"the triplestore returned the data type $unknownDataType for $standoffClassIri that could be handled" + ) } } } // maps a standoff class to an XML tag with attributes - case class XMLTagItem(namespace: String, - tagname: String, - classname: String, - tagItem: XMLTag, - attributes: Map[IRI, XMLAttrItem]) + case class XMLTagItem( + namespace: String, + tagname: String, + classname: String, + tagItem: XMLTag, + attributes: Map[IRI, XMLAttrItem] + ) // maps a standoff property to XML attributes case class XMLAttrItem(namespace: String, attrname: String) /** - * Inverts a [[MappingXMLtoStandoff]] and makes standoff class Iris keys. - * This is makes it easier to map standoff classes back to XML tags (recreating XML from standoff). - * - * This method also checks for duplicate usage of standoff classes and properties in the attribute mapping of a tag. - * - * @param mappingXMLtoStandoff mapping from XML to standoff. - * @return a Map standoff class Iris to [[XMLTagItem]]. - */ + * Inverts a [[MappingXMLtoStandoff]] and makes standoff class Iris keys. + * This is makes it easier to map standoff classes back to XML tags (recreating XML from standoff). + * + * This method also checks for duplicate usage of standoff classes and properties in the attribute mapping of a tag. + * + * @param mappingXMLtoStandoff mapping from XML to standoff. + * @return a Map standoff class Iris to [[XMLTagItem]]. + */ def invertXMLToStandoffMapping(mappingXMLtoStandoff: MappingXMLtoStandoff): Map[IRI, XMLTagItem] = { // check for duplicate standoff class Iris @@ -901,41 +990,41 @@ object StandoffTagUtilV2 { mappingXMLtoStandoff.namespace.flatMap { case (tagNamespace: String, tagMapping: Map[String, Map[String, XMLTag]]) => - tagMapping.flatMap { - case (tagname: String, classnameMapping: Map[String, XMLTag]) => - classnameMapping.map { - case (classname: String, tagItem: XMLTag) => - // collect all the property Iris defined in the attributes for the current tag - // over all namespaces - val propIris: Iterable[IRI] = tagItem.mapping.attributesToProps.values.flatten.map { - case (attrName, propIri) => - propIri - } + tagMapping.flatMap { case (tagname: String, classnameMapping: Map[String, XMLTag]) => + classnameMapping.map { case (classname: String, tagItem: XMLTag) => + // collect all the property Iris defined in the attributes for the current tag + // over all namespaces + val propIris: Iterable[IRI] = tagItem.mapping.attributesToProps.values.flatten.map { + case (attrName, propIri) => + propIri + } - // check for duplicate property Iris - if (propIris.size != propIris.toSet.size) { - throw BadRequestException( - s"the same property IRI is used more than once for the attributes mapping for tag $tagname") - } + // check for duplicate property Iris + if (propIris.size != propIris.toSet.size) { + throw BadRequestException( + s"the same property IRI is used more than once for the attributes mapping for tag $tagname" + ) + } - // inverts the mapping and makes standoff property Iris keys (for attributes) - // this is makes it easier to map standoff properties back to XML attributes - val attrItems: Map[IRI, XMLAttrItem] = tagItem.mapping.attributesToProps.flatMap { - case (attrNamespace: String, attrMappings: Map[String, IRI]) => - attrMappings.map { - case (attrName, propIri) => - propIri -> XMLAttrItem(attrNamespace, attrName) - } + // inverts the mapping and makes standoff property Iris keys (for attributes) + // this is makes it easier to map standoff properties back to XML attributes + val attrItems: Map[IRI, XMLAttrItem] = tagItem.mapping.attributesToProps.flatMap { + case (attrNamespace: String, attrMappings: Map[String, IRI]) => + attrMappings.map { case (attrName, propIri) => + propIri -> XMLAttrItem(attrNamespace, attrName) } - - // standoff class IRI -> XMLTagItem(... attributes -> attrItems) - tagItem.mapping.standoffClassIri -> XMLTagItem(namespace = tagNamespace, - tagname = tagname, - classname = classname, - tagItem = tagItem, - attributes = attrItems) } + // standoff class IRI -> XMLTagItem(... attributes -> attrItems) + tagItem.mapping.standoffClassIri -> XMLTagItem( + namespace = tagNamespace, + tagname = tagname, + classname = classname, + tagItem = tagItem, + attributes = attrItems + ) + } + } } @@ -943,20 +1032,20 @@ object StandoffTagUtilV2 { } /** - * Creates a sequence of [[StandoffTagV2]] from the given standoff nodes resulting from a SPARQL CONSTRUCT query. - * - * @param standoffAssertions standoff assertions to be converted into [[StandoffTagV2]] objects. - * @return a sequence of [[StandoffTagV2]] objects. - */ - def createStandoffTagsV2FromConstructResults(standoffAssertions: Map[IRI, Map[SmartIri, LiteralV2]], - responderManager: ActorRef, - requestingUser: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Vector[StandoffTagV2]] = { + * Creates a sequence of [[StandoffTagV2]] from the given standoff nodes resulting from a SPARQL CONSTRUCT query. + * + * @param standoffAssertions standoff assertions to be converted into [[StandoffTagV2]] objects. + * @return a sequence of [[StandoffTagV2]] objects. + */ + def createStandoffTagsV2FromConstructResults( + standoffAssertions: Map[IRI, Map[SmartIri, LiteralV2]], + responderManager: ActorRef, + requestingUser: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Vector[StandoffTagV2]] = { val unwrappedStandoffAssertions: Map[IRI, Map[IRI, String]] = standoffAssertions.map { case (standoffTagIri, standoffTagAssertions: Map[SmartIri, LiteralV2]) => - standoffTagIri -> standoffTagAssertions.map { - case (predicateIri: SmartIri, literal: LiteralV2) => predicateIri.toString -> literal.toString + standoffTagIri -> standoffTagAssertions.map { case (predicateIri: SmartIri, literal: LiteralV2) => + predicateIri.toString -> literal.toString } } @@ -968,16 +1057,16 @@ object StandoffTagUtilV2 { } /** - * Creates a sequence of [[StandoffTagV2]] from the given standoff nodes resulting from a SPARQL SELECT query. - * - * @param standoffAssertions standoff assertions to be converted into [[StandoffTagV2]] objects. - * @return a sequence of [[StandoffTagV2]] objects. - */ - def createStandoffTagsV2FromSelectResults(standoffAssertions: Map[IRI, Map[IRI, String]], - responderManager: ActorRef, - requestingUser: UserADM)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Vector[StandoffTagV2]] = { + * Creates a sequence of [[StandoffTagV2]] from the given standoff nodes resulting from a SPARQL SELECT query. + * + * @param standoffAssertions standoff assertions to be converted into [[StandoffTagV2]] objects. + * @return a sequence of [[StandoffTagV2]] objects. + */ + def createStandoffTagsV2FromSelectResults( + standoffAssertions: Map[IRI, Map[IRI, String]], + responderManager: ActorRef, + requestingUser: UserADM + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Vector[StandoffTagV2]] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val standoffClassIris: Set[SmartIri] = standoffAssertions.map { @@ -994,152 +1083,159 @@ object StandoffTagUtilV2 { standoffEntities: StandoffEntityInfoGetResponseV2 <- (responderManager ? StandoffEntityInfoGetRequestV2( standoffClassIris = standoffClassIris, standoffPropertyIris = standoffPropertyIris, - requestingUser = requestingUser)).mapTo[StandoffEntityInfoGetResponseV2] + requestingUser = requestingUser + )).mapTo[StandoffEntityInfoGetResponseV2] - standoffTags = standoffAssertions - .map { - case (standoffTagIri: IRI, standoffTagAssertions: Map[IRI, String]) => - val standoffTagSmartIri: SmartIri = standoffTagIri.toSmartIri + standoffTags = standoffAssertions.map { case (standoffTagIri: IRI, standoffTagAssertions: Map[IRI, String]) => + val standoffTagSmartIri: SmartIri = standoffTagIri.toSmartIri - if (!standoffTagSmartIri.isKnoraStandoffIri) { - throw InconsistentRepositoryDataException(s"Invalid standoff tag IRI: $standoffTagIri") - } + if (!standoffTagSmartIri.isKnoraStandoffIri) { + throw InconsistentRepositoryDataException(s"Invalid standoff tag IRI: $standoffTagIri") + } - // The start index in the tag's IRI should match the one in its assertions. + // The start index in the tag's IRI should match the one in its assertions. - val startIndexFromIri: Int = standoffTagSmartIri.getStandoffStartIndex.get - val startIndexFromAssertions: Int = - standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasStartIndex).toInt + val startIndexFromIri: Int = standoffTagSmartIri.getStandoffStartIndex.get + val startIndexFromAssertions: Int = + standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasStartIndex).toInt - if (startIndexFromAssertions != startIndexFromIri) { - throw InconsistentRepositoryDataException( - s"Standoff tag $standoffTagIri has start index $startIndexFromAssertions (expected $startIndexFromIri)") - } + if (startIndexFromAssertions != startIndexFromIri) { + throw InconsistentRepositoryDataException( + s"Standoff tag $standoffTagIri has start index $startIndexFromAssertions (expected $startIndexFromIri)" + ) + } + + // create a sequence of `StandoffTagAttributeV2` from the given attributes + val attributes: Seq[StandoffTagAttributeV2] = + (standoffTagAssertions -- StandoffProperties.systemProperties - OntologyConstants.Rdf.Type).map { + case (propIri: IRI, value) => + val propSmartIri = propIri.toSmartIri + val propDef: ReadPropertyInfoV2 = standoffEntities.standoffPropertyInfoMap(propSmartIri) + val propPredicates: Map[SmartIri, PredicateInfoV2] = propDef.entityInfoContent.predicates + + // check if the given property has an object type constraint (linking property) or an object data type constraint + if (propPredicates.contains(OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri)) { + + // it is a linking property + // check if it refers to a resource or a standoff node - // create a sequence of `StandoffTagAttributeV2` from the given attributes - val attributes: Seq[StandoffTagAttributeV2] = - (standoffTagAssertions -- StandoffProperties.systemProperties - OntologyConstants.Rdf.Type).map { - case (propIri: IRI, value) => - val propSmartIri = propIri.toSmartIri - val propDef: ReadPropertyInfoV2 = standoffEntities.standoffPropertyInfoMap(propSmartIri) - val propPredicates: Map[SmartIri, PredicateInfoV2] = propDef.entityInfoContent.predicates - - // check if the given property has an object type constraint (linking property) or an object data type constraint - if (propPredicates.contains(OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri)) { - - // it is a linking property - // check if it refers to a resource or a standoff node - - if (propDef.isStandoffInternalReferenceProperty) { - // it refers to a standoff node, recreate the original id - - // value points to another standoff node - - // If a v2 SPARQL template was used, the XML ID is in this standoff tag. - val originalId: String = - standoffTagAssertions.get(OntologyConstants.KnoraBase.TargetHasOriginalXMLID) match { - case Some(targetXmlID) => targetXmlID - - case None => - // If a v1 SPARQL template was used, we have to get the target node and to get its XML ID. - standoffAssertions(value).getOrElse( - OntologyConstants.KnoraBase.StandoffTagHasOriginalXMLID, - throw InconsistentRepositoryDataException( - s"referred standoff $value node has no original XML id") - ) - } - - // recreate the original id reference - StandoffTagInternalReferenceAttributeV2(standoffPropertyIri = propSmartIri, value = originalId) - } else { - // it refers to a knora resource - StandoffTagIriAttributeV2(standoffPropertyIri = propSmartIri, value = value) + if (propDef.isStandoffInternalReferenceProperty) { + // it refers to a standoff node, recreate the original id + + // value points to another standoff node + + // If a v2 SPARQL template was used, the XML ID is in this standoff tag. + val originalId: String = + standoffTagAssertions.get(OntologyConstants.KnoraBase.TargetHasOriginalXMLID) match { + case Some(targetXmlID) => targetXmlID + + case None => + // If a v1 SPARQL template was used, we have to get the target node and to get its XML ID. + standoffAssertions(value).getOrElse( + OntologyConstants.KnoraBase.StandoffTagHasOriginalXMLID, + throw InconsistentRepositoryDataException( + s"referred standoff $value node has no original XML id" + ) + ) } - } else if (propPredicates.contains(OntologyConstants.KnoraBase.ObjectDatatypeConstraint.toSmartIri)) { - // it is a data type property (literal) - val propDataType: PredicateInfoV2 = - propPredicates(OntologyConstants.KnoraBase.ObjectDatatypeConstraint.toSmartIri) + // recreate the original id reference + StandoffTagInternalReferenceAttributeV2(standoffPropertyIri = propSmartIri, value = originalId) + } else { + // it refers to a knora resource + StandoffTagIriAttributeV2(standoffPropertyIri = propSmartIri, value = value) + } + } else if (propPredicates.contains(OntologyConstants.KnoraBase.ObjectDatatypeConstraint.toSmartIri)) { - propDataType.objects.headOption match { - case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.String))) => - StandoffTagStringAttributeV2(standoffPropertyIri = propSmartIri, value = value) + // it is a data type property (literal) + val propDataType: PredicateInfoV2 = + propPredicates(OntologyConstants.KnoraBase.ObjectDatatypeConstraint.toSmartIri) - case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Integer))) => - StandoffTagIntegerAttributeV2(standoffPropertyIri = propSmartIri, value = value.toInt) + propDataType.objects.headOption match { + case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.String))) => + StandoffTagStringAttributeV2(standoffPropertyIri = propSmartIri, value = value) - case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Decimal))) => - StandoffTagDecimalAttributeV2(standoffPropertyIri = propSmartIri, value = BigDecimal(value)) + case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Integer))) => + StandoffTagIntegerAttributeV2(standoffPropertyIri = propSmartIri, value = value.toInt) - case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Boolean))) => - StandoffTagBooleanAttributeV2(standoffPropertyIri = propSmartIri, value = value.toBoolean) + case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Decimal))) => + StandoffTagDecimalAttributeV2(standoffPropertyIri = propSmartIri, value = BigDecimal(value)) - case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.DateTime))) | - Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.DateTimeStamp))) => - val timeStamp = stringFormatter.xsdDateTimeStampToInstant( - value, - throw DataConversionException(s"Couldn't parse timestamp: $value")) - StandoffTagTimeAttributeV2(standoffPropertyIri = propSmartIri, value = timeStamp) + case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Boolean))) => + StandoffTagBooleanAttributeV2(standoffPropertyIri = propSmartIri, value = value.toBoolean) - case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Uri))) => - StandoffTagUriAttributeV2(standoffPropertyIri = propSmartIri, value = value) + case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.DateTime))) | + Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.DateTimeStamp))) => + val timeStamp = stringFormatter.xsdDateTimeStampToInstant( + value, + throw DataConversionException(s"Couldn't parse timestamp: $value") + ) + StandoffTagTimeAttributeV2(standoffPropertyIri = propSmartIri, value = timeStamp) - case None => - throw InconsistentRepositoryDataException( - s"did not find ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} for $propIri") + case Some(SmartIriLiteralV2(SmartIri(OntologyConstants.Xsd.Uri))) => + StandoffTagUriAttributeV2(standoffPropertyIri = propSmartIri, value = value) - case other => - throw InconsistentRepositoryDataException( - s"triplestore returned unknown ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} '$other' for $propIri") + case None => + throw InconsistentRepositoryDataException( + s"did not find ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} for $propIri" + ) - } - } else { + case other => throw InconsistentRepositoryDataException( - s"no object class or data type constraint found for property '$propIri'") - } - - }.toVector - - StandoffTagV2( - standoffTagClassIri = standoffTagAssertions(OntologyConstants.Rdf.Type).toSmartIri, - startPosition = standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasStart).toInt, - endPosition = standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasEnd).toInt, - dataType = standoffEntities - .standoffClassInfoMap(standoffTagAssertions(OntologyConstants.Rdf.Type).toSmartIri) - .standoffDataType, - startIndex = startIndexFromAssertions, - endIndex = standoffTagAssertions.get(OntologyConstants.KnoraBase.StandoffTagHasEndIndex).map(_.toInt), - uuid = stringFormatter.decodeUuid(standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasUUID)), - originalXMLID = standoffTagAssertions.get(OntologyConstants.KnoraBase.StandoffTagHasOriginalXMLID), - startParentIndex = standoffTagAssertions - .get(OntologyConstants.KnoraBase.StandoffTagHasStartParent) - .flatMap(_.toSmartIri.getStandoffStartIndex), - endParentIndex = standoffTagAssertions - .get(OntologyConstants.KnoraBase.StandoffTagHasEndParent) - .flatMap(_.toSmartIri.getStandoffStartIndex), - attributes = attributes - ) - } - .toVector + s"triplestore returned unknown ${OntologyConstants.KnoraBase.ObjectDatatypeConstraint} '$other' for $propIri" + ) + + } + } else { + throw InconsistentRepositoryDataException( + s"no object class or data type constraint found for property '$propIri'" + ) + } + + }.toVector + + StandoffTagV2( + standoffTagClassIri = standoffTagAssertions(OntologyConstants.Rdf.Type).toSmartIri, + startPosition = standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasStart).toInt, + endPosition = standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasEnd).toInt, + dataType = standoffEntities + .standoffClassInfoMap(standoffTagAssertions(OntologyConstants.Rdf.Type).toSmartIri) + .standoffDataType, + startIndex = startIndexFromAssertions, + endIndex = standoffTagAssertions.get(OntologyConstants.KnoraBase.StandoffTagHasEndIndex).map(_.toInt), + uuid = stringFormatter.decodeUuid(standoffTagAssertions(OntologyConstants.KnoraBase.StandoffTagHasUUID)), + originalXMLID = standoffTagAssertions.get(OntologyConstants.KnoraBase.StandoffTagHasOriginalXMLID), + startParentIndex = standoffTagAssertions + .get(OntologyConstants.KnoraBase.StandoffTagHasStartParent) + .flatMap(_.toSmartIri.getStandoffStartIndex), + endParentIndex = standoffTagAssertions + .get(OntologyConstants.KnoraBase.StandoffTagHasEndParent) + .flatMap(_.toSmartIri.getStandoffStartIndex), + attributes = attributes + ) + }.toVector .sortBy(_.startIndex) } yield standoffTags } /** - * Converts a sequence of [[StandoffTagAttributeV2]] to a sequence of [[StandoffTagAttribute]]. - * - * @param mapping the mapping used to convert standoff property IRIs to XML attribute names. - * @param attributes the standoff properties to be converted to XML attributes. - * @return a sequence of [[StandoffTagAttribute]]. - */ - private def convertStandoffAttributeTags(mapping: Map[IRI, XMLAttrItem], - attributes: Seq[StandoffTagAttributeV2]): Seq[StandoffTagAttribute] = { + * Converts a sequence of [[StandoffTagAttributeV2]] to a sequence of [[StandoffTagAttribute]]. + * + * @param mapping the mapping used to convert standoff property IRIs to XML attribute names. + * @param attributes the standoff properties to be converted to XML attributes. + * @return a sequence of [[StandoffTagAttribute]]. + */ + private def convertStandoffAttributeTags( + mapping: Map[IRI, XMLAttrItem], + attributes: Seq[StandoffTagAttributeV2] + ): Seq[StandoffTagAttribute] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance attributes.map { attr => val attrItem: XMLAttrItem = mapping.getOrElse( attr.standoffPropertyIri.toString, - throw NotFoundException(s"property IRI ${attr.standoffPropertyIri} could not be found in mapping")) + throw NotFoundException(s"property IRI ${attr.standoffPropertyIri} could not be found in mapping") + ) StandoffTagAttribute( key = attrItem.attrname, @@ -1153,16 +1249,18 @@ object StandoffTagUtilV2 { } /** - * Converts a text value with standoff to an XML String. - * - * @param utf8str the string representation of the text value (`valueHasString`). - * @param standoff the standoff representing the markup. - * @param mappingXMLtoStandoff the mapping used to convert standoff to XML markup. - * @return a String representing an XML document. - */ - def convertStandoffTagV2ToXML(utf8str: String, - standoff: Seq[StandoffTagV2], - mappingXMLtoStandoff: MappingXMLtoStandoff): String = { + * Converts a text value with standoff to an XML String. + * + * @param utf8str the string representation of the text value (`valueHasString`). + * @param standoff the standoff representing the markup. + * @param mappingXMLtoStandoff the mapping used to convert standoff to XML markup. + * @return a String representing an XML document. + */ + def convertStandoffTagV2ToXML( + utf8str: String, + standoff: Seq[StandoffTagV2], + mappingXMLtoStandoff: MappingXMLtoStandoff + ): String = { // inverts the mapping and makes standoff class Iris keys (for tags) val mappingStandoffToXML: Map[IRI, XMLTagItem] = invertXMLToStandoffMapping(mappingXMLtoStandoff) @@ -1172,15 +1270,19 @@ object StandoffTagUtilV2 { val standoffTags: Seq[StandoffTag] = standoff.map { standoffTagV2: StandoffTagV2 => val xmlItemForStandoffClass: XMLTagItem = mappingStandoffToXML.getOrElse( standoffTagV2.standoffTagClassIri.toString, - throw NotFoundException(s"standoff class IRI ${standoffTagV2.standoffTagClassIri} not found in mapping")) + throw NotFoundException(s"standoff class IRI ${standoffTagV2.standoffTagClassIri} not found in mapping") + ) // recreate data type specific attributes (optional) val attributes: Seq[StandoffTagAttribute] = standoffTagV2.dataType match { case Some(StandoffDataTypeClasses.StandoffLinkTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val linkIri = standoffTagV2.attributes @@ -1189,17 +1291,21 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.linkProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.linkProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = linkIri, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = linkIri, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffInternalReferenceTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val internalRefTarget = standoffTagV2.attributes @@ -1208,17 +1314,25 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.internalReferenceProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.internalReferenceProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute( key = dataTypeAttrName, value = StandoffTagUtilV2.internalLinkMarker + internalRefTarget, - xmlNamespace = None) + xmlNamespace = None + ) case Some(StandoffDataTypeClasses.StandoffColorTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val colorString = standoffTagV2.attributes @@ -1227,17 +1341,21 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.colorProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.colorProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = colorString, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = colorString, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffUriTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val uriRef = standoffTagV2.attributes @@ -1246,17 +1364,21 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.uriProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.uriProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = uriRef, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = uriRef, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffIntegerTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val integerString = standoffTagV2.attributes @@ -1265,17 +1387,21 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.integerProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.integerProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = integerString, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = integerString, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffDecimalTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val decimalString = standoffTagV2.attributes @@ -1284,17 +1410,21 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.decimalProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.decimalProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = decimalString, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = decimalString, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffBooleanTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val booleanString = standoffTagV2.attributes @@ -1303,17 +1433,21 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.booleanProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.booleanProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = booleanString, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = booleanString, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffIntervalTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val intervalString = Vector( @@ -1328,17 +1462,21 @@ object StandoffTagUtilV2 { ).mkString(",") val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.intervalProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.intervalProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = intervalString, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = intervalString, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffTimeTag) => val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val timeString = standoffTagV2.attributes @@ -1347,25 +1485,30 @@ object StandoffTagUtilV2 { .stringValue val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.timeProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.timeProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( - key = dataTypeAttrName, - value = timeString, - xmlNamespace = None) + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute(key = dataTypeAttrName, value = timeString, xmlNamespace = None) case Some(StandoffDataTypeClasses.StandoffDateTag) => // create one attribute from date properties val dataTypeAttrName = xmlItemForStandoffClass.tagItem.mapping.dataType - .getOrElse(throw NotFoundException( - s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}")) + .getOrElse( + throw NotFoundException( + s"data type attribute not found in mapping for ${xmlItemForStandoffClass.tagname}" + ) + ) .dataTypeXMLAttribute val calendar = KnoraCalendarV1.lookup( standoffTagV2.attributes .find(_.standoffPropertyIri.toString == OntologyConstants.KnoraBase.ValueHasCalendar) .get - .stringValue) + .stringValue + ) val julianDayCountValueV1: UpdateValueV1 = JulianDayNumberValueV1( dateval1 = standoffTagV2.attributes @@ -1382,28 +1525,36 @@ object StandoffTagUtilV2 { standoffTagV2.attributes .find(_.standoffPropertyIri.toString == OntologyConstants.KnoraBase.ValueHasStartPrecision) .get - .stringValue), + .stringValue + ), dateprecision2 = KnoraPrecisionV1.lookup( standoffTagV2.attributes .find(_.standoffPropertyIri.toString == OntologyConstants.KnoraBase.ValueHasEndPrecision) .get - .stringValue), + .stringValue + ), calendar = calendar ) val conventionalAttributes = standoffTagV2.attributes.filterNot(attr => - StandoffProperties.dateProperties.contains(attr.standoffPropertyIri.toString)) + StandoffProperties.dateProperties.contains(attr.standoffPropertyIri.toString) + ) - convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, conventionalAttributes) :+ StandoffTagAttribute( + convertStandoffAttributeTags( + xmlItemForStandoffClass.attributes, + conventionalAttributes + ) :+ StandoffTagAttribute( key = dataTypeAttrName, value = Vector(calendar.toString, julianDayCountValueV1.toString).mkString(":"), - xmlNamespace = None) + xmlNamespace = None + ) case None => convertStandoffAttributeTags(xmlItemForStandoffClass.attributes, standoffTagV2.attributes) case unknownDataType => throw InconsistentRepositoryDataException( - s"the triplestore returned an unknown data type for ${standoffTagV2.standoffTagClassIri} that could not be handled") + s"the triplestore returned an unknown data type for ${standoffTagV2.standoffTagClassIri} that could not be handled" + ) } // check in mapping if the XML element has an attribute class to be recreated @@ -1435,7 +1586,8 @@ object StandoffTagUtilV2 { endPosition = standoffTagV2.endPosition, startIndex = standoffTagV2.startIndex, endIndex = standoffTagV2.endIndex.getOrElse( - throw InconsistentRepositoryDataException(s"end index is missing for a free standoff tag")), + throw InconsistentRepositoryDataException(s"end index is missing for a free standoff tag") + ), startParentIndex = standoffTagV2.startParentIndex, endParentIndex = standoffTagV2.endParentIndex, attributes = attributesWithClass.toSet @@ -1465,53 +1617,52 @@ object StandoffTagUtilV2 { } /** - * Given a sequence of standoff tags from a text value, makes a collection that can be compared with standoff from - * another text value. - * - * @param standoff the standoff that needs to be compared. - * @return a sequence of sets of tags that have the same start index, ordered by start index. All tags have their - * UUIDs replaced with empty strings. - */ - def makeComparableStandoffCollection(standoff: Seq[StandoffTagV2]): Vector[Set[StandoffTagV2]] = { + * Given a sequence of standoff tags from a text value, makes a collection that can be compared with standoff from + * another text value. + * + * @param standoff the standoff that needs to be compared. + * @return a sequence of sets of tags that have the same start index, ordered by start index. All tags have their + * UUIDs replaced with empty strings. + */ + def makeComparableStandoffCollection(standoff: Seq[StandoffTagV2]): Vector[Set[StandoffTagV2]] = // Since multiple tags could have the same index (e.g. if the standoff editor that // generated them doesn't use indexes), we first group them into sets of tags that have the same index, // then make a sequence of sets of tags sorted by index. - standoff .groupBy(_.startIndex) - .map { - case (index: Int, standoffForIndex: Seq[StandoffTagV2]) => - // Set each tag's UUID to a fixed UUID (because these should not affect the comparison), - // and sort its attributes by standoff property IRI. - val comparableTags = standoffForIndex.map { tag => - tag.copy( - uuid = fixedUuid, - attributes = tag.attributes.sortBy(_.standoffPropertyIri) - ) - } + .map { case (index: Int, standoffForIndex: Seq[StandoffTagV2]) => + // Set each tag's UUID to a fixed UUID (because these should not affect the comparison), + // and sort its attributes by standoff property IRI. + val comparableTags = standoffForIndex.map { tag => + tag.copy( + uuid = fixedUuid, + attributes = tag.attributes.sortBy(_.standoffPropertyIri) + ) + } - index -> comparableTags.toSet + index -> comparableTags.toSet } .toVector - .sortBy { - case (index: Int, standoffForIndex: Set[StandoffTagV2]) => index + .sortBy { case (index: Int, standoffForIndex: Set[StandoffTagV2]) => + index } - .map { - case (index: Int, standoffForIndex: Set[StandoffTagV2]) => standoffForIndex + .map { case (index: Int, standoffForIndex: Set[StandoffTagV2]) => + standoffForIndex } - } /** - * If standoff is supposed to be queried with text values, returns the minimum and maximum start indexes - * for the first page of standoff in a text value. Otherwise, returns `(None, None)`. - * - * @param queryStandoff `true` if standoff should be queried. - * @param settings the application settings. - * @return a tuple containing the minimum start index and maximum start index, or `(None, None)` if standoff - * is not being queried with text values. - */ - def getStandoffMinAndMaxStartIndexesForTextValueQuery(queryStandoff: Boolean, - settings: KnoraSettingsImpl): (Option[Int], Option[Int]) = { + * If standoff is supposed to be queried with text values, returns the minimum and maximum start indexes + * for the first page of standoff in a text value. Otherwise, returns `(None, None)`. + * + * @param queryStandoff `true` if standoff should be queried. + * @param settings the application settings. + * @return a tuple containing the minimum start index and maximum start index, or `(None, None)` if standoff + * is not being queried with text values. + */ + def getStandoffMinAndMaxStartIndexesForTextValueQuery( + queryStandoff: Boolean, + settings: KnoraSettingsImpl + ): (Option[Int], Option[Int]) = if (queryStandoff) { // Yes. Get the first page of standoff with each text value. (Some(0), Some(settings.standoffPerPage - 1)) @@ -1519,5 +1670,4 @@ object StandoffTagUtilV2 { // No. (None, None) } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala index 8d8b6e200b..3dcc7f2b30 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala @@ -36,60 +36,60 @@ import org.knora.webapi.messages.util.ErrorHandlingMap import scala.xml._ /** - * Represents an attribute of a standoff tag. - * - * @param key the name of the attribute. - * @param xmlNamespace the XML namespace that is used for the attribute when the tag is represented as XML. - * @param value the value of the attribute. - */ + * Represents an attribute of a standoff tag. + * + * @param key the name of the attribute. + * @param xmlNamespace the XML namespace that is used for the attribute when the tag is represented as XML. + * @param value the value of the attribute. + */ case class StandoffTagAttribute(key: String, xmlNamespace: Option[IRI], value: String) /** - * Represents markup on a range of characters in a text. - */ + * Represents markup on a range of characters in a text. + */ sealed trait StandoffTag { /** - * The document-specific ID of this tag, if any. - */ + * The document-specific ID of this tag, if any. + */ def originalID: Option[String] /** - * A [[UUID]] representing this tag and any other tags that point to semantically equivalent - * content in other versions of the same text. - */ + * A [[UUID]] representing this tag and any other tags that point to semantically equivalent + * content in other versions of the same text. + */ def uuid: UUID /** - * The name of the tag. - */ + * The name of the tag. + */ def tagName: String /** - * The namespace used when this tag is represented as XML. - */ + * The namespace used when this tag is represented as XML. + */ def xmlNamespace: Option[IRI] /** - * The start position of the text range. - */ + * The start position of the text range. + */ def startPosition: Int /** - * The end position of the text range. - */ + * The end position of the text range. + */ def endPosition: Int /** - * The attributes attached to this tag. - */ + * The attributes attached to this tag. + */ def attributes: Set[StandoffTagAttribute] } /** - * Represents a [[StandoffTag]] that has a single index indicating its position in a sequence of standoff tags, - * and optionally the index of the tag that contains it. - */ + * Represents a [[StandoffTag]] that has a single index indicating its position in a sequence of standoff tags, + * and optionally the index of the tag that contains it. + */ sealed trait IndexedStandoffTag extends StandoffTag { def index: Int @@ -97,133 +97,136 @@ sealed trait IndexedStandoffTag extends StandoffTag { } /** - * Represents a standoff tag that requires a hierarchical document structure. When serialised to XML, it is represented - * as a single element. - * - * @param originalID a client-specific ID for the tag. - * @param uuid a [[UUID]] representing this tag and any other tags that - * point to semantically equivalent content in other versions of the same text. - * @param tagName the name of this tag. - * @param xmlNamespace the namespace used when this tag is represented as XML. - * @param attributes the attributes attached to this tag. - * @param startPosition the start position of the range of characters marked up with this tag. - * @param endPosition the end position of the range of characters marked up with this tag. - * @param index the index of this tag. Indexes are numbered from 0 within the context of a particular text, - * and make it possible to order tags that share the same position. - * @param parentIndex the index of the [[HierarchicalStandoffTag]] that contains this tag. If a tag has no - * parent, it is the root of the tree. - */ -case class HierarchicalStandoffTag(originalID: Option[String], - uuid: UUID, - tagName: String, - xmlNamespace: Option[IRI] = None, - attributes: Set[StandoffTagAttribute] = Set.empty[StandoffTagAttribute], - startPosition: Int, - endPosition: Int, - index: Int, - parentIndex: Option[Int] = None) - extends IndexedStandoffTag + * Represents a standoff tag that requires a hierarchical document structure. When serialised to XML, it is represented + * as a single element. + * + * @param originalID a client-specific ID for the tag. + * @param uuid a [[UUID]] representing this tag and any other tags that + * point to semantically equivalent content in other versions of the same text. + * @param tagName the name of this tag. + * @param xmlNamespace the namespace used when this tag is represented as XML. + * @param attributes the attributes attached to this tag. + * @param startPosition the start position of the range of characters marked up with this tag. + * @param endPosition the end position of the range of characters marked up with this tag. + * @param index the index of this tag. Indexes are numbered from 0 within the context of a particular text, + * and make it possible to order tags that share the same position. + * @param parentIndex the index of the [[HierarchicalStandoffTag]] that contains this tag. If a tag has no + * parent, it is the root of the tree. + */ +case class HierarchicalStandoffTag( + originalID: Option[String], + uuid: UUID, + tagName: String, + xmlNamespace: Option[IRI] = None, + attributes: Set[StandoffTagAttribute] = Set.empty[StandoffTagAttribute], + startPosition: Int, + endPosition: Int, + index: Int, + parentIndex: Option[Int] = None +) extends IndexedStandoffTag /** - * Represents a standoff tag that does not require a hierarchical document structure, although it can be used within - * such a structure. Its XML representation is a pair of empty elements in - * [[http://conferences.idealliance.org/extreme/html/2004/DeRose01/EML2004DeRose01.html#t6 CLIX]] format. - * - * @param originalID a client-specific ID for the tag. - * @param uuid a [[UUID]] representing this tag and any other tags that - * point to semantically equivalent content in other versions of the same text. - * @param tagName the name of the tag. - * @param xmlNamespace the namespace used when this tag is represented as XML. - * @param attributes the attributes attached to this tag. - * @param startPosition the start position of the range of characters marked up with this tag. - * @param endPosition the end position of the range of characters marked up with this tag. - * @param startIndex the index of the start position. Indexes are numbered from 0 within the context of a - * particular text, and make it possible to order tags that share the same position. - * @param startParentIndex the index of the [[HierarchicalStandoffTag]], if any, that contains the start position. - * @param endIndex the index of the end position. - * @param endParentIndex the index of the [[HierarchicalStandoffTag]], if any, that contains the end position. - */ -case class FreeStandoffTag(originalID: Option[String], - uuid: UUID, - tagName: String, - xmlNamespace: Option[IRI] = None, - attributes: Set[StandoffTagAttribute] = Set.empty[StandoffTagAttribute], - startPosition: Int, - endPosition: Int, - startIndex: Int, - startParentIndex: Option[Int] = None, - endIndex: Int, - endParentIndex: Option[Int] = None) - extends StandoffTag + * Represents a standoff tag that does not require a hierarchical document structure, although it can be used within + * such a structure. Its XML representation is a pair of empty elements in + * [[http://conferences.idealliance.org/extreme/html/2004/DeRose01/EML2004DeRose01.html#t6 CLIX]] format. + * + * @param originalID a client-specific ID for the tag. + * @param uuid a [[UUID]] representing this tag and any other tags that + * point to semantically equivalent content in other versions of the same text. + * @param tagName the name of the tag. + * @param xmlNamespace the namespace used when this tag is represented as XML. + * @param attributes the attributes attached to this tag. + * @param startPosition the start position of the range of characters marked up with this tag. + * @param endPosition the end position of the range of characters marked up with this tag. + * @param startIndex the index of the start position. Indexes are numbered from 0 within the context of a + * particular text, and make it possible to order tags that share the same position. + * @param startParentIndex the index of the [[HierarchicalStandoffTag]], if any, that contains the start position. + * @param endIndex the index of the end position. + * @param endParentIndex the index of the [[HierarchicalStandoffTag]], if any, that contains the end position. + */ +case class FreeStandoffTag( + originalID: Option[String], + uuid: UUID, + tagName: String, + xmlNamespace: Option[IRI] = None, + attributes: Set[StandoffTagAttribute] = Set.empty[StandoffTagAttribute], + startPosition: Int, + endPosition: Int, + startIndex: Int, + startParentIndex: Option[Int] = None, + endIndex: Int, + endParentIndex: Option[Int] = None +) extends StandoffTag /** - * Represents a text and its standoff markup. - * - * @param text the text that has been marked up with standoff. - * @param standoff the standoff markup. - */ + * Represents a text and its standoff markup. + * + * @param text the text that has been marked up with standoff. + * @param standoff the standoff markup. + */ case class TextWithStandoff(text: String, standoff: Seq[StandoffTag]) /** - * Represents a difference between two texts, a base text and a derived text. - */ + * Represents a difference between two texts, a base text and a derived text. + */ trait StandoffDiff { /** - * The position in the base text where the difference starts. - */ + * The position in the base text where the difference starts. + */ def baseStartPosition: Int /** - * The position in the derived text where the difference starts. - */ + * The position in the derived text where the difference starts. + */ def derivedStartPosition: Int } /** - * Represents a string that is in both the base text and the derived text. - * - * @param baseStartPosition the start position of the string in the base text. - * @param baseEndPosition the end position of the string in the base text. - * @param derivedStartPosition the start position of the string in the derived text. - * @param derivedEndPosition the end position of the string in the derived text. - */ -case class StandoffDiffEqual(baseStartPosition: Int, - baseEndPosition: Int, - derivedStartPosition: Int, - derivedEndPosition: Int) - extends StandoffDiff + * Represents a string that is in both the base text and the derived text. + * + * @param baseStartPosition the start position of the string in the base text. + * @param baseEndPosition the end position of the string in the base text. + * @param derivedStartPosition the start position of the string in the derived text. + * @param derivedEndPosition the end position of the string in the derived text. + */ +case class StandoffDiffEqual( + baseStartPosition: Int, + baseEndPosition: Int, + derivedStartPosition: Int, + derivedEndPosition: Int +) extends StandoffDiff /** - * Represents a string that is present in the derived text but not in the base text. - * - * @param baseStartPosition the position in the base text where the string would have to be inserted to match - * the derived text. - * @param derivedStartPosition the start position of the inserted string in the derived text. - * @param derivedEndPosition the end position of the inserted string in the derived text. - */ + * Represents a string that is present in the derived text but not in the base text. + * + * @param baseStartPosition the position in the base text where the string would have to be inserted to match + * the derived text. + * @param derivedStartPosition the start position of the inserted string in the derived text. + * @param derivedEndPosition the end position of the inserted string in the derived text. + */ case class StandoffDiffInsert(baseStartPosition: Int, derivedStartPosition: Int, derivedEndPosition: Int) extends StandoffDiff /** - * Represents a string that is present in the base text but not in the derived text. - * - * @param baseStartPosition the start position of the deleted string in the base text. - * @param baseEndPosition the end position of the deleted string in the base text. - * @param derivedStartPosition the position in the derived text where the string would have to be inserted to - * match the base text. - */ + * Represents a string that is present in the base text but not in the derived text. + * + * @param baseStartPosition the start position of the deleted string in the base text. + * @param baseEndPosition the end position of the deleted string in the base text. + * @param derivedStartPosition the position in the derived text where the string would have to be inserted to + * match the base text. + */ case class StandoffDiffDelete(baseStartPosition: Int, baseEndPosition: Int, derivedStartPosition: Int) extends StandoffDiff /** - * Represents an XML element that requires a separator to be inserted at its end. - * This is necessary because the markup is going to be represented in standoff (separated from the text). - * - * @param maybeNamespace the namespace the element belongs to, if any. - * @param tagname the name of the element. - * @param maybeClassname the class of the element, if any. - */ + * Represents an XML element that requires a separator to be inserted at its end. + * This is necessary because the markup is going to be represented in standoff (separated from the text). + * + * @param maybeNamespace the namespace the element belongs to, if any. + * @param tagname the name of the element. + * @param maybeClassname the class of the element, if any. + */ case class XMLTagSeparatorRequired(maybeNamespace: Option[String], tagname: String, maybeClassname: Option[String]) { // generate an XPath expression to match this element @@ -246,8 +249,8 @@ case class XMLTagSeparatorRequired(maybeNamespace: Option[String], tagname: Stri } /** - * Standoff-related constants. - */ + * Standoff-related constants. + */ object XMLToStandoffUtil { // The header written at the start of every XML document. private val XmlHeader = "\n" @@ -266,52 +269,54 @@ object XMLToStandoffUtil { } /** - * Converts XML documents to standoff markup and back again. Supports - * [[http://conferences.idealliance.org/extreme/html/2004/DeRose01/EML2004DeRose01.html#t6 CLIX]] format - * for overlapping tags. - * - * Every standoff tag has a [[UUID]]. These can be represented in XML in different ways: - * - * - In canonical form as a 36-character string. - * - As a 22-character Base64-encoded string. - * - As a document-specific ID that can be mapped to a UUID. - * - * These IDs are represented using the following XML attributes: - * - * - `sID` for CLIX start milestones. - * - `eID` for CLIX end milestones. - * - `id` for all other elements. - * - * IDs are required on CLIX milestones, and optional on other elements. - * - * When converting from XML: - * - * - If a document-specific ID is provided and can be mapped to a UUID, that UUID is used. - * - If a UUID is provided in canonical form or Base64 encoding, that UUID is used. - * - Otherwise (if no ID is provided, or if an ID is provided but cannot be parsed as a UUID or mapped to one), - * a random UUID is generated. - * - * When converting to XML: - * - * - If `writeAllIds` is set to `true` (the default), the ID of every element is written; otherwise, only the IDs of - * CLIX milestones are included. - * - If a UUID can be mapped to a document-specific ID, the document-specific ID is used, otherwise the UUID is used. - * - UUIDs are written in Base64 encoding if `writeBase64Ids` is `true` (the default), otherwise in canonical form. - * - * @param xmlNamespaces A map of prefixes to XML namespaces, to be used when converting standoff to XML. - * @param writeUuidsToXml If `true` (the default), adds the ID of every standoff tag as an attribute when writing - * XML. Otherwise, only the IDs of CLIX milestones and elements that originally had an id in XML are included. - * @param writeBase64IDs If `true`, writes UUIDs in Base64 encoding; otherwise, writes UUIDs in canonical form. - * @param documentSpecificIDs An optional mapping between document-specific IDs and UUIDs. When reading XML, - * each document-specific ID will be converted to the corresponding UUID. Elements that - * don't specify an ID will be assigned a random UUID. When writing XML, each UUID will - * be converted to the corresponding document-specific ID if available. - */ -class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String], - defaultXmlNamespace: Option[IRI] = None, - writeUuidsToXml: Boolean = true, - writeBase64IDs: Boolean = true, - documentSpecificIDs: Map[String, UUID] = Map.empty[String, UUID]) { + * Converts XML documents to standoff markup and back again. Supports + * [[http://conferences.idealliance.org/extreme/html/2004/DeRose01/EML2004DeRose01.html#t6 CLIX]] format + * for overlapping tags. + * + * Every standoff tag has a [[UUID]]. These can be represented in XML in different ways: + * + * - In canonical form as a 36-character string. + * - As a 22-character Base64-encoded string. + * - As a document-specific ID that can be mapped to a UUID. + * + * These IDs are represented using the following XML attributes: + * + * - `sID` for CLIX start milestones. + * - `eID` for CLIX end milestones. + * - `id` for all other elements. + * + * IDs are required on CLIX milestones, and optional on other elements. + * + * When converting from XML: + * + * - If a document-specific ID is provided and can be mapped to a UUID, that UUID is used. + * - If a UUID is provided in canonical form or Base64 encoding, that UUID is used. + * - Otherwise (if no ID is provided, or if an ID is provided but cannot be parsed as a UUID or mapped to one), + * a random UUID is generated. + * + * When converting to XML: + * + * - If `writeAllIds` is set to `true` (the default), the ID of every element is written; otherwise, only the IDs of + * CLIX milestones are included. + * - If a UUID can be mapped to a document-specific ID, the document-specific ID is used, otherwise the UUID is used. + * - UUIDs are written in Base64 encoding if `writeBase64Ids` is `true` (the default), otherwise in canonical form. + * + * @param xmlNamespaces A map of prefixes to XML namespaces, to be used when converting standoff to XML. + * @param writeUuidsToXml If `true` (the default), adds the ID of every standoff tag as an attribute when writing + * XML. Otherwise, only the IDs of CLIX milestones and elements that originally had an id in XML are included. + * @param writeBase64IDs If `true`, writes UUIDs in Base64 encoding; otherwise, writes UUIDs in canonical form. + * @param documentSpecificIDs An optional mapping between document-specific IDs and UUIDs. When reading XML, + * each document-specific ID will be converted to the corresponding UUID. Elements that + * don't specify an ID will be assigned a random UUID. When writing XML, each UUID will + * be converted to the corresponding document-specific ID if available. + */ +class XMLToStandoffUtil( + xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String], + defaultXmlNamespace: Option[IRI] = None, + writeUuidsToXml: Boolean = true, + writeBase64IDs: Boolean = true, + documentSpecificIDs: Map[String, UUID] = Map.empty[String, UUID] +) { import XMLToStandoffUtil._ @@ -330,75 +335,79 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] private val uuidsToDocumentSpecificIds: Map[UUID, String] = documentSpecificIDs.map(_.swap) private val xmlNamespaces2Prefixes = new ErrorHandlingMap( - xmlNamespaces.map(_.swap), { key: String => + xmlNamespaces.map(_.swap), + { key: String => s"No prefix defined for XML namespace $key" } ) /** - * An empty standoff tag representing a CLIX milestone, to facilitate conversion to and from XML. - * - * @param isStartTag if `true`, this tag represents the start element, otherwise it represents the end element. - */ - private case class ClixMilestoneTag(originalID: Option[String], - uuid: UUID, - tagName: String, - xmlNamespace: Option[IRI] = None, - attributes: Set[StandoffTagAttribute] = Set.empty[StandoffTagAttribute], - startPosition: Int, - endPosition: Int, - index: Int, - parentIndex: Option[Int] = None, - isStartTag: Boolean) - extends IndexedStandoffTag + * An empty standoff tag representing a CLIX milestone, to facilitate conversion to and from XML. + * + * @param isStartTag if `true`, this tag represents the start element, otherwise it represents the end element. + */ + private case class ClixMilestoneTag( + originalID: Option[String], + uuid: UUID, + tagName: String, + xmlNamespace: Option[IRI] = None, + attributes: Set[StandoffTagAttribute] = Set.empty[StandoffTagAttribute], + startPosition: Int, + endPosition: Int, + index: Int, + parentIndex: Option[Int] = None, + isStartTag: Boolean + ) extends IndexedStandoffTag /** - * Creates XSLT that inserts a separator after each element matching the XPath expression. - * - * @param xpath the XPath expression used to match elements. - * @param separator the separator to be inserted. - * @return an XSLT stylesheet as a [[String]]. - */ + * Creates XSLT that inserts a separator after each element matching the XPath expression. + * + * @param xpath the XPath expression used to match elements. + * @param separator the separator to be inserted. + * @return an XSLT stylesheet as a [[String]]. + */ private def insertSeparatorsXSLT(xpath: String, separator: Char) = s""" - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | $separator - | - | - | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | $separator + | + | + | """.stripMargin //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Public methods /** - * Converts an XML document to an equivalent [[TextWithStandoff]]. - * - * @param xmlStr the XML document to be converted. - * @param tagsWithSeparator a Seq with the tags that require a separator - * in the string representation (`valueHasString`) once markup is converted to standoff. - * @return a [[TextWithStandoff]]. - */ - def xml2TextWithStandoff(xmlStr: String, - tagsWithSeparator: Seq[XMLTagSeparatorRequired] = Seq.empty[XMLTagSeparatorRequired], - log: LoggingAdapter): TextWithStandoff = { + * Converts an XML document to an equivalent [[TextWithStandoff]]. + * + * @param xmlStr the XML document to be converted. + * @param tagsWithSeparator a Seq with the tags that require a separator + * in the string representation (`valueHasString`) once markup is converted to standoff. + * @return a [[TextWithStandoff]]. + */ + def xml2TextWithStandoff( + xmlStr: String, + tagsWithSeparator: Seq[XMLTagSeparatorRequired] = Seq.empty[XMLTagSeparatorRequired], + log: LoggingAdapter + ): TextWithStandoff = { // Knora uses Unicode INFORMATION SEPARATOR TWO (U+001E) to indicate word breaks where a tag implicitly separates words. But // INFORMATION SEPARATOR TWO is not a valid XML 1.0 character. Therefore, we temporarily insert PARAGRAPH SEPARATOR (U+2029) @@ -411,8 +420,8 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] val xmlStrWithSeparator = if (tagsWithSeparator.nonEmpty) { // build an XSLT to add separators to the XML - val xPAthExpression - : String = tagsWithSeparator.map(_.toXPath).mkString("|") // build a union of the collected elements' expressions + val xPAthExpression: String = + tagsWithSeparator.map(_.toXPath).mkString("|") // build a union of the collected elements' expressions val XSLT = insertSeparatorsXSLT(xPAthExpression, StringFormatter.PARAGRAPH_SEPARATOR) @@ -422,11 +431,13 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] val comp = proc.newXsltCompiler() val exp = comp.compile(new StreamSource(new StringReader(XSLT))) - val source = try { - proc.newDocumentBuilder().build(new StreamSource(new StringReader(xmlStr))) - } catch { - case e: Exception => throw StandoffConversionException(s"The provided XML could not be parsed: ${e.getMessage}") - } + val source = + try { + proc.newDocumentBuilder().build(new StreamSource(new StringReader(xmlStr))) + } catch { + case e: Exception => + throw StandoffConversionException(s"The provided XML could not be parsed: ${e.getMessage}") + } val xmlStrWithSep: StringWriter = new StringWriter() val out = proc.newSerializer(xmlStrWithSep) @@ -443,11 +454,12 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] val saxParser = saxParserFactory.newSAXParser() - val nodes: Elem = try { - XML.withSAXParser(saxParser).loadString(xmlStrWithSeparator) - } catch { - case e: Exception => throw StandoffInternalException(s"XML processing error: ${e.getMessage}", e, log) - } + val nodes: Elem = + try { + XML.withSAXParser(saxParser).loadString(xmlStrWithSeparator) + } catch { + case e: Exception => throw StandoffInternalException(s"XML processing error: ${e.getMessage}", e, log) + } val finishedConversionState = xmlNodes2Standoff( nodes = nodes, @@ -455,11 +467,10 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] ) if (finishedConversionState.clixStartMilestones.nonEmpty) { - val missingEndTags = finishedConversionState.clixStartMilestones - .map { - case (startTagID: String, startTag: ClixMilestoneTag) => - s"<${startTag.tagName} $XmlClixStartIdAttrName=${'"'}$startTagID${'"'}>" - } + val missingEndTags = finishedConversionState.clixStartMilestones.map { + case (startTagID: String, startTag: ClixMilestoneTag) => + s"<${startTag.tagName} $XmlClixStartIdAttrName=${'"'}$startTagID${'"'}>" + } .mkString(", ") throw InvalidStandoffException(s"One or more CLIX milestones were not closed: $missingEndTags") @@ -476,11 +487,11 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] } /** - * Converts a [[TextWithStandoff]] to an equivalent XML document. - * - * @param textWithStandoff the [[TextWithStandoff]] to be converted. - * @return an XML document. - */ + * Converts a [[TextWithStandoff]] to an equivalent XML document. + * + * @param textWithStandoff the [[TextWithStandoff]] to be converted. + * @return an XML document. + */ def textWithStandoff2Xml(textWithStandoff: TextWithStandoff): String = { val tags = textWithStandoff.standoff.foldLeft(Vector.empty[IndexedStandoffTag]) { // Split each free tag into a pair of CLIX milestones. @@ -532,7 +543,8 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] standoffTags2XmlString( text = textWithStandoff.text.replace( StringFormatter.INFORMATION_SEPARATOR_TWO.toString, - StringFormatter.PARAGRAPH_SEPARATOR.toString), // replace information separator (which is an invalid XML character) with paragraph separator + StringFormatter.PARAGRAPH_SEPARATOR.toString + ), // replace information separator (which is an invalid XML character) with paragraph separator groupedTags = groupedTags, posBeforeSiblings = 0, siblings = children, @@ -542,7 +554,8 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] case Some(_) => throw InvalidStandoffException( - "The standoff cannot be serialised to XML because it would have multiple root nodes") + "The standoff cannot be serialised to XML because it would have multiple root nodes" + ) case None => throw InvalidStandoffException("The standoff cannot be serialised to XML because there is no root element") @@ -565,80 +578,81 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] } /** - * Computes the differences between a base text and a derived text. - * - * @param baseText the base text. - * @param derivedText the derived text. - * @return the differences between the two texts. - */ + * Computes the differences between a base text and a derived text. + * + * @param baseText the base text. + * @param derivedText the derived text. + * @return the differences between the two texts. + */ def makeStandoffDiffs(baseText: String, derivedText: String): Seq[StandoffDiff] = { import scala.jdk.CollectionConverters._ - case class DiffConversionState(standoffDiffs: Vector[StandoffDiff] = Vector.empty[StandoffDiff], - basePos: Int = 0, - derivedPos: Int = 0) + case class DiffConversionState( + standoffDiffs: Vector[StandoffDiff] = Vector.empty[StandoffDiff], + basePos: Int = 0, + derivedPos: Int = 0 + ) val diffList = diffMatchPatch.diff_main(baseText, derivedText) diffMatchPatch.diff_cleanupSemantic(diffList) val diffs: Seq[Diff] = diffList.asScala.toSeq - val conversionResult = diffs.foldLeft(DiffConversionState()) { - case (conversionState, diff) => - diff.operation match { - case Operation.EQUAL => - val standoffDiff = StandoffDiffEqual( - baseStartPosition = conversionState.basePos, - baseEndPosition = conversionState.basePos + diff.text.length, - derivedStartPosition = conversionState.derivedPos, - derivedEndPosition = conversionState.derivedPos + diff.text.length - ) + val conversionResult = diffs.foldLeft(DiffConversionState()) { case (conversionState, diff) => + diff.operation match { + case Operation.EQUAL => + val standoffDiff = StandoffDiffEqual( + baseStartPosition = conversionState.basePos, + baseEndPosition = conversionState.basePos + diff.text.length, + derivedStartPosition = conversionState.derivedPos, + derivedEndPosition = conversionState.derivedPos + diff.text.length + ) - DiffConversionState( - standoffDiffs = conversionState.standoffDiffs :+ standoffDiff, - basePos = standoffDiff.baseEndPosition, - derivedPos = standoffDiff.derivedEndPosition - ) + DiffConversionState( + standoffDiffs = conversionState.standoffDiffs :+ standoffDiff, + basePos = standoffDiff.baseEndPosition, + derivedPos = standoffDiff.derivedEndPosition + ) - case Operation.DELETE => - val standoffDiff = StandoffDiffDelete( - baseStartPosition = conversionState.basePos, - baseEndPosition = conversionState.basePos + diff.text.length, - derivedStartPosition = conversionState.derivedPos - ) + case Operation.DELETE => + val standoffDiff = StandoffDiffDelete( + baseStartPosition = conversionState.basePos, + baseEndPosition = conversionState.basePos + diff.text.length, + derivedStartPosition = conversionState.derivedPos + ) - DiffConversionState( - standoffDiffs = conversionState.standoffDiffs :+ standoffDiff, - basePos = standoffDiff.baseEndPosition, - derivedPos = conversionState.derivedPos - ) + DiffConversionState( + standoffDiffs = conversionState.standoffDiffs :+ standoffDiff, + basePos = standoffDiff.baseEndPosition, + derivedPos = conversionState.derivedPos + ) - case Operation.INSERT => - val standoffDiff = StandoffDiffInsert( - baseStartPosition = conversionState.basePos, - derivedStartPosition = conversionState.derivedPos, - derivedEndPosition = conversionState.derivedPos + diff.text.length - ) + case Operation.INSERT => + val standoffDiff = StandoffDiffInsert( + baseStartPosition = conversionState.basePos, + derivedStartPosition = conversionState.derivedPos, + derivedEndPosition = conversionState.derivedPos + diff.text.length + ) - DiffConversionState( - standoffDiffs = conversionState.standoffDiffs :+ standoffDiff, - basePos = conversionState.basePos, - derivedPos = standoffDiff.derivedEndPosition - ) - } + DiffConversionState( + standoffDiffs = conversionState.standoffDiffs :+ standoffDiff, + basePos = conversionState.basePos, + derivedPos = standoffDiff.derivedEndPosition + ) + } } conversionResult.standoffDiffs } /** - * Converts standoff diffs to XML. The resulting XML has a root element called `` containing the base - * text, along with `` tags representing deletions and `` tags representing insertions. - * - * @param baseText the base text that was used to calculate the diffs. - * @param derivedText the derived text that was used to calculate the diffs. - * @param standoffDiffs the standoff diffs. - * @return an XML representation of the diffs. - */ + * Converts standoff diffs to XML. The resulting XML has a root element called `` containing the base + * text, along with `` tags representing deletions and `` tags representing insertions. + * + * @param baseText the base text that was used to calculate the diffs. + * @param derivedText the derived text that was used to calculate the diffs. + * @param standoffDiffs the standoff diffs. + * @return an XML representation of the diffs. + */ def standoffDiffs2Xml(baseText: String, derivedText: String, standoffDiffs: Seq[StandoffDiff]): String = { val stringBuilder = new StringBuilder(XmlHeader).append("") @@ -646,7 +660,8 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] standoffDiff match { case equal: StandoffDiffEqual => stringBuilder.append( - StringEscapeUtils.escapeXml11(baseText.substring(equal.baseStartPosition, equal.baseEndPosition))) + StringEscapeUtils.escapeXml11(baseText.substring(equal.baseStartPosition, equal.baseEndPosition)) + ) case delete: StandoffDiffDelete => stringBuilder @@ -657,8 +672,11 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] case insert: StandoffDiffInsert => stringBuilder .append("") - .append(StringEscapeUtils.escapeXml11( - derivedText.substring(insert.derivedStartPosition, insert.derivedEndPosition))) + .append( + StringEscapeUtils.escapeXml11( + derivedText.substring(insert.derivedStartPosition, insert.derivedEndPosition) + ) + ) .append("") } } @@ -668,15 +686,17 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] } /** - * Given a set of standoff tags referring to an old version of a text and a set of standoff tags referring to a newer - * version of the text, finds the standoff tags that have been added or removed. - * - * @param oldStandoff the standoff tags referring to the old version of the text. - * @param newStandoff the standoff tags referring to the new version of the text. - * @return a tuple containing the added standoff tags and the removed standoff tags. - */ - def findChangedStandoffTags(oldStandoff: Seq[StandoffTag], - newStandoff: Seq[StandoffTag]): (Set[StandoffTag], Set[StandoffTag]) = { + * Given a set of standoff tags referring to an old version of a text and a set of standoff tags referring to a newer + * version of the text, finds the standoff tags that have been added or removed. + * + * @param oldStandoff the standoff tags referring to the old version of the text. + * @param newStandoff the standoff tags referring to the new version of the text. + * @return a tuple containing the added standoff tags and the removed standoff tags. + */ + def findChangedStandoffTags( + oldStandoff: Seq[StandoffTag], + newStandoff: Seq[StandoffTag] + ): (Set[StandoffTag], Set[StandoffTag]) = { def makeStandoffTagUuidMap(standoff: Seq[StandoffTag]): Map[UUID, StandoffTag] = standoff.map { tag => tag.uuid -> tag @@ -701,39 +721,40 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] // Private methods /** - * Represents the state of the conversion of XML text to standoff. - * - * @param currentPos the current position in the text. - * @param parentId the ID of the parent [[HierarchicalStandoffTag]], or [[None]] if the root element is - * being generated. - * @param nextIndex the next available standoff tag index. - * @param standoffTags the standoff tags generated so far. - * @param clixStartMilestones a map of element IDs to CLIX start milestones for which end milestones have not yet - * been encountered. - */ - private case class Xml2StandoffState(currentPos: Int = 0, - parentId: Option[Int] = None, - nextIndex: Int = 0, - standoffTags: Vector[StandoffTag] = Vector.empty[StandoffTag], - clixStartMilestones: Map[String, ClixMilestoneTag] = - Map.empty[String, ClixMilestoneTag]) + * Represents the state of the conversion of XML text to standoff. + * + * @param currentPos the current position in the text. + * @param parentId the ID of the parent [[HierarchicalStandoffTag]], or [[None]] if the root element is + * being generated. + * @param nextIndex the next available standoff tag index. + * @param standoffTags the standoff tags generated so far. + * @param clixStartMilestones a map of element IDs to CLIX start milestones for which end milestones have not yet + * been encountered. + */ + private case class Xml2StandoffState( + currentPos: Int = 0, + parentId: Option[Int] = None, + nextIndex: Int = 0, + standoffTags: Vector[StandoffTag] = Vector.empty[StandoffTag], + clixStartMilestones: Map[String, ClixMilestoneTag] = Map.empty[String, ClixMilestoneTag] + ) /** - * Recursively converts XML nodes to standoff. - * - * @param nodes a sequence of sibling XML nodes to be converted. - * @param startState the current state of the conversion. - * @return the resulting conversion state. - */ + * Recursively converts XML nodes to standoff. + * + * @param nodes a sequence of sibling XML nodes to be converted. + * @param startState the current state of the conversion. + * @return the resulting conversion state. + */ private def xmlNodes2Standoff(nodes: NodeSeq, startState: Xml2StandoffState): Xml2StandoffState = { /** - * Converts an XML element ID to a UUID. - * - * @param id the ID to be converted. - * @return the corresponding UUID. - */ - def id2Uuid(id: String): UUID = { + * Converts an XML element ID to a UUID. + * + * @param id the ID to be converted. + * @return the corresponding UUID. + */ + def id2Uuid(id: String): UUID = // If the ID was listed as a document-specific ID corresponding to an existing UUID, use that UUID. documentSpecificIDs.get(id) match { case Some(existingUuid) => existingUuid @@ -746,15 +767,14 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] UUID.randomUUID } } - } /** - * Converts XML attributes to standoff tag attributes, ignoring ID attributes. - * - * @param element the XML element containing the attributes. - * @return the corresponding standoff tag attributes. - */ - def xmlAttrs2StandoffAttrs(element: Elem): Set[StandoffTagAttribute] = { + * Converts XML attributes to standoff tag attributes, ignoring ID attributes. + * + * @param element the XML element containing the attributes. + * @return the corresponding standoff tag attributes. + */ + def xmlAttrs2StandoffAttrs(element: Elem): Set[StandoffTagAttribute] = element.attributes.foldLeft(Set.empty[StandoffTagAttribute]) { case (acc, xmlAttr: MetaData) if !XmlIdAttrNames.contains(xmlAttr.key) => acc + StandoffTagAttribute( @@ -765,7 +785,6 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] case (acc, _) => acc } - } // Process sibling nodes. nodes.foldLeft(startState) { @@ -807,17 +826,20 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] val startMilestone: ClixMilestoneTag = acc.clixStartMilestones.getOrElse( eID, throw InvalidStandoffException( - s"Found a CLIX milestone with $XmlClixEndIdAttrName $eID, but there was no start milestone with that $XmlClixStartIdAttrName") + s"Found a CLIX milestone with $XmlClixEndIdAttrName $eID, but there was no start milestone with that $XmlClixStartIdAttrName" + ) ) if (startMilestone.tagName != elem.label) { throw InvalidStandoffException( - s"The CLIX start milestone with $XmlClixStartIdAttrName $eID has tag name <${startMilestone.tagName}>, but the end milestone with that $XmlClixEndIdAttrName has tag name ${elem.label}") + s"The CLIX start milestone with $XmlClixStartIdAttrName $eID has tag name <${startMilestone.tagName}>, but the end milestone with that $XmlClixEndIdAttrName has tag name ${elem.label}" + ) } if (startMilestone.xmlNamespace != Option(elem.namespace)) { throw InvalidStandoffException( - s"The CLIX start milestone with $XmlClixStartIdAttrName $eID is in namespace ${startMilestone.xmlNamespace}, but the end milestone with that $XmlClixEndIdAttrName is in namespace ${elem.namespace}") + s"The CLIX start milestone with $XmlClixStartIdAttrName $eID is in namespace ${startMilestone.xmlNamespace}, but the end milestone with that $XmlClixEndIdAttrName is in namespace ${elem.namespace}" + ) } val freeTag = FreeStandoffTag( @@ -881,32 +903,34 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] } /** - * Recursively generates XML text representing [[IndexedStandoffTag]] objects. - * - * @param text the text that has been marked up. - * @param groupedTags a [[Map]] of all the [[IndexedStandoffTag]] objects that refer to the text, grouped by - * parent tag index. - * @param posBeforeSiblings the last position that was processed before this method was called. If there is - * any text before `siblings`, this position will be less than the position of the - * first sibling.. - * @param siblings a sequence of tags having the same parent. - * @param xmlString the resulting XML text. - */ - private def standoffTags2XmlString(text: String, - groupedTags: Map[Option[Int], Seq[IndexedStandoffTag]], - posBeforeSiblings: Int, - siblings: Seq[IndexedStandoffTag], - writeNamespaces: Boolean = false, - xmlString: StringBuilder): Int = { + * Recursively generates XML text representing [[IndexedStandoffTag]] objects. + * + * @param text the text that has been marked up. + * @param groupedTags a [[Map]] of all the [[IndexedStandoffTag]] objects that refer to the text, grouped by + * parent tag index. + * @param posBeforeSiblings the last position that was processed before this method was called. If there is + * any text before `siblings`, this position will be less than the position of the + * first sibling.. + * @param siblings a sequence of tags having the same parent. + * @param xmlString the resulting XML text. + */ + private def standoffTags2XmlString( + text: String, + groupedTags: Map[Option[Int], Seq[IndexedStandoffTag]], + posBeforeSiblings: Int, + siblings: Seq[IndexedStandoffTag], + writeNamespaces: Boolean = false, + xmlString: StringBuilder + ): Int = { /** - * Adds an optional XML namespace prefix to the name of an XML element or attribute. - * - * @param unprefixedName the unprefixed name of the element or attribute. - * @param xmlNamespace the XML namespace. - * @return the prefixed name. - */ - def makePrefixedXmlName(unprefixedName: String, xmlNamespace: Option[IRI]): String = { + * Adds an optional XML namespace prefix to the name of an XML element or attribute. + * + * @param unprefixedName the unprefixed name of the element or attribute. + * @param xmlNamespace the XML namespace. + * @return the prefixed name. + */ + def makePrefixedXmlName(unprefixedName: String, xmlNamespace: Option[IRI]): String = (xmlNamespace, defaultXmlNamespace) match { case (Some(namespace), Some(defaultNamespace)) if namespace != defaultNamespace => xmlNamespaces2Prefixes(namespace) + ":" + unprefixedName @@ -916,26 +940,23 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] case _ => unprefixedName } - } /** - * Writes key-value pairs representing the default XML namespaces, prefixes for other XML namespaces, - * and the attributes of an XML element. - * - * @param tag the tag being converted to XML. - */ + * Writes key-value pairs representing the default XML namespaces, prefixes for other XML namespaces, + * and the attributes of an XML element. + * + * @param tag the tag being converted to XML. + */ def attributesAndNamespaces2Xml(tag: IndexedStandoffTag): Unit = { // If we were asked to write definitions of the default namespace and of namespace prefixes // (because we're writing the root element of the XML document), add them first. val namespacesAsXml: Vector[(String, String)] = if (writeNamespaces) { val maybeDefaultNamespace = defaultXmlNamespace - .map( - namespace => ("xmlns", namespace) - ) + .map(namespace => ("xmlns", namespace)) .toVector - val prefixedNamespaces = xmlNamespaces.map { - case (prefix, namespace) => (s"xmlns:$prefix", namespace) + val prefixedNamespaces = xmlNamespaces.map { case (prefix, namespace) => + (s"xmlns:$prefix", namespace) }.toVector maybeDefaultNamespace ++ prefixedNamespaces @@ -992,75 +1013,74 @@ class XMLToStandoffUtil(xmlNamespaces: Map[String, IRI] = Map.empty[IRI, String] // Convert each sibling standoff tag to XML. - siblings.sortBy(_.index).foldLeft(posBeforeSiblings) { - case (posBeforeTag, tag) => - // If there's some text between the current position and this tag, include it now. - if (tag.startPosition > posBeforeTag) { - xmlString.append(StringEscapeUtils.escapeXml11(text.substring(posBeforeTag, tag.startPosition))) - } + siblings.sortBy(_.index).foldLeft(posBeforeSiblings) { case (posBeforeTag, tag) => + // If there's some text between the current position and this tag, include it now. + if (tag.startPosition > posBeforeTag) { + xmlString.append(StringEscapeUtils.escapeXml11(text.substring(posBeforeTag, tag.startPosition))) + } - // Add a namespace prefix to the tag's name, if necessary. - val prefixedTagName = makePrefixedXmlName(tag.tagName, tag.xmlNamespace) - - if (tag.endPosition > tag.startPosition) { - // Non-empty tag - xmlString.append(s"<$prefixedTagName") - attributesAndNamespaces2Xml(tag) - xmlString.append(">") - - val maybeChildren: Option[Seq[IndexedStandoffTag]] = groupedTags.get(Some(tag.index)) - - val posAfterChildren = maybeChildren match { - case Some(children) => - standoffTags2XmlString( - text = text, - groupedTags = groupedTags, - posBeforeSiblings = tag.startPosition, - siblings = children, - xmlString = xmlString - ) + // Add a namespace prefix to the tag's name, if necessary. + val prefixedTagName = makePrefixedXmlName(tag.tagName, tag.xmlNamespace) + + if (tag.endPosition > tag.startPosition) { + // Non-empty tag + xmlString.append(s"<$prefixedTagName") + attributesAndNamespaces2Xml(tag) + xmlString.append(">") + + val maybeChildren: Option[Seq[IndexedStandoffTag]] = groupedTags.get(Some(tag.index)) + + val posAfterChildren = maybeChildren match { + case Some(children) => + standoffTags2XmlString( + text = text, + groupedTags = groupedTags, + posBeforeSiblings = tag.startPosition, + siblings = children, + xmlString = xmlString + ) - case None => tag.startPosition - } + case None => tag.startPosition + } - // If there's some text between the last child and the closing tag, include it now. - if (tag.endPosition > posAfterChildren) { - xmlString.append(StringEscapeUtils.escapeXml11(text.substring(posAfterChildren, tag.endPosition))) - } + // If there's some text between the last child and the closing tag, include it now. + if (tag.endPosition > posAfterChildren) { + xmlString.append(StringEscapeUtils.escapeXml11(text.substring(posAfterChildren, tag.endPosition))) + } - xmlString.append(s"") - } else { - // Does this tag have children? - val maybeChildren: Option[Seq[IndexedStandoffTag]] = groupedTags.get(Some(tag.index)) - - maybeChildren match { - case Some(children) => - // Yes. Make a start tag. - xmlString.append(s"<$prefixedTagName") - attributesAndNamespaces2Xml(tag) - xmlString.append(">") - - // Recurse to process the empty children. - standoffTags2XmlString( - text = text, - groupedTags = groupedTags, - posBeforeSiblings = tag.startPosition, - siblings = children, - xmlString = xmlString - ) + xmlString.append(s"") + } else { + // Does this tag have children? + val maybeChildren: Option[Seq[IndexedStandoffTag]] = groupedTags.get(Some(tag.index)) + + maybeChildren match { + case Some(children) => + // Yes. Make a start tag. + xmlString.append(s"<$prefixedTagName") + attributesAndNamespaces2Xml(tag) + xmlString.append(">") + + // Recurse to process the empty children. + standoffTags2XmlString( + text = text, + groupedTags = groupedTags, + posBeforeSiblings = tag.startPosition, + siblings = children, + xmlString = xmlString + ) - // Make an end tag. - xmlString.append(s"") + // Make an end tag. + xmlString.append(s"") - case None => - // This tag has no children. - xmlString.append(s"<$prefixedTagName") - attributesAndNamespaces2Xml(tag) - xmlString.append("/>") - } + case None => + // This tag has no children. + xmlString.append(s"<$prefixedTagName") + attributesAndNamespaces2Xml(tag) + xmlString.append("/>") } + } - tag.endPosition + tag.endPosition } } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLUtil.scala index b673bd5413..0e8614b299 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLUtil.scala @@ -28,29 +28,32 @@ import org.knora.webapi.exceptions.StandoffConversionException object XMLUtil { /** - * Applies an XSL transformation to the given XML and returns the result. - * - * @param xml the xml to be transformed. - * @param xslt the XSL transformation to be applied. - * @return the transformation's result. - */ + * Applies an XSL transformation to the given XML and returns the result. + * + * @param xml the xml to be transformed. + * @param xslt the XSL transformation to be applied. + * @return the transformation's result. + */ def applyXSLTransformation(xml: String, xslt: String): String = { // apply the XSL transformation to xml val proc = new net.sf.saxon.s9api.Processor(false) val comp = proc.newXsltCompiler() - val exp: XsltExecutable = try { - comp.compile(new StreamSource(new StringReader(xslt))) - } catch { - case e: Exception => throw StandoffConversionException(s"The provided XSLT could not be parsed: ${e.getMessage}") - } + val exp: XsltExecutable = + try { + comp.compile(new StreamSource(new StringReader(xslt))) + } catch { + case e: Exception => + throw StandoffConversionException(s"The provided XSLT could not be parsed: ${e.getMessage}") + } - val source = try { - proc.newDocumentBuilder().build(new StreamSource(new StringReader(xml))) - } catch { - case e: Exception => throw StandoffConversionException(s"The provided XML could not be parsed: ${e.getMessage}") - } + val source = + try { + proc.newDocumentBuilder().build(new StreamSource(new StringReader(xml))) + } catch { + case e: Exception => throw StandoffConversionException(s"The provided XML could not be parsed: ${e.getMessage}") + } val xmlTransformedStr: StringWriter = new StringWriter() val out = proc.newSerializer(xmlTransformedStr) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraRequestV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraRequestV1.scala index 5be9015861..794de4930f 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraRequestV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraRequestV1.scala @@ -20,6 +20,6 @@ package org.knora.webapi.messages.v1.responder /** - * A tagging trait for messages that can be sent to Knora API v1 responders. - */ + * A tagging trait for messages that can be sent to Knora API v1 responders. + */ trait KnoraRequestV1 diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraResponseV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraResponseV1.scala index 8efdbd3784..8d3fd70ee8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraResponseV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/KnoraResponseV1.scala @@ -22,6 +22,6 @@ package org.knora.webapi.messages.v1.responder import org.knora.webapi.messages.traits.Jsonable /** - * A trait for Knora API v1 response messages. Any response message can be converted into JSON. - */ + * A trait for Knora API v1 response messages. Any response message can be converted into JSON. + */ trait KnoraResponseV1 extends Jsonable diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ckanmessages/CkanMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ckanmessages/CkanMessagesV1.scala index 5cb825246e..187d3a9a70 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ckanmessages/CkanMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ckanmessages/CkanMessagesV1.scala @@ -29,34 +29,35 @@ import spray.json._ // API requests /** - * An abstract trait representing a request message that can be sent to `CkanResponderV1`. - */ + * An abstract trait representing a request message that can be sent to `CkanResponderV1`. + */ sealed trait CkanResponderRequestV1 extends KnoraRequestV1 /** - * Represents an API request payload that asks the Knora API server to return Ckan data - * - * @param projects - * @param limit - * @param info - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile - */ -case class CkanRequestV1(projects: Option[Seq[String]], - limit: Option[Int], - info: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM) - extends CkanResponderRequestV1 + * Represents an API request payload that asks the Knora API server to return Ckan data + * + * @param projects + * @param limit + * @param info + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile + */ +case class CkanRequestV1( + projects: Option[Seq[String]], + limit: Option[Int], + info: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM +) extends CkanResponderRequestV1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // API response /** - * Represents an API response with the Ckan data - * - * @param projects - */ + * Represents an API response with the Ckan data + * + * @param projects + */ case class CkanResponseV1(projects: Seq[CkanProjectV1]) extends KnoraResponseV1 { def toJsValue = CkanV1JsonProtocol.ckanResponseV1Format.write(this) } @@ -65,26 +66,29 @@ case class CkanResponseV1(projects: Seq[CkanProjectV1]) extends KnoraResponseV1 // Components of messages /** - * - * @param project_info - * @param project_datasets - */ + * @param project_info + * @param project_datasets + */ case class CkanProjectV1(project_info: CkanProjectInfoV1, project_datasets: Option[Seq[CkanProjectDatasetV1]] = None) case class CkanProjectInfoV1(shortname: String, longname: String, ckan_tags: Seq[String], ckan_license_id: String) -case class CkanProjectDatasetV1(ckan_title: String, - ckan_tags: Seq[String], - files: Seq[CkanProjectDatasetFileV1], - other_props: Map[String, String]) - -case class CkanProjectDatasetFileV1(ckan_title: String, - ckan_description: Option[String] = None, - data_url: String, - data_mimetype: String, - source_url: String, - source_mimetype: String, - other_props: Option[Map[String, String]] = None) +case class CkanProjectDatasetV1( + ckan_title: String, + ckan_tags: Seq[String], + files: Seq[CkanProjectDatasetFileV1], + other_props: Map[String, String] +) + +case class CkanProjectDatasetFileV1( + ckan_title: String, + ckan_description: Option[String] = None, + data_url: String, + data_mimetype: String, + source_url: String, + source_mimetype: String, + other_props: Option[Map[String, String]] = None +) // dokubib structure /* @@ -189,12 +193,13 @@ case class IncunabulaCkanProjectDatasetFileV1(resid: String, // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON for Ckan. - */ + * A spray-json protocol for generating Knora API v1 JSON for Ckan. + */ object CkanV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol { implicit val ckanProjectDatasetFileV1Format: JsonFormat[CkanProjectDatasetFileV1] = jsonFormat7( - CkanProjectDatasetFileV1) + CkanProjectDatasetFileV1 + ) implicit val ckanProjectDatasetV1Format: JsonFormat[CkanProjectDatasetV1] = jsonFormat4(CkanProjectDatasetV1) implicit val ckanProjectInfoV1Format: JsonFormat[CkanProjectInfoV1] = jsonFormat4(CkanProjectInfoV1) implicit val ckanProjectV1Format: JsonFormat[CkanProjectV1] = jsonFormat2(CkanProjectV1) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/listmessages/ListMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/listmessages/ListMessagesV1.scala index dd57323d00..db6fa0a900 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/listmessages/ListMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/listmessages/ListMessagesV1.scala @@ -32,66 +32,66 @@ import spray.json._ // Messages /** - * An abstract trait for messages that can be sent to `HierarchicalListsResponderV1`. - */ + * An abstract trait for messages that can be sent to `HierarchicalListsResponderV1`. + */ sealed trait ListsResponderRequestV1 extends KnoraRequestV1 /** - * Requests a list. A successful response will be a [[HListGetResponseV1]]. - * - * @param iri the IRI of the list. - * @param userProfile the profile of the user making the request. - */ + * Requests a list. A successful response will be a [[HListGetResponseV1]]. + * + * @param iri the IRI of the list. + * @param userProfile the profile of the user making the request. + */ case class HListGetRequestV1(iri: IRI, userProfile: UserProfileV1) extends ListsResponderRequestV1 /** - * Requests a selection (flat list). A successful response will be a [[SelectionGetResponseV1]]. - * - * @param iri the IRI of the list. - * @param userProfile the profile of the user making the request. - */ + * Requests a selection (flat list). A successful response will be a [[SelectionGetResponseV1]]. + * + * @param iri the IRI of the list. + * @param userProfile the profile of the user making the request. + */ case class SelectionGetRequestV1(iri: IRI, userProfile: UserProfileV1) extends ListsResponderRequestV1 /** - * Requests the path from the root node of a list to a particular node. A successful response will be - * a [[NodePathGetResponseV1]]. - * - * @param iri the IRI of the node. - * @param userProfile the profile of the user making the request. - */ + * Requests the path from the root node of a list to a particular node. A successful response will be + * a [[NodePathGetResponseV1]]. + * + * @param iri the IRI of the node. + * @param userProfile the profile of the user making the request. + */ case class NodePathGetRequestV1(iri: IRI, userProfile: UserProfileV1) extends ListsResponderRequestV1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Responses /** - * An abstract class extended by `HListGetResponseV1` and `SelectionGetResponseV1`. - */ + * An abstract class extended by `HListGetResponseV1` and `SelectionGetResponseV1`. + */ sealed abstract class ListGetResponseV1 extends KnoraResponseV1 /** - * Provides a hierarchical list representing a "hlist" in the old SALSAH. - * - * @param hlist the list requested. - */ + * Provides a hierarchical list representing a "hlist" in the old SALSAH. + * + * @param hlist the list requested. + */ case class HListGetResponseV1(hlist: Seq[ListNodeV1]) extends ListGetResponseV1 with ListV1JsonProtocol { def toJsValue = hlistGetResponseV1Format.write(this) } /** - * Provides a hierarchical list representing a "selection" in the old SALSAH. - * - * @param selection the list requested. - */ + * Provides a hierarchical list representing a "selection" in the old SALSAH. + * + * @param selection the list requested. + */ case class SelectionGetResponseV1(selection: Seq[ListNodeV1]) extends ListGetResponseV1 with ListV1JsonProtocol { def toJsValue = selectionGetResponseV1Format.write(this) } /** - * Responds to a [[NodePathGetRequestV1]] by providing the path to a particular hierarchical list node. - * - * @param nodelist a list of the nodes composing the path from the list's root node up to and including the specified node. - */ + * Responds to a [[NodePathGetRequestV1]] by providing the path to a particular hierarchical list node. + * + * @param nodelist a list of the nodes composing the path from the list's root node up to and including the specified node. + */ case class NodePathGetResponseV1(nodelist: Seq[NodePathElementV1]) extends ListGetResponseV1 with ListV1JsonProtocol { def toJsValue = nodePathGetResponseV1Format.write(this) } @@ -100,35 +100,37 @@ case class NodePathGetResponseV1(nodelist: Seq[NodePathElementV1]) extends ListG // Components of messages /** - * Represents a hierarchical list node in Knora API v1 format. - * - * @param id the IRI of the list node. - * @param name the name of the list node. - * @param label the label of the list node. - * @param children the list node's child nodes. - * @param level the depth of the node in the tree. - * @param position the position of the node among its siblings. - */ -case class ListNodeV1(id: IRI, - name: Option[String], - label: Option[String], - children: Seq[ListNodeV1], - level: Int, - position: Int) + * Represents a hierarchical list node in Knora API v1 format. + * + * @param id the IRI of the list node. + * @param name the name of the list node. + * @param label the label of the list node. + * @param children the list node's child nodes. + * @param level the depth of the node in the tree. + * @param position the position of the node among its siblings. + */ +case class ListNodeV1( + id: IRI, + name: Option[String], + label: Option[String], + children: Seq[ListNodeV1], + level: Int, + position: Int +) /** - * Represents a node on a hierarchical list path. - * - * @param id the IRI of the list node. - * @param name the name of the list node. - * @param label the label of the list node. - */ + * Represents a node on a hierarchical list path. + * + * @param id the IRI of the list node. + * @param name the name of the list node. + * @param label the label of the list node. + */ case class NodePathElementV1(id: IRI, name: Option[String], label: Option[String]) /** - * An enumeration whose values correspond to the types of hierarchical list objects that [[org.knora.webapi.responders.v1.ListsResponderV1]] actor can - * produce: "hlists" | "selections". - */ + * An enumeration whose values correspond to the types of hierarchical list objects that [[org.knora.webapi.responders.v1.ListsResponderV1]] actor can + * produce: "hlists" | "selections". + */ object PathType extends Enumeration { val HList, Selection = Value } @@ -137,18 +139,18 @@ object PathType extends Enumeration { // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON providing data about lists. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about lists. + */ trait ListV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { implicit object HierarchicalListV1JsonFormat extends JsonFormat[ListNodeV1] { /** - * Recursively converts a [[ListNodeV1]] to a [[JsValue]]. - * - * @param tree a [[ListNodeV1]]. - * @return a [[JsValue]]. - */ + * Recursively converts a [[ListNodeV1]] to a [[JsValue]]. + * + * @param tree a [[ListNodeV1]]. + * @return a [[JsValue]]. + */ def write(tree: ListNodeV1): JsValue = { // Does the node have children? val childrenOption = if (tree.children.nonEmpty) { @@ -170,8 +172,8 @@ trait ListV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with } /** - * Not implemented. - */ + * Not implemented. + */ def read(value: JsValue): ListNodeV1 = ??? } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ontologymessages/OntologyMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ontologymessages/OntologyMessagesV1.scala index 00f78d9f7b..354e535a45 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ontologymessages/OntologyMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/ontologymessages/OntologyMessagesV1.scala @@ -36,312 +36,320 @@ import spray.json._ // Messages /** - * An abstract trait representing a message that can be sent to `OntologyResponderV1`. - */ + * An abstract trait representing a message that can be sent to `OntologyResponderV1`. + */ sealed trait OntologyResponderRequestV1 extends KnoraRequestV1 /** - * Requests that all ontologies in the repository are loaded. This message must be sent only once, when the application - * starts, before it accepts any API requests. A successful response will be a [[LoadOntologiesResponse]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - */ + * Requests that all ontologies in the repository are loaded. This message must be sent only once, when the application + * starts, before it accepts any API requests. A successful response will be a [[LoadOntologiesResponse]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + */ case class LoadOntologiesRequestV1(featureFactoryConfig: FeatureFactoryConfig, userADM: UserADM) extends OntologyResponderRequestV1 /** - * Indicates that all ontologies were loaded. - */ + * Indicates that all ontologies were loaded. + */ case class LoadOntologiesResponse() extends KnoraResponseV1 { def toJsValue = JsObject(Map("result" -> JsString("Ontologies loaded."))) } /** - * Requests all available information about a list of ontology entities (resource classes and/or properties). A successful response will be an - * [[EntityInfoGetResponseV1]]. - * - * @param resourceClassIris the IRIs of the resource classes to be queried. - * @param propertyIris the IRIs of the properties to be queried. - * @param userProfile the profile of the user making the request. - */ -case class EntityInfoGetRequestV1(resourceClassIris: Set[IRI] = Set.empty[IRI], - propertyIris: Set[IRI] = Set.empty[IRI], - userProfile: UserADM) - extends OntologyResponderRequestV1 + * Requests all available information about a list of ontology entities (resource classes and/or properties). A successful response will be an + * [[EntityInfoGetResponseV1]]. + * + * @param resourceClassIris the IRIs of the resource classes to be queried. + * @param propertyIris the IRIs of the properties to be queried. + * @param userProfile the profile of the user making the request. + */ +case class EntityInfoGetRequestV1( + resourceClassIris: Set[IRI] = Set.empty[IRI], + propertyIris: Set[IRI] = Set.empty[IRI], + userProfile: UserADM +) extends OntologyResponderRequestV1 /** - * Represents assertions about one or more ontology entities (resource classes and/or properties). - * - * @param resourceClassInfoMap a [[Map]] of resource class IRIs to [[ClassInfoV1]] objects. - * @param propertyInfoMap a [[Map]] of property IRIs to [[PropertyInfoV1]] objects. - */ -case class EntityInfoGetResponseV1(resourceClassInfoMap: Map[IRI, ClassInfoV1], - propertyInfoMap: Map[IRI, PropertyInfoV1]) - -/** - * Requests all available information about a list of ontology entities (standoff classes and/or properties). A successful response will be an - * [[StandoffEntityInfoGetResponseV1]]. - * - * @param standoffClassIris the IRIs of the resource entities to be queried. - * @param standoffPropertyIris the IRIs of the property entities to be queried. - * @param userProfile the profile of the user making the request. - */ -case class StandoffEntityInfoGetRequestV1(standoffClassIris: Set[IRI] = Set.empty[IRI], - standoffPropertyIris: Set[IRI] = Set.empty[IRI], - userProfile: UserADM) - extends OntologyResponderRequestV1 + * Represents assertions about one or more ontology entities (resource classes and/or properties). + * + * @param resourceClassInfoMap a [[Map]] of resource class IRIs to [[ClassInfoV1]] objects. + * @param propertyInfoMap a [[Map]] of property IRIs to [[PropertyInfoV1]] objects. + */ +case class EntityInfoGetResponseV1( + resourceClassInfoMap: Map[IRI, ClassInfoV1], + propertyInfoMap: Map[IRI, PropertyInfoV1] +) /** - * Represents assertions about one or more ontology entities (resource classes and/or properties). - * - * @param standoffClassInfoMap a [[Map]] of resource class IRIs to [[ClassInfoV1]] objects. - * @param standoffPropertyInfoMap a [[Map]] of property IRIs to [[PropertyInfoV1]] objects. - */ -case class StandoffEntityInfoGetResponseV1(standoffClassInfoMap: Map[IRI, ClassInfoV1], - standoffPropertyInfoMap: Map[IRI, PropertyInfoV1]) + * Requests all available information about a list of ontology entities (standoff classes and/or properties). A successful response will be an + * [[StandoffEntityInfoGetResponseV1]]. + * + * @param standoffClassIris the IRIs of the resource entities to be queried. + * @param standoffPropertyIris the IRIs of the property entities to be queried. + * @param userProfile the profile of the user making the request. + */ +case class StandoffEntityInfoGetRequestV1( + standoffClassIris: Set[IRI] = Set.empty[IRI], + standoffPropertyIris: Set[IRI] = Set.empty[IRI], + userProfile: UserADM +) extends OntologyResponderRequestV1 /** - * Requests information about all standoff classes that are a subclass of a data type standoff class. A successful response will be an - * [[StandoffClassesWithDataTypeGetResponseV1]]. - * - * @param userProfile the profile of the user making the request. - */ + * Represents assertions about one or more ontology entities (resource classes and/or properties). + * + * @param standoffClassInfoMap a [[Map]] of resource class IRIs to [[ClassInfoV1]] objects. + * @param standoffPropertyInfoMap a [[Map]] of property IRIs to [[PropertyInfoV1]] objects. + */ +case class StandoffEntityInfoGetResponseV1( + standoffClassInfoMap: Map[IRI, ClassInfoV1], + standoffPropertyInfoMap: Map[IRI, PropertyInfoV1] +) + +/** + * Requests information about all standoff classes that are a subclass of a data type standoff class. A successful response will be an + * [[StandoffClassesWithDataTypeGetResponseV1]]. + * + * @param userProfile the profile of the user making the request. + */ case class StandoffClassesWithDataTypeGetRequestV1(userProfile: UserADM) extends OntologyResponderRequestV1 /** - * Represents assertions about all standoff classes that are a subclass of a data type standoff class. - * - * @param standoffClassInfoMap a [[Map]] of resource class IRIs to [[ClassInfoV1]] objects. - */ + * Represents assertions about all standoff classes that are a subclass of a data type standoff class. + * + * @param standoffClassInfoMap a [[Map]] of resource class IRIs to [[ClassInfoV1]] objects. + */ case class StandoffClassesWithDataTypeGetResponseV1(standoffClassInfoMap: Map[IRI, ClassInfoV1]) /** - * Requests information about all standoff properties. A successful response will be an - * [[StandoffAllPropertiesGetResponseV1]]. - * - * @param userProfile the profile of the user making the request. - */ + * Requests information about all standoff properties. A successful response will be an + * [[StandoffAllPropertiesGetResponseV1]]. + * + * @param userProfile the profile of the user making the request. + */ case class StandoffAllPropertiesGetRequestV1(userProfile: UserADM) extends OntologyResponderRequestV1 /** - * Represents assertions about all standoff all standoff property entities. - * - * @param standoffAllPropertiesInfoMap a [[Map]] of resource entity IRIs to [[PropertyInfoV1]] objects. - */ + * Represents assertions about all standoff all standoff property entities. + * + * @param standoffAllPropertiesInfoMap a [[Map]] of resource entity IRIs to [[PropertyInfoV1]] objects. + */ case class StandoffAllPropertiesGetResponseV1(standoffAllPropertiesInfoMap: Map[IRI, PropertyInfoV1]) /** - * Requests information about a resource type and its possible properties. A successful response will be a - * [[ResourceTypeResponseV1]]. - * - * @param resourceTypeIri the IRI of the resource type to be queried. - * @param userProfile the profile of the user making the request. - */ + * Requests information about a resource type and its possible properties. A successful response will be a + * [[ResourceTypeResponseV1]]. + * + * @param resourceTypeIri the IRI of the resource type to be queried. + * @param userProfile the profile of the user making the request. + */ case class ResourceTypeGetRequestV1(resourceTypeIri: IRI, userProfile: UserADM) extends OntologyResponderRequestV1 /** - * Represents the Knora API v1 JSON response to a request for information about a resource type. - * - * @param restype_info basic information about the resource type. - */ + * Represents the Knora API v1 JSON response to a request for information about a resource type. + * + * @param restype_info basic information about the resource type. + */ case class ResourceTypeResponseV1(restype_info: ResTypeInfoV1) extends KnoraResponseV1 { def toJsValue = ResourceTypeV1JsonProtocol.resourceTypeResponseV1Format.write(this) } /** - * Checks whether a Knora resource or value class is a subclass of (or identical to) another class. This message is used - * internally by Knora, and is not part of Knora API v1. A successful response will be a [[CheckSubClassResponseV1]]. - * - * @param subClassIri the IRI of the subclass. - * @param superClassIri the IRI of the superclass. - */ + * Checks whether a Knora resource or value class is a subclass of (or identical to) another class. This message is used + * internally by Knora, and is not part of Knora API v1. A successful response will be a [[CheckSubClassResponseV1]]. + * + * @param subClassIri the IRI of the subclass. + * @param superClassIri the IRI of the superclass. + */ case class CheckSubClassRequestV1(subClassIri: IRI, superClassIri: IRI, userProfile: UserADM) extends OntologyResponderRequestV1 /** - * Represents a response to a [[CheckSubClassRequestV1]]. - * - * @param isSubClass `true` if the requested inheritance relationship exists. - */ + * Represents a response to a [[CheckSubClassRequestV1]]. + * + * @param isSubClass `true` if the requested inheritance relationship exists. + */ case class CheckSubClassResponseV1(isSubClass: Boolean) /** - * Requests information about named graphs containing ontologies. This corresponds to the concept of vocabularies in - * the SALSAH prototype. - * - * @param projectIris the IRIs of the projects for which named graphs should be returned. If this set is empty, information - * about all ontology named graphs is returned. - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - */ -case class NamedGraphsGetRequestV1(projectIris: Set[IRI] = Set.empty[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM) - extends OntologyResponderRequestV1 + * Requests information about named graphs containing ontologies. This corresponds to the concept of vocabularies in + * the SALSAH prototype. + * + * @param projectIris the IRIs of the projects for which named graphs should be returned. If this set is empty, information + * about all ontology named graphs is returned. + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + */ +case class NamedGraphsGetRequestV1( + projectIris: Set[IRI] = Set.empty[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM +) extends OntologyResponderRequestV1 /** - * Represents the Knora API V1 response to a [[NamedGraphsGetRequestV1]]. - * - * @param vocabularies information about named graphs containing ontologies. - */ + * Represents the Knora API V1 response to a [[NamedGraphsGetRequestV1]]. + * + * @param vocabularies information about named graphs containing ontologies. + */ case class NamedGraphsResponseV1(vocabularies: Seq[NamedGraphV1]) extends KnoraResponseV1 { def toJsValue = ResourceTypeV1JsonProtocol.namedGraphsResponseV1Format.write(this) } /** - * Requests all resource classes that are defined in the given named graph. - * - * @param namedGraph the named graph for which the resource classes shall be returned. - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - */ -case class ResourceTypesForNamedGraphGetRequestV1(namedGraph: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM) - extends OntologyResponderRequestV1 + * Requests all resource classes that are defined in the given named graph. + * + * @param namedGraph the named graph for which the resource classes shall be returned. + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + */ +case class ResourceTypesForNamedGraphGetRequestV1( + namedGraph: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM +) extends OntologyResponderRequestV1 /** - * Represents the Knora API V1 response to a [[ResourceTypesForNamedGraphGetRequestV1]]. - * It contains all the resource classes for a named graph. - * - * @param resourcetypes the resource classes for the queried named graph. - */ + * Represents the Knora API V1 response to a [[ResourceTypesForNamedGraphGetRequestV1]]. + * It contains all the resource classes for a named graph. + * + * @param resourcetypes the resource classes for the queried named graph. + */ case class ResourceTypesForNamedGraphResponseV1(resourcetypes: Seq[ResourceTypeV1]) extends KnoraResponseV1 { def toJsValue = ResourceTypeV1JsonProtocol.resourceTypesForNamedGraphResponseV1Format.write(this) } /** - * Requests all property types that are defined in the given named graph. - * If the named graph is not set, the property types of all named graphs are requested. - * - * @param namedGraph the named graph to query for or None if all the named graphs should be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - */ -case class PropertyTypesForNamedGraphGetRequestV1(namedGraph: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM) - extends OntologyResponderRequestV1 + * Requests all property types that are defined in the given named graph. + * If the named graph is not set, the property types of all named graphs are requested. + * + * @param namedGraph the named graph to query for or None if all the named graphs should be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + */ +case class PropertyTypesForNamedGraphGetRequestV1( + namedGraph: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM +) extends OntologyResponderRequestV1 /** - * Represents the Knora API V1 response to a [[PropertyTypesForNamedGraphGetRequestV1]]. - * It contains all property types for the requested named graph. - * - * @param properties the property types for the requested named graph. - */ + * Represents the Knora API V1 response to a [[PropertyTypesForNamedGraphGetRequestV1]]. + * It contains all property types for the requested named graph. + * + * @param properties the property types for the requested named graph. + */ case class PropertyTypesForNamedGraphResponseV1(properties: Seq[PropertyDefinitionInNamedGraphV1]) extends KnoraResponseV1 { def toJsValue = ResourceTypeV1JsonProtocol.propertyTypesForNamedGraphResponseV1Format.write(this) } /** - * Gets all property types that are defined for the given resource class. - * - * @param resourceClassIri the IRI of the resource class to query for. - * @param userProfile the profile of the user making the request. - */ + * Gets all property types that are defined for the given resource class. + * + * @param resourceClassIri the IRI of the resource class to query for. + * @param userProfile the profile of the user making the request. + */ case class PropertyTypesForResourceTypeGetRequestV1(resourceClassIri: IRI, userProfile: UserADM) extends OntologyResponderRequestV1 /** - * Represents the Knora API V1 response to a [[PropertyTypesForResourceTypeGetRequestV1]]. - * It contains all the property types for the requested resource class. - * - * @param properties the property types for the requested resource class. - */ + * Represents the Knora API V1 response to a [[PropertyTypesForResourceTypeGetRequestV1]]. + * It contains all the property types for the requested resource class. + * + * @param properties the property types for the requested resource class. + */ case class PropertyTypesForResourceTypeResponseV1(properties: Vector[PropertyDefinitionV1]) extends KnoraResponseV1 { def toJsValue = ResourceTypeV1JsonProtocol.propertyTypesForResourceTypeResponseV1Format.write(this) } /** - * Requests information about the subclasses of a Knora resource class. A successful response will be - * a [[SubClassesGetResponseV1]]. - * - * @param resourceClassIri the IRI of the Knora resource class. - * @param userADM the profile of the user making the request. - */ + * Requests information about the subclasses of a Knora resource class. A successful response will be + * a [[SubClassesGetResponseV1]]. + * + * @param resourceClassIri the IRI of the Knora resource class. + * @param userADM the profile of the user making the request. + */ case class SubClassesGetRequestV1(resourceClassIri: IRI, userADM: UserADM) extends OntologyResponderRequestV1 /** - * Provides information about the subclasses of a Knora resource class. - * - * @param subClasses a list of [[SubClassInfoV1]] representing the subclasses of the specified class. - */ + * Provides information about the subclasses of a Knora resource class. + * + * @param subClasses a list of [[SubClassInfoV1]] representing the subclasses of the specified class. + */ case class SubClassesGetResponseV1(subClasses: Seq[SubClassInfoV1]) extends KnoraResponseV1 { def toJsValue = ResourceTypeV1JsonProtocol.subClassesGetResponseV1Format.write(this) } /** - * Requests information about the ontology entities in the specified named graph. A successful response will be a - * [[NamedGraphEntityInfoV1]]. - * - * @param namedGraphIri the IRI of the named graph. - * @param userProfile the profile of the user making the request. - */ + * Requests information about the ontology entities in the specified named graph. A successful response will be a + * [[NamedGraphEntityInfoV1]]. + * + * @param namedGraphIri the IRI of the named graph. + * @param userProfile the profile of the user making the request. + */ case class NamedGraphEntityInfoRequestV1(namedGraphIri: IRI, userProfile: UserADM) extends OntologyResponderRequestV1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Components of messages /** - * Represents a predicate that is asserted about a given ontology entity, and the objects of that predicate. - */ + * Represents a predicate that is asserted about a given ontology entity, and the objects of that predicate. + */ class PredicateInfoV1(predicateInfoV2: PredicateInfoV2) { /** - * Returns the IRI of the predicate. - */ + * Returns the IRI of the predicate. + */ def predicateIri: IRI = predicateInfoV2.predicateIri.toString /** - * Returns the objects of the predicate that have no language codes. - */ + * Returns the objects of the predicate that have no language codes. + */ def objects: Set[String] = - predicateInfoV2.objects - .filter { - case StringLiteralV2(_, Some(_)) => false - case _ => true - } + predicateInfoV2.objects.filter { + case StringLiteralV2(_, Some(_)) => false + case _ => true + } .map(_.toString) .toSet /** - * Returns the objects of the predicate that have language codes: a Map of language codes to literals. - */ + * Returns the objects of the predicate that have language codes: a Map of language codes to literals. + */ def objectsWithLang: Map[String, String] = - predicateInfoV2.objects.collect { - case StringLiteralV2(str, Some(lang)) => lang -> str + predicateInfoV2.objects.collect { case StringLiteralV2(str, Some(lang)) => + lang -> str }.toMap } /** - * Represents information about an OWL class or property. - */ + * Represents information about an OWL class or property. + */ sealed trait EntityInfoV1 { protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance protected def entityInfoContent: EntityInfoContentV2 /** - * Returns a [[Map]] of predicate IRIs to [[PredicateInfoV1]] objects. - */ + * Returns a [[Map]] of predicate IRIs to [[PredicateInfoV1]] objects. + */ lazy val predicates: Map[IRI, PredicateInfoV1] = { - entityInfoContent.predicates.map { - case (smartIri, predicateInfoV2) => smartIri.toString -> new PredicateInfoV1(predicateInfoV2) + entityInfoContent.predicates.map { case (smartIri, predicateInfoV2) => + smartIri.toString -> new PredicateInfoV1(predicateInfoV2) } } /** - * Returns an object for a given predicate. If requested, attempts to return the object in the user's preferred - * language, in the system's default language, or in any language, in that order. - * - * @param predicateIri the IRI of the predicate. - * @param preferredLangs the user's preferred language and the system's default language. - * @return an object for the predicate, or [[None]] if this entity doesn't have the specified predicate, or - * if the predicate has no objects. - */ - def getPredicateObject(predicateIri: IRI, preferredLangs: Option[(String, String)] = None): Option[String] = { + * Returns an object for a given predicate. If requested, attempts to return the object in the user's preferred + * language, in the system's default language, or in any language, in that order. + * + * @param predicateIri the IRI of the predicate. + * @param preferredLangs the user's preferred language and the system's default language. + * @return an object for the predicate, or [[None]] if this entity doesn't have the specified predicate, or + * if the predicate has no objects. + */ + def getPredicateObject(predicateIri: IRI, preferredLangs: Option[(String, String)] = None): Option[String] = entityInfoContent.getPredicateStringLiteralObject( predicateIri = predicateIri.toSmartIri, preferredLangs = preferredLangs @@ -349,165 +357,161 @@ sealed trait EntityInfoV1 { case Some(obj) => Some(obj) case None => predicates.get(predicateIri).flatMap(_.objects.headOption) } - } /** - * Returns all the string (non-IRI) objects specified without language tags for a given predicate. - * - * @param predicateIri the IRI of the predicate. - * @return the predicate's objects, or an empty set if this entity doesn't have the specified predicate. - */ - def getPredicateStringObjectsWithoutLang(predicateIri: IRI): Set[String] = { + * Returns all the string (non-IRI) objects specified without language tags for a given predicate. + * + * @param predicateIri the IRI of the predicate. + * @return the predicate's objects, or an empty set if this entity doesn't have the specified predicate. + */ + def getPredicateStringObjectsWithoutLang(predicateIri: IRI): Set[String] = entityInfoContent.getPredicateStringLiteralObjectsWithoutLang(predicateIri.toSmartIri).toSet - } } /** - * Represents the assertions about an OWL class. - */ + * Represents the assertions about an OWL class. + */ class ClassInfoV1(classInfoV2: ReadClassInfoV2) extends EntityInfoV1 { override protected def entityInfoContent: EntityInfoContentV2 = classInfoV2.entityInfoContent /** - * Returns the IRI of the resource class. - */ + * Returns the IRI of the resource class. + */ def resourceClassIri: IRI = classInfoV2.entityInfoContent.classIri.toString /** - * Returns the IRIs of all the base classes of the resource class. - */ + * Returns the IRIs of all the base classes of the resource class. + */ def subClassOf: Set[IRI] = classInfoV2.entityInfoContent.subClassOf.map(_.toString) - def allCardinalities: Map[IRI, KnoraCardinalityInfo] = { - classInfoV2.allCardinalities.map { - case (smartIri, cardinality) => smartIri.toString -> cardinality + def allCardinalities: Map[IRI, KnoraCardinalityInfo] = + classInfoV2.allCardinalities.map { case (smartIri, cardinality) => + smartIri.toString -> cardinality } - } /** - * Returns a [[Map]] of properties to [[Value]] objects representing the resource class's - * cardinalities on those properties. - */ - def knoraResourceCardinalities: Map[IRI, KnoraCardinalityInfo] = { - classInfoV2.allResourcePropertyCardinalities.map { - case (smartIri, cardinality) => smartIri.toString -> cardinality + * Returns a [[Map]] of properties to [[Value]] objects representing the resource class's + * cardinalities on those properties. + */ + def knoraResourceCardinalities: Map[IRI, KnoraCardinalityInfo] = + classInfoV2.allResourcePropertyCardinalities.map { case (smartIri, cardinality) => + smartIri.toString -> cardinality } - } /** - * Returns a [[Set]] of IRIs of properties of the resource class that point to other resources. - */ + * Returns a [[Set]] of IRIs of properties of the resource class that point to other resources. + */ def linkProperties: Set[IRI] = classInfoV2.linkProperties.map(_.toString) /** - * Returns a [[Set]] of IRIs of properties of the resource class that point to `LinkValue` objects. - */ + * Returns a [[Set]] of IRIs of properties of the resource class that point to `LinkValue` objects. + */ def linkValueProperties: Set[IRI] = classInfoV2.linkValueProperties.map(_.toString) /** - * Returns a [[Set]] of IRIs of properties of the resource class that point to `FileValue` objects. - */ + * Returns a [[Set]] of IRIs of properties of the resource class that point to `FileValue` objects. + */ def fileValueProperties: Set[IRI] = classInfoV2.fileValueProperties.map(_.toString) /** - * If this is a standoff tag class, returns the standoff datatype tag class (if any) that it - * is a subclass of. - */ + * If this is a standoff tag class, returns the standoff datatype tag class (if any) that it + * is a subclass of. + */ def standoffDataType: Option[StandoffDataTypeClasses.Value] = classInfoV2.standoffDataType } /** - * Represents the assertions about an OWL property. - */ + * Represents the assertions about an OWL property. + */ class PropertyInfoV1(propertyInfoV2: ReadPropertyInfoV2) extends EntityInfoV1 { override protected def entityInfoContent: EntityInfoContentV2 = propertyInfoV2.entityInfoContent /** - * Returns the IRI of the queried property. - */ + * Returns the IRI of the queried property. + */ def propertyIri: IRI = propertyInfoV2.entityInfoContent.propertyIri.toString /** - * Returns the IRI of the ontology in which the property is defined. - */ + * Returns the IRI of the ontology in which the property is defined. + */ def ontologyIri: IRI = propertyInfoV2.entityInfoContent.propertyIri.getOntologyFromEntity.toString /** - * Returns `true` if the property is a subproperty of `knora-base:hasLinkTo`. - */ + * Returns `true` if the property is a subproperty of `knora-base:hasLinkTo`. + */ def isLinkProp: Boolean = propertyInfoV2.isLinkProp /** - * Returns `true` if the property is a subproperty of `knora-base:hasLinkToValue`. - */ + * Returns `true` if the property is a subproperty of `knora-base:hasLinkToValue`. + */ def isLinkValueProp: Boolean = propertyInfoV2.isLinkValueProp /** - * Returns `true` if the property is a subproperty of `knora-base:hasFileValue`. - */ + * Returns `true` if the property is a subproperty of `knora-base:hasFileValue`. + */ def isFileValueProp: Boolean = propertyInfoV2.isFileValueProp /** - * Returns `true` if this is a subproperty (directly or indirectly) of - * [[org.knora.webapi.messages.OntologyConstants.KnoraBase.StandoffTagHasInternalReference]]. - */ + * Returns `true` if this is a subproperty (directly or indirectly) of + * [[org.knora.webapi.messages.OntologyConstants.KnoraBase.StandoffTagHasInternalReference]]. + */ def isStandoffInternalReferenceProperty: Boolean = propertyInfoV2.isStandoffInternalReferenceProperty } /** - * Methods to convert v2 ontology classes to v1. - */ + * Methods to convert v2 ontology classes to v1. + */ object ConvertOntologyClassV2ToV1 { /** - * Wraps OWL class information from `OntologyResponderV2` for use in API v1. - */ - def classInfoMapV2ToV1(classInfoMap: Map[SmartIri, ReadClassInfoV2]): Map[IRI, ClassInfoV1] = { - classInfoMap.map { - case (smartIri, classInfoV2) => smartIri.toString -> new ClassInfoV1(classInfoV2) + * Wraps OWL class information from `OntologyResponderV2` for use in API v1. + */ + def classInfoMapV2ToV1(classInfoMap: Map[SmartIri, ReadClassInfoV2]): Map[IRI, ClassInfoV1] = + classInfoMap.map { case (smartIri, classInfoV2) => + smartIri.toString -> new ClassInfoV1(classInfoV2) } - } /** - * Wraps OWL property information from `OntologyResponderV2` for use in API v1. - */ - def propertyInfoMapV2ToV1(propertyInfoMap: Map[SmartIri, ReadPropertyInfoV2]): Map[IRI, PropertyInfoV1] = { - propertyInfoMap.map { - case (smartIri, propertyInfoV2) => smartIri.toString -> new PropertyInfoV1(propertyInfoV2) + * Wraps OWL property information from `OntologyResponderV2` for use in API v1. + */ + def propertyInfoMapV2ToV1(propertyInfoMap: Map[SmartIri, ReadPropertyInfoV2]): Map[IRI, PropertyInfoV1] = + propertyInfoMap.map { case (smartIri, propertyInfoV2) => + smartIri.toString -> new PropertyInfoV1(propertyInfoV2) } - } } /** - * Represents the assertions about a given named graph entity. - * - * @param namedGraphIri the IRI of the named graph. - * @param resourceClasses the resource classes defined in the named graph. - * @param propertyIris the properties defined in the named graph. - */ + * Represents the assertions about a given named graph entity. + * + * @param namedGraphIri the IRI of the named graph. + * @param resourceClasses the resource classes defined in the named graph. + * @param propertyIris the properties defined in the named graph. + */ case class NamedGraphEntityInfoV1(namedGraphIri: IRI, resourceClasses: Set[IRI], propertyIris: Set[IRI]) /** - * Represents information about a resource type. - * - * @param name the IRI of the resource type. - * @param label the label of the resource type. - * @param description a description of the resource type. - * @param iconsrc an icon representing the resource type. - * @param properties a list of definitions of properties that resources of this type can have. - */ -case class ResTypeInfoV1(name: IRI, - label: Option[String], - description: Option[String], - iconsrc: Option[String], - properties: Seq[PropertyDefinitionV1]) - -/** - * Represents information about a property type. It is extended by [[PropertyDefinitionV1]] - * and [[PropertyDefinitionInNamedGraphV1]]. - */ + * Represents information about a resource type. + * + * @param name the IRI of the resource type. + * @param label the label of the resource type. + * @param description a description of the resource type. + * @param iconsrc an icon representing the resource type. + * @param properties a list of definitions of properties that resources of this type can have. + */ +case class ResTypeInfoV1( + name: IRI, + label: Option[String], + description: Option[String], + iconsrc: Option[String], + properties: Seq[PropertyDefinitionV1] +) + +/** + * Represents information about a property type. It is extended by [[PropertyDefinitionV1]] + * and [[PropertyDefinitionInNamedGraphV1]]. + */ trait PropertyDefinitionBaseV1 { val id: IRI val name: IRI @@ -520,101 +524,105 @@ trait PropertyDefinitionBaseV1 { } /** - * Describes a property type that resources of some particular type can have. - * - * @param id the IRI of the property definition. - * @param name the IRI of the property definition. - * @param label the label of the property definition. - * @param description a description of the property definition. - * @param vocabulary the IRI of the vocabulary (i.e. the named graph) that the property definition belongs to. - * @param occurrence the cardinality of this property: 1, 1-n, 0-1, or 0-n. - * @param valuetype_id the IRI of a subclass of `knora-base:Value`, representing the type of value that this property contains. - * @param attributes HTML attributes to be used with the property's GUI element. - * @param gui_name the IRI of a named individual of type `salsah-gui:Guielement`, representing the type of GUI element - * that should be used for inputting values for this property. - * @param guiorder the property's order among the properties defined on some particular class. - */ -case class PropertyDefinitionV1(id: IRI, - name: IRI, - label: Option[String], - description: Option[String], - vocabulary: IRI, - occurrence: String, - valuetype_id: IRI, - attributes: Option[String], - gui_name: Option[String], - guiorder: Option[Int] = None) - extends PropertyDefinitionBaseV1 - -/** - * Describes a property type that a named graph contains. - * - * @param id the IRI of the property definition. - * @param name the IRI of the property definition. - * @param label the label of the property definition. - * @param description a description of the property definition. - * @param vocabulary the IRI of the vocabulary (i.e. the named graph) that the property definition belongs to. - * @param valuetype_id the IRI of a subclass of `knora-base:Value`, representing the type of value that this property contains. - * @param attributes HTML attributes to be used with the property's GUI element. - * @param gui_name the IRI of a named individual of type `salsah-gui:Guielement`, representing the type of GUI element - * that should be used for inputting values for this property. - */ -case class PropertyDefinitionInNamedGraphV1(id: IRI, - name: IRI, - label: Option[String], - description: Option[String], - vocabulary: IRI, - valuetype_id: IRI, - attributes: Option[String], - gui_name: Option[String]) - extends PropertyDefinitionBaseV1 - -/** - * Represents a named graph (corresponds to a vocabulary in the SALSAH prototype). - * - * @param id the id of the named graph. - * @param shortname the short name of the named graph. - * @param longname the full name of the named graph. - * @param description a description of the named graph. - * @param project_id the project belonging to the named graph. - * @param uri the IRI of the named graph. - * @param active indicates if this is named graph the user's project belongs to. - */ -case class NamedGraphV1(id: IRI, - shortname: String, - longname: String, - description: String, - project_id: IRI, - uri: IRI, - active: Boolean) { + * Describes a property type that resources of some particular type can have. + * + * @param id the IRI of the property definition. + * @param name the IRI of the property definition. + * @param label the label of the property definition. + * @param description a description of the property definition. + * @param vocabulary the IRI of the vocabulary (i.e. the named graph) that the property definition belongs to. + * @param occurrence the cardinality of this property: 1, 1-n, 0-1, or 0-n. + * @param valuetype_id the IRI of a subclass of `knora-base:Value`, representing the type of value that this property contains. + * @param attributes HTML attributes to be used with the property's GUI element. + * @param gui_name the IRI of a named individual of type `salsah-gui:Guielement`, representing the type of GUI element + * that should be used for inputting values for this property. + * @param guiorder the property's order among the properties defined on some particular class. + */ +case class PropertyDefinitionV1( + id: IRI, + name: IRI, + label: Option[String], + description: Option[String], + vocabulary: IRI, + occurrence: String, + valuetype_id: IRI, + attributes: Option[String], + gui_name: Option[String], + guiorder: Option[Int] = None +) extends PropertyDefinitionBaseV1 + +/** + * Describes a property type that a named graph contains. + * + * @param id the IRI of the property definition. + * @param name the IRI of the property definition. + * @param label the label of the property definition. + * @param description a description of the property definition. + * @param vocabulary the IRI of the vocabulary (i.e. the named graph) that the property definition belongs to. + * @param valuetype_id the IRI of a subclass of `knora-base:Value`, representing the type of value that this property contains. + * @param attributes HTML attributes to be used with the property's GUI element. + * @param gui_name the IRI of a named individual of type `salsah-gui:Guielement`, representing the type of GUI element + * that should be used for inputting values for this property. + */ +case class PropertyDefinitionInNamedGraphV1( + id: IRI, + name: IRI, + label: Option[String], + description: Option[String], + vocabulary: IRI, + valuetype_id: IRI, + attributes: Option[String], + gui_name: Option[String] +) extends PropertyDefinitionBaseV1 + +/** + * Represents a named graph (corresponds to a vocabulary in the SALSAH prototype). + * + * @param id the id of the named graph. + * @param shortname the short name of the named graph. + * @param longname the full name of the named graph. + * @param description a description of the named graph. + * @param project_id the project belonging to the named graph. + * @param uri the IRI of the named graph. + * @param active indicates if this is named graph the user's project belongs to. + */ +case class NamedGraphV1( + id: IRI, + shortname: String, + longname: String, + description: String, + project_id: IRI, + uri: IRI, + active: Boolean +) { def toJsValue = ResourceTypeV1JsonProtocol.namedGraphV1Format.write(this) } /** - * Represents information about a subclass of a resource class. - * - * @param id the IRI of the subclass. - * @param label the `rdfs:label` of the subclass. - */ + * Represents information about a subclass of a resource class. + * + * @param id the IRI of the subclass. + * @param label the `rdfs:label` of the subclass. + */ case class SubClassInfoV1(id: IRI, label: String) /** - * Represents a resource class and its properties. - * - * @param id the IRI of the resource class. - * @param label the label of the resource class. - * @param properties the properties of the resource class. - */ + * Represents a resource class and its properties. + * + * @param id the IRI of the resource class. + * @param label the label of the resource class. + * @param properties the properties of the resource class. + */ case class ResourceTypeV1(id: IRI, label: String, properties: Vector[PropertyTypeV1]) { def toJsValue = ResourceTypeV1JsonProtocol.resourceTypeV1Format.write(this) } /** - * Represents a property type. - * - * @param id the IRI of the property type. - * @param label the label of the property type. - */ + * Represents a property type. + * + * @param id the IRI of the property type. + * @param label the label of the property type. + */ case class PropertyTypeV1(id: IRI, label: String) { def toJsValue = ResourceTypeV1JsonProtocol.propertyTypeV1Format.write(this) } @@ -623,16 +631,18 @@ case class PropertyTypeV1(id: IRI, label: String) { // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON providing data about resources and their properties. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about resources and their properties. + */ object ResourceTypeV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { implicit val propertyDefinitionV1Format: JsonFormat[PropertyDefinitionV1] = jsonFormat10(PropertyDefinitionV1) implicit val propertyDefinitionInNamedGraphV1Format: JsonFormat[PropertyDefinitionInNamedGraphV1] = jsonFormat8( - PropertyDefinitionInNamedGraphV1) + PropertyDefinitionInNamedGraphV1 + ) implicit val resTypeInfoV1Format: JsonFormat[ResTypeInfoV1] = jsonFormat5(ResTypeInfoV1) implicit val resourceTypeResponseV1Format: RootJsonFormat[ResourceTypeResponseV1] = jsonFormat1( - ResourceTypeResponseV1) + ResourceTypeResponseV1 + ) implicit val namedGraphV1Format: RootJsonFormat[NamedGraphV1] = jsonFormat7(NamedGraphV1) implicit val namedGraphsResponseV1Format: RootJsonFormat[NamedGraphsResponseV1] = jsonFormat1(NamedGraphsResponseV1) implicit val propertyTypeV1Format: RootJsonFormat[PropertyTypeV1] = jsonFormat2(PropertyTypeV1) @@ -645,5 +655,6 @@ object ResourceTypeV1JsonProtocol extends SprayJsonSupport with DefaultJsonProto jsonFormat1(PropertyTypesForResourceTypeResponseV1) implicit val subClassInfoV1Format: JsonFormat[SubClassInfoV1] = jsonFormat2(SubClassInfoV1) implicit val subClassesGetResponseV1Format: RootJsonFormat[SubClassesGetResponseV1] = jsonFormat1( - SubClassesGetResponseV1) + SubClassesGetResponseV1 + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/projectmessages/ProjectMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/projectmessages/ProjectMessagesV1.scala index 3c21fe1e49..e672953505 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/projectmessages/ProjectMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/projectmessages/ProjectMessagesV1.scala @@ -35,82 +35,85 @@ import spray.json.{DefaultJsonProtocol, JsValue, JsonFormat, NullOptions, RootJs // Messages /** - * An abstract trait representing a request message that can be sent to [[org.knora.webapi.responders.v1.ProjectsResponderV1]]. - */ + * An abstract trait representing a request message that can be sent to [[org.knora.webapi.responders.v1.ProjectsResponderV1]]. + */ sealed trait ProjectsResponderRequestV1 extends KnoraRequestV1 // Requests /** - * Get all information about all projects in form of [[ProjectsResponseV1]]. The ProjectsGetRequestV1 returns either - * something or a NotFound exception if there are no projects found. Administration permission checking is performed. - * - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ + * Get all information about all projects in form of [[ProjectsResponseV1]]. The ProjectsGetRequestV1 returns either + * something or a NotFound exception if there are no projects found. Administration permission checking is performed. + * + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ case class ProjectsGetRequestV1(featureFactoryConfig: FeatureFactoryConfig, userProfile: Option[UserProfileV1]) extends ProjectsResponderRequestV1 /** - * Get all information about all projects in form of a sequence of [[ProjectInfoV1]]. Returns an empty sequence if - * no projects are found. Administration permission checking is skipped. - * - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ + * Get all information about all projects in form of a sequence of [[ProjectInfoV1]]. Returns an empty sequence if + * no projects are found. Administration permission checking is skipped. + * + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ case class ProjectsGetV1(featureFactoryConfig: FeatureFactoryConfig, userProfile: Option[UserProfileV1]) extends ProjectsResponderRequestV1 /** - * Get info about a single project identified through its IRI. A successful response will be a [[ProjectInfoResponseV1]]. - * - * @param iri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfileV1 the profile of the user making the request (optional). - */ -case class ProjectInfoByIRIGetRequestV1(iri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfileV1: Option[UserProfileV1]) - extends ProjectsResponderRequestV1 + * Get info about a single project identified through its IRI. A successful response will be a [[ProjectInfoResponseV1]]. + * + * @param iri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfileV1 the profile of the user making the request (optional). + */ +case class ProjectInfoByIRIGetRequestV1( + iri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfileV1: Option[UserProfileV1] +) extends ProjectsResponderRequestV1 /** - * Get info about a single project identified through its IRI. A successful response will be an [[Option[ProjectInfoV1] ]]. - * - * @param iri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfileV1 the profile of the user making the request (optional). - */ -case class ProjectInfoByIRIGetV1(iri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfileV1: Option[UserProfileV1]) - extends ProjectsResponderRequestV1 + * Get info about a single project identified through its IRI. A successful response will be an [[Option[ProjectInfoV1] ]]. + * + * @param iri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfileV1 the profile of the user making the request (optional). + */ +case class ProjectInfoByIRIGetV1( + iri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfileV1: Option[UserProfileV1] +) extends ProjectsResponderRequestV1 /** - * Find everything about a single project identified through its shortname. - * - * @param shortname of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfileV1 the profile of the user making the request. - */ -case class ProjectInfoByShortnameGetRequestV1(shortname: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfileV1: Option[UserProfileV1]) - extends ProjectsResponderRequestV1 + * Find everything about a single project identified through its shortname. + * + * @param shortname of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfileV1 the profile of the user making the request. + */ +case class ProjectInfoByShortnameGetRequestV1( + shortname: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfileV1: Option[UserProfileV1] +) extends ProjectsResponderRequestV1 // Responses /** - * Represents the Knora API v1 JSON response to a request for information about all projects. - * - * @param projects information about all existing projects. - */ + * Represents the Knora API v1 JSON response to a request for information about all projects. + * + * @param projects information about all existing projects. + */ case class ProjectsResponseV1(projects: Seq[ProjectInfoV1]) extends KnoraResponseV1 with ProjectV1JsonProtocol { def toJsValue: JsValue = projectsResponseV1Format.write(this) } /** - * Represents the Knora API v1 JSON response to a request for information about a single project. - * - * @param project_info all information about the project. - */ + * Represents the Knora API v1 JSON response to a request for information about a single project. + * + * @param project_info all information about the project. + */ case class ProjectInfoResponseV1(project_info: ProjectInfoV1) extends KnoraResponseV1 with ProjectV1JsonProtocol { def toJsValue: JsValue = projectInfoResponseV1Format.write(this) } @@ -119,37 +122,39 @@ case class ProjectInfoResponseV1(project_info: ProjectInfoV1) extends KnoraRespo // Components of messages /** - * Represents basic information about a project. - * - * @param id The project's IRI. - * @param shortname The project's shortname. Needs to be system wide unique. - * @param longname The project's long name. Needs to be system wide unique. - * @param description The project's description. - * @param keywords The project's keywords. - * @param logo The project's logo. - * @param institution The project's institution. - * @param ontologies The project's ontologies. - * @param status The project's status. - * @param selfjoin The project's self-join status. - */ -case class ProjectInfoV1(id: IRI, - shortname: String, - shortcode: String, - longname: Option[String], - description: Option[String], - keywords: Option[String], - logo: Option[String], - institution: Option[IRI], - ontologies: Seq[IRI], - status: Boolean, - selfjoin: Boolean) + * Represents basic information about a project. + * + * @param id The project's IRI. + * @param shortname The project's shortname. Needs to be system wide unique. + * @param longname The project's long name. Needs to be system wide unique. + * @param description The project's description. + * @param keywords The project's keywords. + * @param logo The project's logo. + * @param institution The project's institution. + * @param ontologies The project's ontologies. + * @param status The project's status. + * @param selfjoin The project's self-join status. + */ +case class ProjectInfoV1( + id: IRI, + shortname: String, + shortcode: String, + longname: Option[String], + description: Option[String], + keywords: Option[String], + logo: Option[String], + institution: Option[IRI], + ontologies: Seq[IRI], + status: Boolean, + selfjoin: Boolean +) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formating /** - * A spray-json protocol for generating Knora API v1 JSON providing data about projects. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about projects. + */ trait ProjectV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { // Some of these formatters have to use lazyFormat because there is a recursive dependency between this @@ -158,7 +163,9 @@ trait ProjectV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol wi // https://github.com/spray/spray-json#jsonformats-for-recursive-types implicit val projectInfoV1Format: JsonFormat[ProjectInfoV1] = jsonFormat11(ProjectInfoV1) implicit val projectsResponseV1Format: RootJsonFormat[ProjectsResponseV1] = rootFormat( - lazyFormat(jsonFormat(ProjectsResponseV1, "projects"))) + lazyFormat(jsonFormat(ProjectsResponseV1, "projects")) + ) implicit val projectInfoResponseV1Format: RootJsonFormat[ProjectInfoResponseV1] = rootFormat( - lazyFormat(jsonFormat(ProjectInfoResponseV1, "project_info"))) + lazyFormat(jsonFormat(ProjectInfoResponseV1, "project_info")) + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/resourcemessages/ResourceMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/resourcemessages/ResourceMessagesV1.scala index 3ba3cb38a9..e5034fe172 100755 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/resourcemessages/ResourceMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/resourcemessages/ResourceMessagesV1.scala @@ -43,90 +43,98 @@ import spray.json._ // API requests /** - * Represents an API request payload that asks the Knora API server to create a new resource - * and properties attached to that resource. - * - * @param restype_id the resource type of the resource to be created. - * @param label the rdfs:label of the resource. - * @param properties the properties to be created as a Map of property types to property value(s). - * @param file the filename of a file that has been uploaded to Sipi's temporary storage. - * @param project_id the IRI of the project the resources is added to. - */ -case class CreateResourceApiRequestV1(restype_id: IRI, - label: String, - properties: Map[IRI, Seq[CreateResourceValueV1]], - file: Option[String] = None, - project_id: IRI) { + * Represents an API request payload that asks the Knora API server to create a new resource + * and properties attached to that resource. + * + * @param restype_id the resource type of the resource to be created. + * @param label the rdfs:label of the resource. + * @param properties the properties to be created as a Map of property types to property value(s). + * @param file the filename of a file that has been uploaded to Sipi's temporary storage. + * @param project_id the IRI of the project the resources is added to. + */ +case class CreateResourceApiRequestV1( + restype_id: IRI, + label: String, + properties: Map[IRI, Seq[CreateResourceValueV1]], + file: Option[String] = None, + project_id: IRI +) { def toJsValue: JsValue = ResourceV1JsonProtocol.createResourceApiRequestV1Format.write(this) } /** - * Used internally to represent a request to create a resource from an XML import. - * - * @param restype_id the IRI of the resource class. - * @param label the resource's label. - * @param client_id the client's unique ID for the resource. - * @param properties the resource's properties. - * @param file a file in Sipi's temporary storage that should be attached to the resource. - * @param creationDate the creation date that should be attached to the resource. - */ -case class CreateResourceFromXmlImportRequestV1(restype_id: IRI, - client_id: String, - label: String, - properties: Map[IRI, Seq[CreateResourceValueV1]], - file: Option[String] = None, - creationDate: Option[Instant]) + * Used internally to represent a request to create a resource from an XML import. + * + * @param restype_id the IRI of the resource class. + * @param label the resource's label. + * @param client_id the client's unique ID for the resource. + * @param properties the resource's properties. + * @param file a file in Sipi's temporary storage that should be attached to the resource. + * @param creationDate the creation date that should be attached to the resource. + */ +case class CreateResourceFromXmlImportRequestV1( + restype_id: IRI, + client_id: String, + label: String, + properties: Map[IRI, Seq[CreateResourceValueV1]], + file: Option[String] = None, + creationDate: Option[Instant] +) /** - * Represents a property value to be created. - * - * @param richtext_value a richtext object to be added to the resource. - * @param int_value an integer literal to be used in the value. - */ -case class CreateResourceValueV1(richtext_value: Option[CreateRichtextV1] = None, - link_value: Option[IRI] = None, - link_to_client_id: Option[String] = None, - int_value: Option[Int] = None, - decimal_value: Option[BigDecimal] = None, - boolean_value: Option[Boolean] = None, - uri_value: Option[String] = None, - date_value: Option[String] = None, - color_value: Option[String] = None, - geom_value: Option[String] = None, - hlist_value: Option[IRI] = None, - interval_value: Option[Seq[BigDecimal]] = None, - time_value: Option[String] = None, - geoname_value: Option[String] = None, - comment: Option[String] = None) { + * Represents a property value to be created. + * + * @param richtext_value a richtext object to be added to the resource. + * @param int_value an integer literal to be used in the value. + */ +case class CreateResourceValueV1( + richtext_value: Option[CreateRichtextV1] = None, + link_value: Option[IRI] = None, + link_to_client_id: Option[String] = None, + int_value: Option[Int] = None, + decimal_value: Option[BigDecimal] = None, + boolean_value: Option[Boolean] = None, + uri_value: Option[String] = None, + date_value: Option[String] = None, + color_value: Option[String] = None, + geom_value: Option[String] = None, + hlist_value: Option[IRI] = None, + interval_value: Option[Seq[BigDecimal]] = None, + time_value: Option[String] = None, + geoname_value: Option[String] = None, + comment: Option[String] = None +) { // Make sure only one value is given. - if (List( - richtext_value, - link_value, - link_to_client_id, - int_value, - decimal_value, - boolean_value, - uri_value, - date_value, - color_value, - geom_value, - hlist_value, - interval_value, - time_value, - geoname_value - ).flatten.size > 1) { + if ( + List( + richtext_value, + link_value, + link_to_client_id, + int_value, + decimal_value, + boolean_value, + uri_value, + date_value, + color_value, + geom_value, + hlist_value, + interval_value, + time_value, + geoname_value + ).flatten.size > 1 + ) { throw BadRequestException(s"Different value types were submitted for the same property") } /** - * Returns the type of the given value. - * - * @return a value type IRI. - */ - def getValueClassIri: IRI = { + * Returns the type of the given value. + * + * @return a value type IRI. + */ + def getValueClassIri: IRI = if (richtext_value.nonEmpty) OntologyConstants.KnoraBase.TextValue else if (link_value.nonEmpty || link_to_client_id.nonEmpty) OntologyConstants.KnoraBase.LinkValue else if (int_value.nonEmpty) OntologyConstants.KnoraBase.IntValue @@ -141,153 +149,159 @@ case class CreateResourceValueV1(richtext_value: Option[CreateRichtextV1] = None else if (time_value.nonEmpty) OntologyConstants.KnoraBase.TimeValue else if (geoname_value.nonEmpty) OntologyConstants.KnoraBase.GeonameValue else throw BadRequestException("No value specified") - } } /** - * Represents an API request that asks the Knora API server to change a resource's label. - * - * @param label the resource's new label. - */ + * Represents an API request that asks the Knora API server to change a resource's label. + * + * @param label the resource's new label. + */ case class ChangeResourceLabelApiRequestV1(label: String) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Messages /** - * An abstract trait representing a request message that can be sent to `ResourcesResponderV1`. - */ + * An abstract trait representing a request message that can be sent to `ResourcesResponderV1`. + */ sealed trait ResourcesResponderRequestV1 extends KnoraRequestV1 /** - * Requests a description of a resource. A successful response will be a [[ResourceInfoResponseV1]]. - * - * @param iri the IRI of the resource to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ + * Requests a description of a resource. A successful response will be a [[ResourceInfoResponseV1]]. + * + * @param iri the IRI of the resource to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ case class ResourceInfoGetRequestV1(iri: IRI, featureFactoryConfig: FeatureFactoryConfig, userProfile: UserADM) extends ResourcesResponderRequestV1 /** - * Requests a full description of a resource, along with its properties, their values, incoming references, and other - * information. A successful response will be a [[ResourceFullResponseV1]]. - * - * @param iri the IRI of the resource to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - * @param getIncoming if `true`, information about incoming references will be included in the response. - */ -case class ResourceFullGetRequestV1(iri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM, - getIncoming: Boolean = true) - extends ResourcesResponderRequestV1 + * Requests a full description of a resource, along with its properties, their values, incoming references, and other + * information. A successful response will be a [[ResourceFullResponseV1]]. + * + * @param iri the IRI of the resource to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + * @param getIncoming if `true`, information about incoming references will be included in the response. + */ +case class ResourceFullGetRequestV1( + iri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM, + getIncoming: Boolean = true +) extends ResourcesResponderRequestV1 /** - * Requests a [[ResourceContextResponseV1]] describing the context of a resource (i.e. the resources that are part of it). - * - * @param iri the IRI of the resource to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param resinfo if `true`, the [[ResourceContextResponseV1]] will include a [[ResourceInfoV1]]. - */ -case class ResourceContextGetRequestV1(iri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - resinfo: Boolean) - extends ResourcesResponderRequestV1 + * Requests a [[ResourceContextResponseV1]] describing the context of a resource (i.e. the resources that are part of it). + * + * @param iri the IRI of the resource to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param resinfo if `true`, the [[ResourceContextResponseV1]] will include a [[ResourceInfoV1]]. + */ +case class ResourceContextGetRequestV1( + iri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + resinfo: Boolean +) extends ResourcesResponderRequestV1 /** - * Requests the permissions for the current user on the given resource. A successful response will be a [[ResourceRightsResponseV1]]. - * - * @param iri the IRI of the resource to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ + * Requests the permissions for the current user on the given resource. A successful response will be a [[ResourceRightsResponseV1]]. + * + * @param iri the IRI of the resource to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ case class ResourceRightsGetRequestV1(iri: IRI, featureFactoryConfig: FeatureFactoryConfig, userProfile: UserADM) extends ResourcesResponderRequestV1 /** - * Requests a search for resources matching the given string. - * - * @param searchString the string to search for. - * @param resourceTypeIri if set, restrict search to this resource class. - * @param numberOfProps the amount of describing properties to be returned for each found resource (e.g if set to two, for an incunabula book its title and creator would be returned). - * @param limitOfResults limits number of resources to be returned. - * @param userProfile the profile of the user making the request. - */ -case class ResourceSearchGetRequestV1(searchString: String, - resourceTypeIri: Option[IRI], - numberOfProps: Int, - limitOfResults: Int, - userProfile: UserADM) - extends ResourcesResponderRequestV1 + * Requests a search for resources matching the given string. + * + * @param searchString the string to search for. + * @param resourceTypeIri if set, restrict search to this resource class. + * @param numberOfProps the amount of describing properties to be returned for each found resource (e.g if set to two, for an incunabula book its title and creator would be returned). + * @param limitOfResults limits number of resources to be returned. + * @param userProfile the profile of the user making the request. + */ +case class ResourceSearchGetRequestV1( + searchString: String, + resourceTypeIri: Option[IRI], + numberOfProps: Int, + limitOfResults: Int, + userProfile: UserADM +) extends ResourcesResponderRequestV1 /** - * Requests the creation of a new resource of the given type with the given properties. - * - * @param resourceTypeIri the type of the new resource. - * @param label the rdfs:label of the resource. - * @param values the properties to add: type and value(s): a Map of propertyIris to ApiValueV1. - * @param file a file that has been uploaded to Sipi's temporary storage and should be attached to the resource. - * @param projectIri the IRI of the project the resources is added to. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class ResourceCreateRequestV1(resourceTypeIri: IRI, - label: String, - values: Map[IRI, Seq[CreateValueV1WithComment]], - file: Option[FileValueV1] = None, - projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV1 + * Requests the creation of a new resource of the given type with the given properties. + * + * @param resourceTypeIri the type of the new resource. + * @param label the rdfs:label of the resource. + * @param values the properties to add: type and value(s): a Map of propertyIris to ApiValueV1. + * @param file a file that has been uploaded to Sipi's temporary storage and should be attached to the resource. + * @param projectIri the IRI of the project the resources is added to. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class ResourceCreateRequestV1( + resourceTypeIri: IRI, + label: String, + values: Map[IRI, Seq[CreateValueV1WithComment]], + file: Option[FileValueV1] = None, + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV1 /** - * Requests the creation of one of multiple new resources. - * - * @param resourceTypeIri the type of the new resource. - * @param clientResourceID the client's ID for the resource. - * @param label the rdfs:label of the resource. - * @param values the properties to add: type and value(s): a Map of propertyIris to ApiValueV1. - * @param file a file in Sipi's temporary storage that should be attached to the resource. - * @param creationDate the creation date that should be attached to the resource. - */ -case class OneOfMultipleResourceCreateRequestV1(resourceTypeIri: IRI, - clientResourceID: String, - label: String, - values: Map[IRI, Seq[CreateValueV1WithComment]], - file: Option[FileValueV1] = None, - creationDate: Option[Instant]) + * Requests the creation of one of multiple new resources. + * + * @param resourceTypeIri the type of the new resource. + * @param clientResourceID the client's ID for the resource. + * @param label the rdfs:label of the resource. + * @param values the properties to add: type and value(s): a Map of propertyIris to ApiValueV1. + * @param file a file in Sipi's temporary storage that should be attached to the resource. + * @param creationDate the creation date that should be attached to the resource. + */ +case class OneOfMultipleResourceCreateRequestV1( + resourceTypeIri: IRI, + clientResourceID: String, + label: String, + values: Map[IRI, Seq[CreateValueV1WithComment]], + file: Option[FileValueV1] = None, + creationDate: Option[Instant] +) /** - * Requests the creation of multiple new resources. - * - * @param resourcesToCreate the collection of requests for creation of new resources. - * @param projectIri the IRI of the project the resources are added to. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class MultipleResourceCreateRequestV1(resourcesToCreate: Seq[OneOfMultipleResourceCreateRequestV1], - projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV1 + * Requests the creation of multiple new resources. + * + * @param resourcesToCreate the collection of requests for creation of new resources. + * @param projectIri the IRI of the project the resources are added to. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class MultipleResourceCreateRequestV1( + resourcesToCreate: Seq[OneOfMultipleResourceCreateRequestV1], + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV1 /** - * describes the answer to creation of multiple resources - * - * @param createdResources created resources - * - */ -case class MultipleResourceCreateResponseV1(createdResources: Seq[OneOfMultipleResourcesCreateResponseV1], - projectADM: ProjectADM) - extends KnoraResponseV1 + * describes the answer to creation of multiple resources + * + * @param createdResources created resources + */ +case class MultipleResourceCreateResponseV1( + createdResources: Seq[OneOfMultipleResourcesCreateResponseV1], + projectADM: ProjectADM +) extends KnoraResponseV1 with UpdateResultInProject { def toJsValue: JsValue = ResourceV1JsonProtocol.MultipleResourceCreateResponseV1Format.write(this) @@ -295,115 +309,118 @@ case class MultipleResourceCreateResponseV1(createdResources: Seq[OneOfMultipleR } /** - * Represents one of multiple resources that were created in response to a single API request. - * - * @param clientResourceID the client's ID for the resource. - * @param resourceIri the IRI that was assigned to the resource. - */ + * Represents one of multiple resources that were created in response to a single API request. + * + * @param clientResourceID the client's ID for the resource. + * @param resourceIri the IRI that was assigned to the resource. + */ case class OneOfMultipleResourcesCreateResponseV1(clientResourceID: String, resourceIri: IRI, label: String) /** - * Checks whether a resource belongs to a certain OWL class or to a subclass of that class. This message is used - * internally by Knora, and is not part of Knora API v1. A successful response will be a [[ResourceCheckClassResponseV1]]. - * - * @param resourceIri the IRI of the resource. - * @param owlClass the IRI of the OWL class to compare the resource's class to. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ -case class ResourceCheckClassRequestV1(resourceIri: IRI, - owlClass: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM) - extends ResourcesResponderRequestV1 + * Checks whether a resource belongs to a certain OWL class or to a subclass of that class. This message is used + * internally by Knora, and is not part of Knora API v1. A successful response will be a [[ResourceCheckClassResponseV1]]. + * + * @param resourceIri the IRI of the resource. + * @param owlClass the IRI of the OWL class to compare the resource's class to. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ +case class ResourceCheckClassRequestV1( + resourceIri: IRI, + owlClass: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM +) extends ResourcesResponderRequestV1 /** - * Requests that a resource is marked as deleted. A successful response will be a [[ResourceDeleteResponseV1]]. - * - * @param resourceIri the IRI of the resource to be marked as deleted. - * @param deleteComment an optional comment explaining why the resource is being marked as deleted. - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class ResourceDeleteRequestV1(resourceIri: IRI, - deleteComment: Option[String], - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV1 + * Requests that a resource is marked as deleted. A successful response will be a [[ResourceDeleteResponseV1]]. + * + * @param resourceIri the IRI of the resource to be marked as deleted. + * @param deleteComment an optional comment explaining why the resource is being marked as deleted. + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class ResourceDeleteRequestV1( + resourceIri: IRI, + deleteComment: Option[String], + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV1 /** - * Represents a response to a [[ResourceCheckClassRequestV1]]. - * - * @param isInClass `true` if the resource is in the specified OWL class or a subclass of that class. - */ + * Represents a response to a [[ResourceCheckClassRequestV1]]. + * + * @param isInClass `true` if the resource is in the specified OWL class or a subclass of that class. + */ case class ResourceCheckClassResponseV1(isInClass: Boolean) /** - * Represents a successful response to a [[ResourceDeleteRequestV1]]. - * - * @param id the IRI of the resource that was marked as deleted. - */ + * Represents a successful response to a [[ResourceDeleteRequestV1]]. + * + * @param id the IRI of the resource that was marked as deleted. + */ case class ResourceDeleteResponseV1(id: IRI) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.resourceDeleteResponseV1Format.write(this) } /** - * Represents the Knora API v1 JSON response to a request for information about a resource. - * - * @param resource_info basic information about the resource. - * @param rights a permission code indicating what rights the user who made the request has on the resource. - */ + * Represents the Knora API v1 JSON response to a request for information about a resource. + * + * @param resource_info basic information about the resource. + * @param rights a permission code indicating what rights the user who made the request has on the resource. + */ case class ResourceInfoResponseV1(resource_info: Option[ResourceInfoV1] = None, rights: Option[Int] = None) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.resourceInfoResponseV1Format.write(this) } /** - * Represents the Knora API v1 JSON response to a request for a full description of a resource, along with its - * properties, their values, incoming references, and other information. - * - * @param resinfo basic information about the resource. - * @param resdata additional information about the resource. - * @param props the resource's properties with their values. - * @param incoming incoming references to the resource. - * @param access `OK` if the user has access to the resource, otherwise `NO_ACCESS`. - */ -case class ResourceFullResponseV1(resinfo: Option[ResourceInfoV1] = None, - resdata: Option[ResourceDataV1] = None, - props: Option[PropsV1] = None, - incoming: Seq[IncomingV1] = Nil, - access: String) - extends KnoraResponseV1 { + * Represents the Knora API v1 JSON response to a request for a full description of a resource, along with its + * properties, their values, incoming references, and other information. + * + * @param resinfo basic information about the resource. + * @param resdata additional information about the resource. + * @param props the resource's properties with their values. + * @param incoming incoming references to the resource. + * @param access `OK` if the user has access to the resource, otherwise `NO_ACCESS`. + */ +case class ResourceFullResponseV1( + resinfo: Option[ResourceInfoV1] = None, + resdata: Option[ResourceDataV1] = None, + props: Option[PropsV1] = None, + incoming: Seq[IncomingV1] = Nil, + access: String +) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.resourceFullResponseV1Format.write(this) } /** - * Describes the context of a resource, i.e. the resources that are part of the specified resource. - * - * @param resource_context resources relating to this resource via `knora-base:partOf`. - */ + * Describes the context of a resource, i.e. the resources that are part of the specified resource. + * + * @param resource_context resources relating to this resource via `knora-base:partOf`. + */ case class ResourceContextResponseV1(resource_context: ResourceContextV1) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceContextV1JsonProtocol.resourceContextResponseV1Format.write(this) } /** - * Describes the permissions that the current user has on a given resurce. - * - * @param rights the permissions for the given user on this resource - */ + * Describes the permissions that the current user has on a given resurce. + * + * @param rights the permissions for the given user on this resource + */ case class ResourceRightsResponseV1(rights: Option[Int]) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.resourceRightsResponseV1Format.write(this) } /** - * Describes the answer to a resource search request [[ResourceSearchGetRequestV1]]: - * the resources matching the given criteria (search string, resource class). - * - * @param resources the resorces that match the given given search criteria. - */ + * Describes the answer to a resource search request [[ResourceSearchGetRequestV1]]: + * the resources matching the given criteria (search string, resource class). + * + * @param resources the resorces that match the given given search criteria. + */ case class ResourceSearchResponseV1(resources: Seq[ResourceSearchResultRowV1] = Vector.empty[ResourceSearchResultRowV1]) extends KnoraResponseV1 { @@ -411,121 +428,121 @@ case class ResourceSearchResponseV1(resources: Seq[ResourceSearchResultRowV1] = } /** - * Describes the answer to a newly created resource [[ResourceCreateRequestV1]]. - * - * @param res_id the IRI ow the new resource. - * @param results the values that have been attached to the resource. The key in the Map refers - * to the property IRI and the Seq contains all instances of values of this type. - * @param projectADM the project in which the resource is to be created. - */ -case class ResourceCreateResponseV1(res_id: IRI, - results: Map[IRI, Seq[ResourceCreateValueResponseV1]] = - Map.empty[IRI, Seq[ResourceCreateValueResponseV1]], - projectADM: ProjectADM) - extends KnoraResponseV1 + * Describes the answer to a newly created resource [[ResourceCreateRequestV1]]. + * + * @param res_id the IRI ow the new resource. + * @param results the values that have been attached to the resource. The key in the Map refers + * to the property IRI and the Seq contains all instances of values of this type. + * @param projectADM the project in which the resource is to be created. + */ +case class ResourceCreateResponseV1( + res_id: IRI, + results: Map[IRI, Seq[ResourceCreateValueResponseV1]] = Map.empty[IRI, Seq[ResourceCreateValueResponseV1]], + projectADM: ProjectADM +) extends KnoraResponseV1 with UpdateResultInProject { def toJsValue: JsValue = ResourceV1JsonProtocol.ResourceCreateResponseV1Format.write(this) } /** - * Requests the properties of a given resource. - * - * @param iri the iri of the given resource. - * @param featureFactoryConfig the feature factory configuration. - */ + * Requests the properties of a given resource. + * + * @param iri the iri of the given resource. + * @param featureFactoryConfig the feature factory configuration. + */ case class PropertiesGetRequestV1(iri: IRI, featureFactoryConfig: FeatureFactoryConfig, userProfile: UserADM) extends ResourcesResponderRequestV1 // TODO: refactor PropertiesGetResponseV1 (https://github.com/dhlab-basel/Knora/issues/134#issue-154443186) /** - * Describes the answer to a [[PropertiesGetRequestV1]]. - * - * @param properties the properties of the specified resource. - */ + * Describes the answer to a [[PropertiesGetRequestV1]]. + * + * @param properties the properties of the specified resource. + */ case class PropertiesGetResponseV1(properties: PropsGetV1) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.propertiesGetResponseV1Format.write(this) } /** - * Requests the label of a resource to be changed. - * - * @param resourceIri the IRI of the resource whose label should be changed. - * @param label the new value of the label. - * @param featureFactoryConfig the feature factory configuration. - * @param userADM the profile of the user making the request. - * @param apiRequestID the ID of the API request. - * - */ -case class ChangeResourceLabelRequestV1(resourceIri: IRI, - label: String, - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV1 + * Requests the label of a resource to be changed. + * + * @param resourceIri the IRI of the resource whose label should be changed. + * @param label the new value of the label. + * @param featureFactoryConfig the feature factory configuration. + * @param userADM the profile of the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class ChangeResourceLabelRequestV1( + resourceIri: IRI, + label: String, + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV1 /** - * Represents the answer to a [[ChangeResourceLabelRequestV1]]. - * - * @param res_id the IRI of the resource whose label was changed. - * @param label the resource's new label. - */ + * Represents the answer to a [[ChangeResourceLabelRequestV1]]. + * + * @param res_id the IRI of the resource whose label was changed. + * @param label the resource's new label. + */ case class ChangeResourceLabelResponseV1(res_id: IRI, label: String) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.changeResourceLabelResponseV1Format.write(this) } /** - * Requests a graph of resources that are reachable via links to or from a given resource. A successful response - * will be a [[GraphDataGetResponseV1]]. - * - * @param resourceIri the IRI of the initial resource. - * @param depth the maximum depth of the graph, counting from the initial resource. - * @param userADM the profile of the user making the request. - */ + * Requests a graph of resources that are reachable via links to or from a given resource. A successful response + * will be a [[GraphDataGetResponseV1]]. + * + * @param resourceIri the IRI of the initial resource. + * @param depth the maximum depth of the graph, counting from the initial resource. + * @param userADM the profile of the user making the request. + */ case class GraphDataGetRequestV1(resourceIri: IRI, depth: Int, userADM: UserADM) extends ResourcesResponderRequestV1 /** - * Provides a graph of resources that are reachable via links to or from a given resource, in response to a - * [[GraphDataGetRequestV1]]. - * - * @param nodes the nodes that are visible in the graph. - * @param edges the edges that are visible in the graph. - */ + * Provides a graph of resources that are reachable via links to or from a given resource, in response to a + * [[GraphDataGetRequestV1]]. + * + * @param nodes the nodes that are visible in the graph. + * @param edges the edges that are visible in the graph. + */ case class GraphDataGetResponseV1(nodes: Seq[GraphNodeV1], edges: Seq[GraphEdgeV1]) extends KnoraResponseV1 { def toJsValue: JsValue = ResourceV1JsonProtocol.graphDataGetResponseV1Format.write(this) } /** - * Causes the resources responder to return an invalid response message. - */ + * Causes the resources responder to return an invalid response message. + */ case class UnexpectedMessageRequest() extends ResourcesResponderRequestV1 /** - * Causes the resources responder to return a response message containing an internal servers exception. - */ + * Causes the resources responder to return a response message containing an internal servers exception. + */ case class InternalServerExceptionMessageRequest() extends ResourcesResponderRequestV1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Components of messages /** - * Indicates whether a resource has parts, is part of another resource, or neither. - */ + * Indicates whether a resource has parts, is part of another resource, or neither. + */ object ResourceContextCodeV1 extends Enumeration { /** - * Indicates that a resource has no parts and is not part of another resource. - */ + * Indicates that a resource has no parts and is not part of another resource. + */ val RESOURCE_CONTEXT_NONE: Value = Value(0) /** - * Indicates that a resource is part of another resource. - */ + * Indicates that a resource is part of another resource. + */ val RESOURCE_CONTEXT_IS_PARTOF: Value = Value(1) /** - * Indicates that a resource has parts. - */ + * Indicates that a resource has parts. + */ val RESOURCE_CONTEXT_IS_COMPOUND: Value = Value(2) object ResourceContextCodeV1Protocol extends DefaultJsonProtocol { @@ -546,250 +563,264 @@ object ResourceContextCodeV1 extends Enumeration { } /** - * Describes the context of a resource, i.e. the other resources that are part of the queried resource. - * - * @param res_id the IRI of each resource that is part of the queried resource. - * @param resclass_name obsolete. - * @param preview a thumbnail image of each resource that is part of the queried resource. - * @param locations full quality representations of the resource. - * @param firstprop the `rdfs:label` of each resource that is part of the queried resource. - * @param region unused, always an array of nulls. - * @param context indicates whether the queried resource is part of another resource, has parts of its own, or neither. - * @param canonical_res_id the IRI of the containing resource. - * @param resinfo a [[ResourceInfoV1]] describing the containing resource. - */ -case class ResourceContextV1(res_id: Option[Seq[IRI]] = None, - resclass_name: Option[String] = None, - preview: Option[Seq[Option[LocationV1]]] = None, - locations: Option[Seq[Option[Seq[LocationV1]]]] = None, - firstprop: Option[Seq[Option[String]]] = None, - region: Option[Seq[Option[String]]] = None, - context: ResourceContextCodeV1.Value, - canonical_res_id: IRI, - resinfo: Option[ResourceInfoV1] = None, - parent_res_id: Option[IRI] = None, - parent_resinfo: Option[ResourceInfoV1] = None) + * Describes the context of a resource, i.e. the other resources that are part of the queried resource. + * + * @param res_id the IRI of each resource that is part of the queried resource. + * @param resclass_name obsolete. + * @param preview a thumbnail image of each resource that is part of the queried resource. + * @param locations full quality representations of the resource. + * @param firstprop the `rdfs:label` of each resource that is part of the queried resource. + * @param region unused, always an array of nulls. + * @param context indicates whether the queried resource is part of another resource, has parts of its own, or neither. + * @param canonical_res_id the IRI of the containing resource. + * @param resinfo a [[ResourceInfoV1]] describing the containing resource. + */ +case class ResourceContextV1( + res_id: Option[Seq[IRI]] = None, + resclass_name: Option[String] = None, + preview: Option[Seq[Option[LocationV1]]] = None, + locations: Option[Seq[Option[Seq[LocationV1]]]] = None, + firstprop: Option[Seq[Option[String]]] = None, + region: Option[Seq[Option[String]]] = None, + context: ResourceContextCodeV1.Value, + canonical_res_id: IRI, + resinfo: Option[ResourceInfoV1] = None, + parent_res_id: Option[IRI] = None, + parent_resinfo: Option[ResourceInfoV1] = None +) /** - * Describes a resource that is part of the context of another resource. Used internally to construct instances of [[ResourceContextV1]]. - * - * @param res_id the IRI of a resource that is part of the queried resource. - * @param preview a thumbnail image of the resource represented by `res_id`. - * @param locations full quality representations of the resource represented by `res_id` in various qualities. - * @param firstprop the `rdfs:label` of the resource represented by `res_id`. - * @param region unused, always null. - */ -case class ResourceContextItemV1(res_id: IRI, - preview: Option[LocationV1], - locations: Option[Seq[LocationV1]], - firstprop: Option[String], - region: Option[String] = None) + * Describes a resource that is part of the context of another resource. Used internally to construct instances of [[ResourceContextV1]]. + * + * @param res_id the IRI of a resource that is part of the queried resource. + * @param preview a thumbnail image of the resource represented by `res_id`. + * @param locations full quality representations of the resource represented by `res_id` in various qualities. + * @param firstprop the `rdfs:label` of the resource represented by `res_id`. + * @param region unused, always null. + */ +case class ResourceContextItemV1( + res_id: IRI, + preview: Option[LocationV1], + locations: Option[Seq[LocationV1]], + firstprop: Option[String], + region: Option[String] = None +) /** - * Represents basic information about a Knora resource, in Knora API v1 JSON. - * - * @param project_id the IRI of the project that the resource is associated with. - * @param person_id the IRI of the resource's owner. - * @param restype_id the IRI of the resource's OWL class. - * @param restype_name same as `restype_id`. - * @param restype_label the label of the resource class. - * @param restype_description a description of the resource class. - * @param restype_iconsrc the URL of an icon for the resource class. - * @param preview the URL of a preview of the resource. - * @param locations if this resource has binary data, a list of the available representations of that data (e.g. resolutions of an image). - * @param locdata obsolete. - * @param resclass_name obsolete, always `object`. - * @param resclass_has_location `true` if the resource has binary data. - * @param lastmod a timestamp of the last modification of the resource. - * @param value_of obsolete, always 0. - * @param firstproperty a string representation of the resource's first property. - * @param regions representation of regions pointing to this resource. - */ -case class ResourceInfoV1(project_id: IRI, - project_shortcode: String, - person_id: IRI, - restype_id: IRI, - restype_name: Option[IRI] = None, - restype_label: Option[String] = None, - restype_description: Option[String] = None, - restype_iconsrc: Option[String] = None, - preview: Option[LocationV1] = None, - locations: Option[Seq[LocationV1]] = None, - locdata: Option[LocationV1] = None, - resclass_name: String = "object", - resclass_has_location: Boolean = false, - lastmod: String = "0000-00-00 00:00:00", - value_of: Int = 0, - firstproperty: Option[String] = None, - regions: Option[Seq[PropsGetForRegionV1]] = None) + * Represents basic information about a Knora resource, in Knora API v1 JSON. + * + * @param project_id the IRI of the project that the resource is associated with. + * @param person_id the IRI of the resource's owner. + * @param restype_id the IRI of the resource's OWL class. + * @param restype_name same as `restype_id`. + * @param restype_label the label of the resource class. + * @param restype_description a description of the resource class. + * @param restype_iconsrc the URL of an icon for the resource class. + * @param preview the URL of a preview of the resource. + * @param locations if this resource has binary data, a list of the available representations of that data (e.g. resolutions of an image). + * @param locdata obsolete. + * @param resclass_name obsolete, always `object`. + * @param resclass_has_location `true` if the resource has binary data. + * @param lastmod a timestamp of the last modification of the resource. + * @param value_of obsolete, always 0. + * @param firstproperty a string representation of the resource's first property. + * @param regions representation of regions pointing to this resource. + */ +case class ResourceInfoV1( + project_id: IRI, + project_shortcode: String, + person_id: IRI, + restype_id: IRI, + restype_name: Option[IRI] = None, + restype_label: Option[String] = None, + restype_description: Option[String] = None, + restype_iconsrc: Option[String] = None, + preview: Option[LocationV1] = None, + locations: Option[Seq[LocationV1]] = None, + locdata: Option[LocationV1] = None, + resclass_name: String = "object", + resclass_has_location: Boolean = false, + lastmod: String = "0000-00-00 00:00:00", + value_of: Int = 0, + firstproperty: Option[String] = None, + regions: Option[Seq[PropsGetForRegionV1]] = None +) /** - * Provides additional information about a Knora resource, in Knora API v1 JSON. - * - * @param res_id the IRI of the resource. - * @param restype_name the IRI of the resource's OWL class. - * @param restype_label the `rdfs:label` of the resource's OWL class. - * @param iconsrc the icon of the resource's OWL class. - * @param rights a numeric code representing the permissions that the requesting user has on the resource. - */ -case class ResourceDataV1(res_id: IRI, - restype_name: IRI, - restype_label: Option[String] = None, - iconsrc: Option[String] = None, - rights: Option[Int]) + * Provides additional information about a Knora resource, in Knora API v1 JSON. + * + * @param res_id the IRI of the resource. + * @param restype_name the IRI of the resource's OWL class. + * @param restype_label the `rdfs:label` of the resource's OWL class. + * @param iconsrc the icon of the resource's OWL class. + * @param rights a numeric code representing the permissions that the requesting user has on the resource. + */ +case class ResourceDataV1( + res_id: IRI, + restype_name: IRI, + restype_label: Option[String] = None, + iconsrc: Option[String] = None, + rights: Option[Int] +) /** - * Represents information about a resource that has a reference to the queried resource. - * - * @param ext_res_id the IRI of the referring resource. - * @param resinfo a [[ResourceInfoV1]] describing the referring resource. - * @param value the `rdfs:label` of the referring resource. - */ + * Represents information about a resource that has a reference to the queried resource. + * + * @param ext_res_id the IRI of the referring resource. + * @param resinfo a [[ResourceInfoV1]] describing the referring resource. + * @param value the `rdfs:label` of the referring resource. + */ case class IncomingV1(ext_res_id: ExternalResourceIDV1, resinfo: ResourceInfoV1, value: Option[String]) /** - * Represents an incoming reference from another resource. - * - * @param id the IRI of the resource that is the source of the incoming reference. - * @param pid the IRI of the property that points from the source resource to the target resource. - */ + * Represents an incoming reference from another resource. + * + * @param id the IRI of the resource that is the source of the incoming reference. + * @param pid the IRI of the property that points from the source resource to the target resource. + */ case class ExternalResourceIDV1(id: IRI, pid: IRI) /** - * Represents an available binary representation of the resource (e.g. an image at a particular resolution). - * - * @param format_name `JPEG`, `JPEG2000`, `TIFF`, etc. - * @param origname the name of the original file containing the binary data. - * @param nx the width of the image in pixels. - * @param ny the height of the image in pixels. - * @param path the URL from which this representation can be retrieved. - */ -case class LocationV1(format_name: String, - origname: Option[String], - nx: Option[Int] = None, - ny: Option[Int] = None, - path: String, - fps: Int = 0, - duration: Int = 0, - protocol: String = "file") + * Represents an available binary representation of the resource (e.g. an image at a particular resolution). + * + * @param format_name `JPEG`, `JPEG2000`, `TIFF`, etc. + * @param origname the name of the original file containing the binary data. + * @param nx the width of the image in pixels. + * @param ny the height of the image in pixels. + * @param path the URL from which this representation can be retrieved. + */ +case class LocationV1( + format_name: String, + origname: Option[String], + nx: Option[Int] = None, + ny: Option[Int] = None, + path: String, + fps: Int = 0, + duration: Int = 0, + protocol: String = "file" +) /** - * Represents a property of a resource. - * - * @param pid the IRI of the property. - * @param regular_property obsolete, always 1. - * @param valuetype_id the IRI of the OWL class of the values of this property. - * @param guielement the type of GUI element used to render this property. - * @param is_annotation obsolete, always 0. - * @param label the `rdfs:label` of this property. - * @param attributes HTML attributes for the GUI element used to render this property. - * @param occurrence the cardinality of this property: 1, 1-n, 0-1, or 0-n. - * @param values the property's literal values for this resource (may be an empty list). - * @param value_ids the IRIs of the value objects representing the property's values for this resource. - * @param comments any comments attached to the value objects. - * @param value_restype if the property's value is another resource, contains the `rdfs:label` of the OWL class - * of each resource referred to. - * @param value_firstprops if the property's value is another resource, contains the `rdfs:label` of each resource - * referred to. - * @param value_iconsrcs if the property's value is another resource, contains the icon representing the OWL - * class of each resource referred to. - */ -case class PropertyV1(pid: IRI, - regular_property: Int = 1, - valuetype_id: Option[IRI] = None, - guiorder: Option[Int] = None, - guielement: Option[String] = None, - is_annotation: String = "0", - label: Option[String] = None, - attributes: String = "", - occurrence: Option[String] = None, - values: Seq[ApiValueV1] = Nil, - value_ids: Seq[IRI] = Nil, - comments: Seq[Option[String]] = Nil, - value_restype: Seq[Option[String]] = Nil, - value_iconsrcs: Seq[Option[String]] = Nil, - value_firstprops: Seq[Option[String]] = Nil, - value_rights: Seq[Option[Int]], - locations: Seq[LocationV1] = Nil) + * Represents a property of a resource. + * + * @param pid the IRI of the property. + * @param regular_property obsolete, always 1. + * @param valuetype_id the IRI of the OWL class of the values of this property. + * @param guielement the type of GUI element used to render this property. + * @param is_annotation obsolete, always 0. + * @param label the `rdfs:label` of this property. + * @param attributes HTML attributes for the GUI element used to render this property. + * @param occurrence the cardinality of this property: 1, 1-n, 0-1, or 0-n. + * @param values the property's literal values for this resource (may be an empty list). + * @param value_ids the IRIs of the value objects representing the property's values for this resource. + * @param comments any comments attached to the value objects. + * @param value_restype if the property's value is another resource, contains the `rdfs:label` of the OWL class + * of each resource referred to. + * @param value_firstprops if the property's value is another resource, contains the `rdfs:label` of each resource + * referred to. + * @param value_iconsrcs if the property's value is another resource, contains the icon representing the OWL + * class of each resource referred to. + */ +case class PropertyV1( + pid: IRI, + regular_property: Int = 1, + valuetype_id: Option[IRI] = None, + guiorder: Option[Int] = None, + guielement: Option[String] = None, + is_annotation: String = "0", + label: Option[String] = None, + attributes: String = "", + occurrence: Option[String] = None, + values: Seq[ApiValueV1] = Nil, + value_ids: Seq[IRI] = Nil, + comments: Seq[Option[String]] = Nil, + value_restype: Seq[Option[String]] = Nil, + value_iconsrcs: Seq[Option[String]] = Nil, + value_firstprops: Seq[Option[String]] = Nil, + value_rights: Seq[Option[Int]], + locations: Seq[LocationV1] = Nil +) /** - * Holds a list of [[PropertyV1]] objects representing the properties of a resource in - * Knora API v1 format. In Knora API v1, we format these as a JSON object (a map of property IRIs to PropertyV1 objects) - * rather than as a list, but future API versions will probably format them as a JSON array. This case class exists - * to make it clearer that we are currently formatting this list in a particular way - * (see [[ResourceV1JsonProtocol.PropsV1JsonFormat]]). - * - * @param properties a list of [[PropertyV1]] objects. - */ + * Holds a list of [[PropertyV1]] objects representing the properties of a resource in + * Knora API v1 format. In Knora API v1, we format these as a JSON object (a map of property IRIs to PropertyV1 objects) + * rather than as a list, but future API versions will probably format them as a JSON array. This case class exists + * to make it clearer that we are currently formatting this list in a particular way + * (see [[ResourceV1JsonProtocol.PropsV1JsonFormat]]). + * + * @param properties a list of [[PropertyV1]] objects. + */ case class PropsV1(properties: Seq[PropertyV1]) /** - * Represents a property of a resource in the format as required for the properties route. - * - * @param pid the IRI of the property. - * @param label the `rdfs:label` of this property. - * @param valuetype_id the IRI of the OWL class of the values of this property. - * @param valuetype a string indicating the OWL class of the values of this property. - * @param guielement the type of GUI element used to render this property. - * @param attributes HTML attributes for the GUI element used to render this property. - * @param is_annotation obsolete, always 0. - * @param values the property's literal values for this resource (may be an empty list). - */ -case class PropertyGetV1(pid: IRI, - label: Option[String] = None, - valuetype_id: Option[IRI] = None, - valuetype: Option[String] = None, - guielement: Option[String] = None, - attributes: String = "", - is_annotation: String = "0", - values: Seq[PropertyGetValueV1]) + * Represents a property of a resource in the format as required for the properties route. + * + * @param pid the IRI of the property. + * @param label the `rdfs:label` of this property. + * @param valuetype_id the IRI of the OWL class of the values of this property. + * @param valuetype a string indicating the OWL class of the values of this property. + * @param guielement the type of GUI element used to render this property. + * @param attributes HTML attributes for the GUI element used to render this property. + * @param is_annotation obsolete, always 0. + * @param values the property's literal values for this resource (may be an empty list). + */ +case class PropertyGetV1( + pid: IRI, + label: Option[String] = None, + valuetype_id: Option[IRI] = None, + valuetype: Option[String] = None, + guielement: Option[String] = None, + attributes: String = "", + is_annotation: String = "0", + values: Seq[PropertyGetValueV1] +) /** - * Represents the value of a property in the format as required for the properties route. - * - * @param person_id the owner of the value. - * @param comment any comment attached to the value. - * @param textval the string representation of the value object. - * @param value literal(s) representing this value object. - * @param id the IRI of this value object. - * @param lastmod the date of the last modification of this value. - * @param lastmod_utc the date of the last modification of this value as UTC. - * - */ + * Represents the value of a property in the format as required for the properties route. + * + * @param person_id the owner of the value. + * @param comment any comment attached to the value. + * @param textval the string representation of the value object. + * @param value literal(s) representing this value object. + * @param id the IRI of this value object. + * @param lastmod the date of the last modification of this value. + * @param lastmod_utc the date of the last modification of this value as UTC. + */ case class PropertyGetValueV1( - person_id: Option[IRI] = None, - comment: Option[String], - textval: String, - value: ApiValueV1, // TODO: this is called 'val' in the old Salsah, but val is a keyword in scala - id: IRI, - lastmod: Option[String] = None, - lastmod_utc: Option[String] = None) + person_id: Option[IRI] = None, + comment: Option[String], + textval: String, + value: ApiValueV1, // TODO: this is called 'val' in the old Salsah, but val is a keyword in scala + id: IRI, + lastmod: Option[String] = None, + lastmod_utc: Option[String] = None +) /** - * Holds a list of [[PropertyGetV1]] objects representing the properties of a resource in the format as requested - * by properties route (see [[ResourceV1JsonProtocol.PropsGetV1JsonFormat]]). - * - * @param properties a list of [[PropertyGetV1]] objects. - */ + * Holds a list of [[PropertyGetV1]] objects representing the properties of a resource in the format as requested + * by properties route (see [[ResourceV1JsonProtocol.PropsGetV1JsonFormat]]). + * + * @param properties a list of [[PropertyGetV1]] objects. + */ case class PropsGetV1(properties: Seq[PropertyGetV1]) /** - * Holds a list of [[PropertyGetV1]] objects representing the properties of a region in the format requested for the context query. If a resource - * is pointed to by regions, these are returned in the resource's context query (`resinfo.regions`). Additionally, the region's IRI and the icon of its resource class are given - * (see [[ResourceV1JsonProtocol.PropsGetForRegionV1JsonFormat]]). - * - * @param properties a list of [[PropertyGetV1]] objects. - * @param res_id the region's IRI. - * @param iconsrc the icon of the region's resource class. - */ + * Holds a list of [[PropertyGetV1]] objects representing the properties of a region in the format requested for the context query. If a resource + * is pointed to by regions, these are returned in the resource's context query (`resinfo.regions`). Additionally, the region's IRI and the icon of its resource class are given + * (see [[ResourceV1JsonProtocol.PropsGetForRegionV1JsonFormat]]). + * + * @param properties a list of [[PropertyGetV1]] objects. + * @param res_id the region's IRI. + * @param iconsrc the icon of the region's resource class. + */ case class PropsGetForRegionV1(properties: Seq[PropertyGetV1], res_id: IRI, iconsrc: Option[String]) /** - * Represents information about one resource matching the criteria of a [[ResourceSearchGetRequestV1]] - * - * @param id IRI - * @param value property value(s) of the resource (the amount depends on `numberOfProps` defined in [[ResourceSearchGetRequestV1]]) - */ + * Represents information about one resource matching the criteria of a [[ResourceSearchGetRequestV1]] + * + * @param id IRI + * @param value property value(s) of the resource (the amount depends on `numberOfProps` defined in [[ResourceSearchGetRequestV1]]) + */ case class ResourceSearchResultRowV1(id: IRI, value: Seq[String], rights: Option[Int] = None) { def toJsValue: JsValue = ResourceV1JsonProtocol.resourceSearchResultV1Format.write(this) @@ -799,13 +830,13 @@ case class ResourceSearchResultRowV1(id: IRI, value: Seq[String], rights: Option // GUI naming conversions /** - * Converts between SALSAH and Knora GUI naming conventions. - */ + * Converts between SALSAH and Knora GUI naming conventions. + */ object SalsahGuiConversions { /** - * A [[Map]] of Knora IRIs to SALSAH GUI element names. - */ + * A [[Map]] of Knora IRIs to SALSAH GUI element names. + */ private val iris2SalsahGuiElements: Map[IRI, IRI] = Map( OntologyConstants.SalsahGui.SimpleText -> "text", OntologyConstants.SalsahGui.Textarea -> "textarea", @@ -827,52 +858,50 @@ object SalsahGuiConversions { ) /** - * A [[Map]] of SALSAH GUI element names to their Knora IRIs. - */ + * A [[Map]] of SALSAH GUI element names to their Knora IRIs. + */ private val salsahGuiElements2Iris = iris2SalsahGuiElements.map(_.swap) /** - * Converts a Knora IRI representing a GUI element to the corresponding SALSAH GUI element name. - * - * @param iri the Knora IRI of the GUI element. - * @return the corresponding SALSAH GUI element name. - */ - def iri2SalsahGuiElement(iri: IRI): String = { + * Converts a Knora IRI representing a GUI element to the corresponding SALSAH GUI element name. + * + * @param iri the Knora IRI of the GUI element. + * @return the corresponding SALSAH GUI element name. + */ + def iri2SalsahGuiElement(iri: IRI): String = iris2SalsahGuiElements.get(iri) match { case Some(salsahGuiElement) => salsahGuiElement case None => throw new InconsistentRepositoryDataException(s"No SALSAH GUI element found for IRI: $iri") } - } /** - * Converts a SALSAH GUI element name to its Knora IRI. - * - * @param salsahGuiElement a SALSAH GUI element name. - * @return the corresponding Knora IRI. - */ - def salsahGuiElement2Iri(salsahGuiElement: String): IRI = { + * Converts a SALSAH GUI element name to its Knora IRI. + * + * @param salsahGuiElement a SALSAH GUI element name. + * @return the corresponding Knora IRI. + */ + def salsahGuiElement2Iri(salsahGuiElement: String): IRI = salsahGuiElements2Iris.get(salsahGuiElement) match { case Some(iri) => iri case None => throw new InconsistentRepositoryDataException(s"No IRI found for SALSAH GUI element: $salsahGuiElement") } - } } /** - * Describes values that have been attached to a new resource. - * - * @param value the value that has been attached to the resource. - * @param id the value object IRI of the value. - */ + * Describes values that have been attached to a new resource. + * + * @param value the value that has been attached to the resource. + * @param id the value object IRI of the value. + */ case class ResourceCreateValueResponseV1(value: ResourceCreateValueObjectResponseV1, id: IRI) { def toJsValue: JsValue = ResourceV1JsonProtocol.resourceCreateValueResponseV1Format.write(this) } /** - * Represents the possible value types to be returned to the client after creating a new resource with values. - * This isn't used anywhere else in the API, and we're not even sure the SALSAH GUI needs it. - */ + * Represents the possible value types to be returned to the client after creating a new resource with values. + * This isn't used anywhere else in the API, and we're not even sure the SALSAH GUI needs it. + */ object LiteralValueType extends Enumeration { type ValueType = Value val StringValue: Value = Value(0, "string") @@ -892,69 +921,70 @@ object LiteralValueType extends Enumeration { } /** - * Represents the value. All values have a textual representation. The other types depend on the current value type. - * - * @param textval textual representation of the value. - * @param ival integer value if it is an [[IntegerValueV1]]. - * @param dval decimal value if it is a [[DecimalValueV1]]. - * @param dateval1 start date if it is a [[DateValueV1]]. - * @param dateval2 end date if it is a [[DateValueV1]]. - * @param dateprecision1 the start date's precision if it is a [[DateValueV1]]. - * @param dateprecision2 the end date's precision if it is a [[DateValueV1]]. - * @param calendar the date's calendar if it is a [[DateValueV1]]. - * @param timeval1 start time value if it is an [[IntervalValueV1]]. - * @param timeval2 end time value if it is an [[IntervalValueV1]]. - * @param resource_id the IRI of the new resource. - * @param property_id the IRI of the property the value belongs to. - * @param person_id the person that created the value. - * @param order the order of the value (valueHasOrder). - */ + * Represents the value. All values have a textual representation. The other types depend on the current value type. + * + * @param textval textual representation of the value. + * @param ival integer value if it is an [[IntegerValueV1]]. + * @param dval decimal value if it is a [[DecimalValueV1]]. + * @param dateval1 start date if it is a [[DateValueV1]]. + * @param dateval2 end date if it is a [[DateValueV1]]. + * @param dateprecision1 the start date's precision if it is a [[DateValueV1]]. + * @param dateprecision2 the end date's precision if it is a [[DateValueV1]]. + * @param calendar the date's calendar if it is a [[DateValueV1]]. + * @param timeval1 start time value if it is an [[IntervalValueV1]]. + * @param timeval2 end time value if it is an [[IntervalValueV1]]. + * @param resource_id the IRI of the new resource. + * @param property_id the IRI of the property the value belongs to. + * @param person_id the person that created the value. + * @param order the order of the value (valueHasOrder). + */ case class ResourceCreateValueObjectResponseV1( - textval: Map[LiteralValueType.Value, String], - ival: Option[Map[LiteralValueType.Value, Int]] = None, - dval: Option[Map[LiteralValueType.Value, BigDecimal]] = None, - dateval1: Option[Map[LiteralValueType.Value, String]] = None, - dateval2: Option[Map[LiteralValueType.Value, String]] = None, - dateprecision1: Option[Map[LiteralValueType.Value, KnoraPrecisionV1.Value]] = None, - dateprecision2: Option[Map[LiteralValueType.Value, KnoraPrecisionV1.Value]] = None, - calendar: Option[Map[LiteralValueType.Value, KnoraCalendarV1.Value]] = None, - timeval1: Option[Map[LiteralValueType.Value, BigDecimal]] = None, - timeval2: Option[Map[LiteralValueType.Value, BigDecimal]] = None, - resource_id: Map[LiteralValueType.Value, IRI], - property_id: Map[LiteralValueType.Value, IRI], - person_id: Map[LiteralValueType.Value, IRI], - order: Map[LiteralValueType.Value, Int]) { + textval: Map[LiteralValueType.Value, String], + ival: Option[Map[LiteralValueType.Value, Int]] = None, + dval: Option[Map[LiteralValueType.Value, BigDecimal]] = None, + dateval1: Option[Map[LiteralValueType.Value, String]] = None, + dateval2: Option[Map[LiteralValueType.Value, String]] = None, + dateprecision1: Option[Map[LiteralValueType.Value, KnoraPrecisionV1.Value]] = None, + dateprecision2: Option[Map[LiteralValueType.Value, KnoraPrecisionV1.Value]] = None, + calendar: Option[Map[LiteralValueType.Value, KnoraCalendarV1.Value]] = None, + timeval1: Option[Map[LiteralValueType.Value, BigDecimal]] = None, + timeval2: Option[Map[LiteralValueType.Value, BigDecimal]] = None, + resource_id: Map[LiteralValueType.Value, IRI], + property_id: Map[LiteralValueType.Value, IRI], + person_id: Map[LiteralValueType.Value, IRI], + order: Map[LiteralValueType.Value, Int] +) { // TODO: do we need to add geonames here? def toJsValue: JsValue = ResourceV1JsonProtocol.resourceCreateValueObjectResponseV1Format.write(this) } /** - * Represents a node (i.e. a resource) in a [[GraphDataGetResponseV1]]. - * - * @param resourceIri the IRI of the resource. - * @param resourceLabel the label of the resource. - * @param resourceClassIri the IRI of the resource's OWL class. - * @param resourceClassLabel the label of the resource's OWL class. - */ + * Represents a node (i.e. a resource) in a [[GraphDataGetResponseV1]]. + * + * @param resourceIri the IRI of the resource. + * @param resourceLabel the label of the resource. + * @param resourceClassIri the IRI of the resource's OWL class. + * @param resourceClassLabel the label of the resource's OWL class. + */ case class GraphNodeV1(resourceIri: IRI, resourceLabel: String, resourceClassIri: IRI, resourceClassLabel: String) /** - * Represents an edge (i.e. a link) in a [[GraphDataGetResponseV1]]. - * - * @param source the resource that is the source of the link. - * @param target the resource that is the target of the link. - * @param propertyIri the IRI of the link property. - * @param propertyLabel the label of the link property. - */ + * Represents an edge (i.e. a link) in a [[GraphDataGetResponseV1]]. + * + * @param source the resource that is the source of the link. + * @param target the resource that is the target of the link. + * @param propertyIri the IRI of the link property. + * @param propertyLabel the label of the link property. + */ case class GraphEdgeV1(source: IRI, target: IRI, propertyIri: IRI, propertyLabel: String) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON providing data about resources and their properties. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about resources and their properties. + */ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { import LiteralValueType.LiteralValueTypeV1Protocol._ @@ -963,39 +993,38 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol implicit val locationFormat: JsonFormat[LocationV1] = jsonFormat8(LocationV1) /** - * Converts an optional list to an [[Option]] containing a tuple of the list's name and [[JsValue]]. The - * [[Option]] will be a [[Some]] if the list is non-empty, or a [[None]] if the list is empty. - * - * @param name the list's name. - * @param list the list. - * @param jsValueFun a function that returns the list's [[JsValue]]. - * @return either a [[Some]] containing the list's name and [[JsValue]], or a [[None]]. - */ - private def list2JsonOption(name: String, list: Seq[Any], jsValueFun: () => JsValue): Option[(String, JsValue)] = { + * Converts an optional list to an [[Option]] containing a tuple of the list's name and [[JsValue]]. The + * [[Option]] will be a [[Some]] if the list is non-empty, or a [[None]] if the list is empty. + * + * @param name the list's name. + * @param list the list. + * @param jsValueFun a function that returns the list's [[JsValue]]. + * @return either a [[Some]] containing the list's name and [[JsValue]], or a [[None]]. + */ + private def list2JsonOption(name: String, list: Seq[Any], jsValueFun: () => JsValue): Option[(String, JsValue)] = if (list.nonEmpty) { // We need jsValueFun so spray-json will know the list's type at compile time. Some(name -> jsValueFun()) } else { None } - } /** - * Converts between [[PropsV1]] objects and [[JsValue]] objects. - */ + * Converts between [[PropsV1]] objects and [[JsValue]] objects. + */ implicit object PropsV1JsonFormat extends JsonFormat[PropsV1] { /** - * Not implemented. - */ + * Not implemented. + */ def read(jsonVal: JsValue): PropsV1 = ??? /** - * Converts a [[PropsV1]] into a [[JsValue]]. - * - * @param propsV1 the [[PropsV1]]. - * @return a [[JsValue]]. - */ + * Converts a [[PropsV1]] into a [[JsValue]]. + * + * @param propsV1 the [[PropsV1]]. + * @return a [[JsValue]]. + */ def write(propsV1: PropsV1): JsValue = { // Convert each PropertyV1 object into a JsObject. val properties: Map[IRI, JsValue] = propsV1.properties.map { propertyV1 => @@ -1027,21 +1056,21 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol } /** - * Converts between [[PropsGetV1]] objects and [[JsValue]] objects. - */ + * Converts between [[PropsGetV1]] objects and [[JsValue]] objects. + */ implicit object PropsGetV1JsonFormat extends JsonFormat[PropsGetV1] { /** - * Not implemented. - */ + * Not implemented. + */ def read(jsonVal: JsValue): PropsGetV1 = ??? /** - * Converts a [[PropsGetV1]] into a [[JsValue]]. - * - * @param propsGetV1 the [[PropsGetV1]]. - * @return a [[JsValue]]. - */ + * Converts a [[PropsGetV1]] into a [[JsValue]]. + * + * @param propsGetV1 the [[PropsGetV1]]. + * @return a [[JsValue]]. + */ def write(propsGetV1: PropsGetV1): JsValue = { // Convert each PropertyGetV1 object into a JsObject. val properties: Map[IRI, JsValue] = propsGetV1.properties.map { (propertyGetV1: PropertyGetV1) => @@ -1065,54 +1094,51 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol } /** - * Converts between [[PropsGetForRegionV1]] objects and [[JsValue]] objects. - */ + * Converts between [[PropsGetForRegionV1]] objects and [[JsValue]] objects. + */ implicit object PropsGetForRegionV1JsonFormat extends JsonFormat[PropsGetForRegionV1] { - def getRequiredString(jsObj: JsObject, key: String): String = { + def getRequiredString(jsObj: JsObject, key: String): String = jsObj.fields.get(key) match { case Some(JsString(str)) => str case _ => throw InvalidApiJsonException(s"missing or invalid '$key'") } - } - def getOptionalString(jsObj: JsObject, key: String): Option[String] = { + def getOptionalString(jsObj: JsObject, key: String): Option[String] = jsObj.fields.get(key) match { case Some(JsString(str)) => Some(str) case Some(JsNull) => None case None => None case _ => throw InvalidApiJsonException(s"'$key' must be a string") } - } /** - * Converts a [[JsValue]] to a [[PropsGetForRegionV1]]. - * - * @param jsonVal the [[JsValue]] to be converted. - * @return a [[PropsGetForRegionV1]]. - */ + * Converts a [[JsValue]] to a [[PropsGetForRegionV1]]. + * + * @param jsonVal the [[JsValue]] to be converted. + * @return a [[PropsGetForRegionV1]]. + */ def read(jsonVal: JsValue): PropsGetForRegionV1 = { val jsonObj = jsonVal.asJsObject val properties: Map[String, JsValue] = jsonObj.fields - "res_id" - "iconsrc" - val propsConverted: Seq[PropertyGetV1] = properties.map { - case (propname: String, prop: JsValue) => - val propObj = prop.asJsObject + val propsConverted: Seq[PropertyGetV1] = properties.map { case (propname: String, prop: JsValue) => + val propObj = prop.asJsObject - PropertyGetV1( - pid = getRequiredString(propObj, "pid"), - label = getOptionalString(propObj, "label"), - /*values = propObj.fields.get("values") match { + PropertyGetV1( + pid = getRequiredString(propObj, "pid"), + label = getOptionalString(propObj, "label"), + /*values = propObj.fields.get("values") match { case Some(JsArray(valuesVector)) => valuesVector.map(_.convertTo[PropertyGetValueV1]) case _ => throw InvalidApiJsonException("missing or invalid 'values'") }*/ - // TODO: create an empty vector because for now we cannot recreate an ApiValueV1 from a JsValue since the read method - // TODO: of ValueV1JsonFormat in ValueMessagesV1 cannot deduce its value type. - values = Vector.empty[PropertyGetValueV1] - ) + // TODO: create an empty vector because for now we cannot recreate an ApiValueV1 from a JsValue since the read method + // TODO: of ValueV1JsonFormat in ValueMessagesV1 cannot deduce its value type. + values = Vector.empty[PropertyGetValueV1] + ) }.toSeq PropsGetForRegionV1( @@ -1123,11 +1149,11 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol } /** - * Converts a [[PropsGetForRegionV1]] into a [[JsValue]]. - * - * @param propsGetForRegionV1 the [[PropsGetForRegionV1]]. - * @return a [[JsValue]]. - */ + * Converts a [[PropsGetForRegionV1]] into a [[JsValue]]. + * + * @param propsGetForRegionV1 the [[PropsGetForRegionV1]]. + * @return a [[JsValue]]. + */ def write(propsGetForRegionV1: PropsGetForRegionV1): JsValue = { // Convert each PropertyGetV1 object into a JsObject. val properties: Map[IRI, JsValue] = propsGetForRegionV1.properties.map { (propertyGetV1: PropertyGetV1) => @@ -1146,27 +1172,30 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol }.toMap JsObject( - properties ++ Map("res_id" -> propsGetForRegionV1.res_id.toJson, - "iconsrc" -> propsGetForRegionV1.iconsrc.toJson)) // add res_id and iconsrc to response + properties ++ Map( + "res_id" -> propsGetForRegionV1.res_id.toJson, + "iconsrc" -> propsGetForRegionV1.iconsrc.toJson + ) + ) // add res_id and iconsrc to response } } /** - * Converts between [[ResourceInfoV1]] objects and [[JsValue]] objects. - */ + * Converts between [[ResourceInfoV1]] objects and [[JsValue]] objects. + */ implicit object ResourceInfoV1Format extends JsonFormat[ResourceInfoV1] { /** - * Not implemented. - */ + * Not implemented. + */ def read(jsonVal: JsValue): ResourceInfoV1 = ??? /** - * Converts a [[ResourceInfoV1]] into [[JsValue]] for formatting as JSON. - * - * @param resInfoV1 the [[ResourceInfoV1]] to be converted. - * @return a [[JsValue]]. - */ + * Converts a [[ResourceInfoV1]] into [[JsValue]] for formatting as JSON. + * + * @param resInfoV1 the [[ResourceInfoV1]] to be converted. + * @return a [[JsValue]]. + */ def write(resInfoV1: ResourceInfoV1): JsValue = { // Don't include the "firstproperty" field if we have no data for it. val firstProp: Option[(String, JsString)] = resInfoV1.firstproperty match { @@ -1197,18 +1226,18 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol } /** - * Converts between [[MultipleResourceCreateResponseV1]] objects and [[JsValue]] objects. - */ + * Converts between [[MultipleResourceCreateResponseV1]] objects and [[JsValue]] objects. + */ implicit object MultipleResourceCreateResponseV1Format extends JsonFormat[MultipleResourceCreateResponseV1] { /** - * Not implemented. - */ + * Not implemented. + */ override def read(json: JsValue): MultipleResourceCreateResponseV1 = ??? /** - * Converts a [[MultipleResourceCreateResponseV1]] into a [[JsValue]] for formatting as JSON. - */ + * Converts a [[MultipleResourceCreateResponseV1]] into a [[JsValue]] for formatting as JSON. + */ override def write(response: MultipleResourceCreateResponseV1): JsValue = { val fields = Map( "createdResources" -> response.createdResources.toJson @@ -1233,44 +1262,56 @@ object ResourceV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol implicit val createResourceValueV1Format: RootJsonFormat[CreateResourceValueV1] = jsonFormat15(CreateResourceValueV1) implicit val createResourceApiRequestV1Format: RootJsonFormat[CreateResourceApiRequestV1] = jsonFormat5( - CreateResourceApiRequestV1) + CreateResourceApiRequestV1 + ) implicit val ChangeResourceLabelApiRequestV1Format: RootJsonFormat[ChangeResourceLabelApiRequestV1] = jsonFormat1( - ChangeResourceLabelApiRequestV1) + ChangeResourceLabelApiRequestV1 + ) implicit val resourceInfoResponseV1Format: RootJsonFormat[ResourceInfoResponseV1] = jsonFormat2( - ResourceInfoResponseV1) + ResourceInfoResponseV1 + ) implicit val resourceDataV1Format: JsonFormat[ResourceDataV1] = jsonFormat5(ResourceDataV1) implicit val externalResourceIDV1Format: JsonFormat[ExternalResourceIDV1] = jsonFormat2(ExternalResourceIDV1) implicit val incomingV1Format: JsonFormat[IncomingV1] = jsonFormat3(IncomingV1) implicit val resourceFullResponseV1Format: RootJsonFormat[ResourceFullResponseV1] = jsonFormat5( - ResourceFullResponseV1) + ResourceFullResponseV1 + ) implicit val propertiesGetValueV1Format: JsonFormat[PropertyGetValueV1] = jsonFormat7(PropertyGetValueV1) implicit val propertiesGetResponseV1Format: RootJsonFormat[PropertiesGetResponseV1] = jsonFormat1( - PropertiesGetResponseV1) + PropertiesGetResponseV1 + ) implicit val resourceRightsResponseV1Format: RootJsonFormat[ResourceRightsResponseV1] = jsonFormat1( - ResourceRightsResponseV1) + ResourceRightsResponseV1 + ) implicit val resourceSearchResultV1Format: RootJsonFormat[ResourceSearchResultRowV1] = jsonFormat3( - ResourceSearchResultRowV1) + ResourceSearchResultRowV1 + ) implicit val resourceSearchResponseV1Format: RootJsonFormat[ResourceSearchResponseV1] = jsonFormat1( - ResourceSearchResponseV1) + ResourceSearchResponseV1 + ) implicit val resourceCreateValueObjectResponseV1Format: RootJsonFormat[ResourceCreateValueObjectResponseV1] = jsonFormat14(ResourceCreateValueObjectResponseV1) implicit val resourceCreateValueResponseV1Format: RootJsonFormat[ResourceCreateValueResponseV1] = jsonFormat2( - ResourceCreateValueResponseV1) + ResourceCreateValueResponseV1 + ) implicit val oneOfMultipleResourcesCreateResponseFormat: JsonFormat[OneOfMultipleResourcesCreateResponseV1] = jsonFormat3(OneOfMultipleResourcesCreateResponseV1) implicit val resourceDeleteResponseV1Format: RootJsonFormat[ResourceDeleteResponseV1] = jsonFormat1( - ResourceDeleteResponseV1) + ResourceDeleteResponseV1 + ) implicit val changeResourceLabelResponseV1Format: RootJsonFormat[ChangeResourceLabelResponseV1] = jsonFormat2( - ChangeResourceLabelResponseV1) + ChangeResourceLabelResponseV1 + ) implicit val graphNodeV1Format: JsonFormat[GraphNodeV1] = jsonFormat4(GraphNodeV1) implicit val graphEdgeV1Format: JsonFormat[GraphEdgeV1] = jsonFormat4(GraphEdgeV1) implicit val graphDataGetResponseV1Format: RootJsonFormat[GraphDataGetResponseV1] = jsonFormat2( - GraphDataGetResponseV1) + GraphDataGetResponseV1 + ) } /** - * A spray-json protocol for generating resource context information in Knora API v1 JSON format. - */ + * A spray-json protocol for generating resource context information in Knora API v1 JSON format. + */ object ResourceContextV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol { import ResourceContextCodeV1.ResourceContextCodeV1Protocol._ @@ -1278,5 +1319,6 @@ object ResourceContextV1JsonProtocol extends SprayJsonSupport with DefaultJsonPr implicit val resourceContextV1Format: JsonFormat[ResourceContextV1] = jsonFormat11(ResourceContextV1) implicit val resourceContextResponseV1Format: RootJsonFormat[ResourceContextResponseV1] = jsonFormat1( - ResourceContextResponseV1) + ResourceContextResponseV1 + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/searchmessages/SearchMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/searchmessages/SearchMessagesV1.scala index bb42ac5d17..0b03622e6e 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/searchmessages/SearchMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/searchmessages/SearchMessagesV1.scala @@ -30,8 +30,8 @@ import spray.json._ // Messages /** - * An abstract trait for messages that can be sent to `SearchResponderV1`. - */ + * An abstract trait for messages that can be sent to `SearchResponderV1`. + */ sealed trait SearchResponderRequestV1 extends KnoraRequestV1 { def searchValue: Any @@ -48,48 +48,51 @@ sealed trait SearchResponderRequestV1 extends KnoraRequestV1 { } /** - * Requests a fulltext search. A successful response will be a [[SearchGetResponseV1]]. - * - * @param userProfile the profile of the user making the request. - */ -case class FulltextSearchGetRequestV1(searchValue: String, - filterByRestype: Option[IRI] = None, - filterByProject: Option[IRI] = None, - startAt: Int, - showNRows: Int, - userProfile: UserADM) - extends SearchResponderRequestV1 + * Requests a fulltext search. A successful response will be a [[SearchGetResponseV1]]. + * + * @param userProfile the profile of the user making the request. + */ +case class FulltextSearchGetRequestV1( + searchValue: String, + filterByRestype: Option[IRI] = None, + filterByProject: Option[IRI] = None, + startAt: Int, + showNRows: Int, + userProfile: UserADM +) extends SearchResponderRequestV1 /** - * Requests an extended search. A successful response will be a [[SearchGetResponseV1]]. - * - * @param userProfile the profile of the user making the request. - */ -case class ExtendedSearchGetRequestV1(filterByRestype: Option[IRI] = None, - filterByProject: Option[IRI] = None, - filterByOwner: Option[IRI] = None, - propertyIri: Seq[IRI] = Nil, // parallel structure - propertyValueType: Seq[IRI] = Nil, - compareProps: Seq[SearchComparisonOperatorV1.Value] = Nil, // parallel structure - searchValue: Seq[String] = Nil, // parallel structure - startAt: Int, - showNRows: Int, - userProfile: UserADM) - extends SearchResponderRequestV1 + * Requests an extended search. A successful response will be a [[SearchGetResponseV1]]. + * + * @param userProfile the profile of the user making the request. + */ +case class ExtendedSearchGetRequestV1( + filterByRestype: Option[IRI] = None, + filterByProject: Option[IRI] = None, + filterByOwner: Option[IRI] = None, + propertyIri: Seq[IRI] = Nil, // parallel structure + propertyValueType: Seq[IRI] = Nil, + compareProps: Seq[SearchComparisonOperatorV1.Value] = Nil, // parallel structure + searchValue: Seq[String] = Nil, // parallel structure + startAt: Int, + showNRows: Int, + userProfile: UserADM +) extends SearchResponderRequestV1 /** - * Represents a response to a user search query (both fulltext and extended search) - * - * @param subjects list of [[SearchResultRowV1]] each representing on resource. - * @param nhits total number of hits. - * @param paging information for paging. - * @param thumb_max maximal dimensions of preview representations. - */ -case class SearchGetResponseV1(subjects: Seq[SearchResultRowV1] = Nil, - nhits: String, - paging: Seq[SearchResultPage] = Nil, - thumb_max: SearchPreviewDimensionsV1) - extends KnoraResponseV1 { + * Represents a response to a user search query (both fulltext and extended search) + * + * @param subjects list of [[SearchResultRowV1]] each representing on resource. + * @param nhits total number of hits. + * @param paging information for paging. + * @param thumb_max maximal dimensions of preview representations. + */ +case class SearchGetResponseV1( + subjects: Seq[SearchResultRowV1] = Nil, + nhits: String, + paging: Seq[SearchResultPage] = Nil, + thumb_max: SearchPreviewDimensionsV1 +) extends KnoraResponseV1 { def toJsValue = SearchV1JsonProtocol.searchResponseV1Format.write(this) } @@ -97,8 +100,8 @@ case class SearchGetResponseV1(subjects: Seq[SearchResultRowV1] = Nil, // Components of messages /** - * Enumeration representing the possible operators for comparing properties to a given value - */ + * Enumeration representing the possible operators for comparing properties to a given value + */ object SearchComparisonOperatorV1 extends Enumeration { val EXISTS = Value(0, "EXISTS") val EQ = Value(1, "EQ") @@ -114,66 +117,68 @@ object SearchComparisonOperatorV1 extends Enumeration { val valueMap: Map[String, SearchComparisonOperatorV1.Value] = values.map(v => (v.toString, v)).toMap - def lookup(name: String): SearchComparisonOperatorV1.Value = { + def lookup(name: String): SearchComparisonOperatorV1.Value = valueMap.get(name) match { case Some(value) => value case None => throw BadRequestException(s"compop type not supported: $name") } - } } /** - * The maximum X and Y dimensions of the preview representations in a list of search results. - * - * @param nx max width. - * @param ny max height. - */ + * The maximum X and Y dimensions of the preview representations in a list of search results. + * + * @param nx max width. + * @param ny max height. + */ case class SearchPreviewDimensionsV1(nx: Int, ny: Int) /** - * Represents one row (resource) in [[SearchGetResponseV1]] - * - * @param obj_id IRI of the retrieved resource. - * @param preview_path path to a preview representation. - * @param iconsrc icon representing the resource type. - * @param icontitle description of the resource type. - * @param iconlabel description of the resource type. - * @param valuetype_id value type of the first property. - * @param valuelabel label of the first property. - * @param value (text) value of the first property. - */ -case class SearchResultRowV1(obj_id: IRI, - preview_path: Option[String], - iconsrc: Option[String], - icontitle: Option[String], - iconlabel: Option[String], - valuetype_id: Seq[IRI], - valuelabel: Seq[String], - value: Seq[String] = Nil, - preview_nx: Int, - preview_ny: Int, - rights: Option[Int] = None) + * Represents one row (resource) in [[SearchGetResponseV1]] + * + * @param obj_id IRI of the retrieved resource. + * @param preview_path path to a preview representation. + * @param iconsrc icon representing the resource type. + * @param icontitle description of the resource type. + * @param iconlabel description of the resource type. + * @param valuetype_id value type of the first property. + * @param valuelabel label of the first property. + * @param value (text) value of the first property. + */ +case class SearchResultRowV1( + obj_id: IRI, + preview_path: Option[String], + iconsrc: Option[String], + icontitle: Option[String], + iconlabel: Option[String], + valuetype_id: Seq[IRI], + valuelabel: Seq[String], + value: Seq[String] = Nil, + preview_nx: Int, + preview_ny: Int, + rights: Option[Int] = None +) /** - * An element in a list of search result pages. - * - * @param current true if this element represents the current page. - * @param start_at the index of the first search result on the page. - * @param show_nrows the number of results on the page. - */ + * An element in a list of search result pages. + * + * @param current true if this element represents the current page. + * @param start_at the index of the first search result on the page. + * @param show_nrows the number of results on the page. + */ case class SearchResultPage(current: Boolean, start_at: Int, show_nrows: Int) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON providing data about representations of a resource. - */ + * A spray-json protocol for generating Knora API v1 JSON providing data about representations of a resource. + */ object SearchV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { implicit val searchResultPageV1Format: JsonFormat[SearchResultPage] = jsonFormat3(SearchResultPage) implicit val searchPreviewDimensionsV1Format: JsonFormat[SearchPreviewDimensionsV1] = jsonFormat2( - SearchPreviewDimensionsV1) + SearchPreviewDimensionsV1 + ) implicit val searchResultRowV1Format: JsonFormat[SearchResultRowV1] = jsonFormat11(SearchResultRowV1) implicit val searchResponseV1Format: RootJsonFormat[SearchGetResponseV1] = jsonFormat4(SearchGetResponseV1) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/standoffmessages/StandoffMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/standoffmessages/StandoffMessagesV1.scala index f1c3cd493c..82c619e3df 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/standoffmessages/StandoffMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/standoffmessages/StandoffMessagesV1.scala @@ -31,85 +31,89 @@ import org.knora.webapi.messages.v2.responder.standoffmessages.MappingXMLtoStand import spray.json._ /** - * An abstract trait representing a Knora v1 API request message that can be sent to `StandoffResponderV1`. - */ + * An abstract trait representing a Knora v1 API request message that can be sent to `StandoffResponderV1`. + */ sealed trait StandoffResponderRequestV1 extends KnoraRequestV1 /** - * Represents a request to create a mapping between XML elements and attributes and standoff classes and properties. - * A successful response will be a [[CreateMappingResponseV1]]. - * - * @param xml the mapping in XML. - * @param projectIri the IRI of the project the mapping belongs to. - * @param mappingName the name of the mapping to be created. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ -case class CreateMappingRequestV1(xml: String, - label: String, - projectIri: IRI, - mappingName: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends StandoffResponderRequestV1 + * Represents a request to create a mapping between XML elements and attributes and standoff classes and properties. + * A successful response will be a [[CreateMappingResponseV1]]. + * + * @param xml the mapping in XML. + * @param projectIri the IRI of the project the mapping belongs to. + * @param mappingName the name of the mapping to be created. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ +case class CreateMappingRequestV1( + xml: String, + label: String, + projectIri: IRI, + mappingName: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends StandoffResponderRequestV1 /** - * Provides the IRI of the created mapping. - * - * @param mappingIri the IRI of the resource (knora-base:XMLToStandoffMapping) representing the mapping that has been created. - */ + * Provides the IRI of the created mapping. + * + * @param mappingIri the IRI of the resource (knora-base:XMLToStandoffMapping) representing the mapping that has been created. + */ case class CreateMappingResponseV1(mappingIri: IRI) extends KnoraResponseV1 { def toJsValue: JsValue = RepresentationV1JsonProtocol.createMappingResponseV1Format.write(this) } /** - * Represents a request to get a mapping from XML elements and attributes to standoff entities. - * - * @param mappingIri the IRI of the mapping. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ + * Represents a request to get a mapping from XML elements and attributes to standoff entities. + * + * @param mappingIri the IRI of the mapping. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ case class GetMappingRequestV1(mappingIri: IRI, featureFactoryConfig: FeatureFactoryConfig, userProfile: UserADM) extends StandoffResponderRequestV1 /** - * Represents a response to a [[GetMappingRequestV1]]. - * - * @param mappingIri the IRI of the requested mapping. - * @param mapping the requested mapping. - * @param standoffEntities the standoff entities referred to in the mapping. - */ -case class GetMappingResponseV1(mappingIri: IRI, - mapping: MappingXMLtoStandoff, - standoffEntities: StandoffEntityInfoGetResponseV1) + * Represents a response to a [[GetMappingRequestV1]]. + * + * @param mappingIri the IRI of the requested mapping. + * @param mapping the requested mapping. + * @param standoffEntities the standoff entities referred to in the mapping. + */ +case class GetMappingResponseV1( + mappingIri: IRI, + mapping: MappingXMLtoStandoff, + standoffEntities: StandoffEntityInfoGetResponseV1 +) /** - * Represents a request that gets an XSL Transformation represented by a `knora-base:XSLTransformation`. - * - * @param xsltTextRepresentationIri the IRI of the `knora-base:XSLTransformation`. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ -case class GetXSLTransformationRequestV1(xsltTextRepresentationIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM) - extends StandoffResponderRequestV1 + * Represents a request that gets an XSL Transformation represented by a `knora-base:XSLTransformation`. + * + * @param xsltTextRepresentationIri the IRI of the `knora-base:XSLTransformation`. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ +case class GetXSLTransformationRequestV1( + xsltTextRepresentationIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM +) extends StandoffResponderRequestV1 /** - * Represents a response to a [[GetXSLTransformationRequestV1]]. - * - * @param xslt the XSLT to be applied to the XML created from standoff. - */ + * Represents a response to a [[GetXSLTransformationRequestV1]]. + * + * @param xslt the XSLT to be applied to the XML created from standoff. + */ case class GetXSLTransformationResponseV1(xslt: String) /** - * Represents an API request to create a mapping. - * - * @param project_id the project in which the mapping is to be added. - * @param label the label describing the mapping. - * @param mappingName the name of the mapping (will be appended to the mapping IRI). - */ + * Represents an API request to create a mapping. + * + * @param project_id the project in which the mapping is to be added. + * @param label the label describing the mapping. + * @param mappingName the name of the mapping (will be appended to the mapping IRI). + */ case class CreateMappingApiRequestV1(project_id: IRI, label: String, mappingName: String) { def toJsValue: JsValue = RepresentationV1JsonProtocol.createMappingApiRequestV1Format.write(this) } @@ -118,12 +122,14 @@ case class CreateMappingApiRequestV1(project_id: IRI, label: String, mappingName // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON for standoff handling. - */ + * A spray-json protocol for generating Knora API v1 JSON for standoff handling. + */ object RepresentationV1JsonProtocol extends DefaultJsonProtocol with NullOptions with SprayJsonSupport { implicit val createMappingApiRequestV1Format: RootJsonFormat[CreateMappingApiRequestV1] = jsonFormat3( - CreateMappingApiRequestV1) + CreateMappingApiRequestV1 + ) implicit val createMappingResponseV1Format: RootJsonFormat[CreateMappingResponseV1] = jsonFormat1( - CreateMappingResponseV1) + CreateMappingResponseV1 + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/usermessages/UserMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/usermessages/UserMessagesV1.scala index f365743d6a..d874e9cf43 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/usermessages/UserMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/usermessages/UserMessagesV1.scala @@ -40,156 +40,159 @@ import spray.json._ // Messages /** - * An abstract trait representing message that can be sent to `UsersResponderV1`. - */ + * An abstract trait representing message that can be sent to `UsersResponderV1`. + */ sealed trait UsersResponderRequestV1 extends KnoraRequestV1 /** - * Get all information about all users in form of [[UsersGetResponseV1]]. The UsersGetRequestV1 returns either - * something or a NotFound exception if there are no users found. Administration permission checking is performed. - * - * @param userProfileV1 the profile of the user that is making the request. - */ + * Get all information about all users in form of [[UsersGetResponseV1]]. The UsersGetRequestV1 returns either + * something or a NotFound exception if there are no users found. Administration permission checking is performed. + * + * @param userProfileV1 the profile of the user that is making the request. + */ case class UsersGetRequestV1(userProfileV1: UserProfileV1) extends UsersResponderRequestV1 /** - * Get all information about all users in form of a sequence of [[UserDataV1]]. Returns an empty sequence if - * no users are found. Administration permission checking is skipped. - * - */ + * Get all information about all users in form of a sequence of [[UserDataV1]]. Returns an empty sequence if + * no users are found. Administration permission checking is skipped. + */ case class UsersGetV1(userProfile: UserProfileV1) extends UsersResponderRequestV1 /** - * A message that requests basic user data. A successful response will be a [[UserDataV1]]. - * - * @param userIri the IRI of the user to be queried. - * @param short denotes if all information should be returned. If short == true, then token and password are not returned. - */ + * A message that requests basic user data. A successful response will be a [[UserDataV1]]. + * + * @param userIri the IRI of the user to be queried. + * @param short denotes if all information should be returned. If short == true, then token and password are not returned. + */ case class UserDataByIriGetV1(userIri: IRI, short: Boolean = true) extends UsersResponderRequestV1 /** - * A message that requests a user's profile. A successful response will be a [[UserProfileResponseV1]]. - * - * @param userIri the IRI of the user to be queried. - * @param userProfileType the extent of the information returned. - * @param featureFactoryConfig the feature factory configuration. - */ -case class UserProfileByIRIGetRequestV1(userIri: IRI, - userProfileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserProfileV1) - extends UsersResponderRequestV1 + * A message that requests a user's profile. A successful response will be a [[UserProfileResponseV1]]. + * + * @param userIri the IRI of the user to be queried. + * @param userProfileType the extent of the information returned. + * @param featureFactoryConfig the feature factory configuration. + */ +case class UserProfileByIRIGetRequestV1( + userIri: IRI, + userProfileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserProfileV1 +) extends UsersResponderRequestV1 /** - * A message that requests a user's profile. A successful response will be a [[UserProfileV1]]. - * - * @param userIri the IRI of the user to be queried. - * @param userProfileType the extent of the information returned. - */ -case class UserProfileByIRIGetV1(userIri: IRI, - userProfileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig) - extends UsersResponderRequestV1 + * A message that requests a user's profile. A successful response will be a [[UserProfileV1]]. + * + * @param userIri the IRI of the user to be queried. + * @param userProfileType the extent of the information returned. + */ +case class UserProfileByIRIGetV1( + userIri: IRI, + userProfileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig +) extends UsersResponderRequestV1 /** - * A message that requests a user's profile. A successful response will be a [[UserProfileResponseV1]]. - * - * @param email the email of the user to be queried. - * @param userProfileType the extent of the information returned. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the requesting user's profile. - */ -case class UserProfileByEmailGetRequestV1(email: String, - userProfileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserProfileV1) - extends UsersResponderRequestV1 + * A message that requests a user's profile. A successful response will be a [[UserProfileResponseV1]]. + * + * @param email the email of the user to be queried. + * @param userProfileType the extent of the information returned. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the requesting user's profile. + */ +case class UserProfileByEmailGetRequestV1( + email: String, + userProfileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserProfileV1 +) extends UsersResponderRequestV1 /** - * A message that requests a user's profile. A successful response will be a [[UserProfileV1]]. - * - * @param email the email of the user to be queried. - * @param userProfileType the extent of the information returned. - * @param featureFactoryConfig the feature factory configuration. - */ -case class UserProfileByEmailGetV1(email: String, - userProfileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig) - extends UsersResponderRequestV1 + * A message that requests a user's profile. A successful response will be a [[UserProfileV1]]. + * + * @param email the email of the user to be queried. + * @param userProfileType the extent of the information returned. + * @param featureFactoryConfig the feature factory configuration. + */ +case class UserProfileByEmailGetV1( + email: String, + userProfileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig +) extends UsersResponderRequestV1 /** - * Requests user's project memberships. - * - * @param userIri the IRI of the user. - * @param userProfileV1 the user profile of the user requesting the update. - * @param apiRequestID the ID of the API request. - */ + * Requests user's project memberships. + * + * @param userIri the IRI of the user. + * @param userProfileV1 the user profile of the user requesting the update. + * @param apiRequestID the ID of the API request. + */ case class UserProjectMembershipsGetRequestV1(userIri: IRI, userProfileV1: UserProfileV1, apiRequestID: UUID) extends UsersResponderRequestV1 /** - * Requests user's project admin memberships. - * - * @param userIri the IRI of the user. - * @param userProfileV1 the user profile of the user requesting the update. - * @param apiRequestID the ID of the API request. - */ + * Requests user's project admin memberships. + * + * @param userIri the IRI of the user. + * @param userProfileV1 the user profile of the user requesting the update. + * @param apiRequestID the ID of the API request. + */ case class UserProjectAdminMembershipsGetRequestV1(userIri: IRI, userProfileV1: UserProfileV1, apiRequestID: UUID) extends UsersResponderRequestV1 /** - * Requests user's group memberships. - * - * @param userIri the IRI of the user. - * @param userProfileV1 the user profile of the user requesting the update. - * @param apiRequestID the ID of the API request. - */ + * Requests user's group memberships. + * + * @param userIri the IRI of the user. + * @param userProfileV1 the user profile of the user requesting the update. + * @param apiRequestID the ID of the API request. + */ case class UserGroupMembershipsGetRequestV1(userIri: IRI, userProfileV1: UserProfileV1, apiRequestID: UUID) extends UsersResponderRequestV1 // Responses /** - * Represents an answer to a request for a list of all users. - * - * @param users a sequence of user profiles of the requested type. - */ + * Represents an answer to a request for a list of all users. + * + * @param users a sequence of user profiles of the requested type. + */ case class UsersGetResponseV1(users: Seq[UserDataV1]) extends KnoraResponseV1 { def toJsValue: JsValue = UserV1JsonProtocol.usersGetResponseV1Format.write(this) } /** - * Represents an answer to a user profile request. - * - * @param userProfile the user's profile of the requested type. - */ + * Represents an answer to a user profile request. + * + * @param userProfile the user's profile of the requested type. + */ case class UserProfileResponseV1(userProfile: UserProfileV1) extends KnoraResponseV1 { def toJsValue: JsValue = UserV1JsonProtocol.userProfileResponseV1Format.write(this) } /** - * Represents an answer to a request for a list of all projects the user is member of. - * - * @param projects a sequence of projects the user is member of. - */ + * Represents an answer to a request for a list of all projects the user is member of. + * + * @param projects a sequence of projects the user is member of. + */ case class UserProjectMembershipsGetResponseV1(projects: Seq[IRI]) extends KnoraResponseV1 { def toJsValue: JsValue = UserV1JsonProtocol.userProjectMembershipsGetResponseV1Format.write(this) } /** - * Represents an answer to a request for a list of all projects the user is member of the project admin group. - * - * @param projects a sequence of projects the user is member of the project admin group. - */ + * Represents an answer to a request for a list of all projects the user is member of the project admin group. + * + * @param projects a sequence of projects the user is member of the project admin group. + */ case class UserProjectAdminMembershipsGetResponseV1(projects: Seq[IRI]) extends KnoraResponseV1 { def toJsValue: JsValue = UserV1JsonProtocol.userProjectAdminMembershipsGetResponseV1Format.write(this) } /** - * Represents an answer to a request for a list of all groups the user is member of. - * - * @param groups a sequence of groups the user is member of. - */ + * Represents an answer to a request for a list of all groups the user is member of. + * + * @param groups a sequence of groups the user is member of. + */ case class UserGroupMembershipsGetResponseV1(groups: Seq[IRI]) extends KnoraResponseV1 { def toJsValue: JsValue = UserV1JsonProtocol.userGroupMembershipsGetResponseV1Format.write(this) } @@ -198,29 +201,31 @@ case class UserGroupMembershipsGetResponseV1(groups: Seq[IRI]) extends KnoraResp // Components of messages /** - * Represents a user's profile. - * - * @param userData basic information about the user. - * @param groups the groups that the user belongs to. - * @param projects_info the projects that the user belongs to. - * @param sessionId the sessionId,. - * @param permissionData the user's permission data. - */ -case class UserProfileV1(userData: UserDataV1 = UserDataV1(lang = "en"), - groups: Seq[IRI] = Seq.empty[IRI], - projects_info: Map[IRI, ProjectInfoV1] = Map.empty[IRI, ProjectInfoV1], - sessionId: Option[String] = None, - isSystemUser: Boolean = false, - permissionData: PermissionsDataADM = PermissionsDataADM()) { + * Represents a user's profile. + * + * @param userData basic information about the user. + * @param groups the groups that the user belongs to. + * @param projects_info the projects that the user belongs to. + * @param sessionId the sessionId,. + * @param permissionData the user's permission data. + */ +case class UserProfileV1( + userData: UserDataV1 = UserDataV1(lang = "en"), + groups: Seq[IRI] = Seq.empty[IRI], + projects_info: Map[IRI, ProjectInfoV1] = Map.empty[IRI, ProjectInfoV1], + sessionId: Option[String] = None, + isSystemUser: Boolean = false, + permissionData: PermissionsDataADM = PermissionsDataADM() +) { /** - * Check password using either SHA-1 or SCrypt. - * The SCrypt password always starts with '$e0801$' (spring.framework implementation) - * - * @param password the password to check. - * @return true if password matches and false if password doesn't match. - */ - def passwordMatch(password: String): Boolean = { + * Check password using either SHA-1 or SCrypt. + * The SCrypt password always starts with '$e0801$' (spring.framework implementation) + * + * @param password the password to check. + * @return true if password matches and false if password doesn't match. + */ + def passwordMatch(password: String): Boolean = userData.password.exists { hashedPassword => // check which type of hash we have if (hashedPassword.startsWith("$e0801$")) { @@ -239,15 +244,13 @@ case class UserProfileV1(userData: UserDataV1 = UserDataV1(lang = "en"), md.digest(password.getBytes("UTF-8")).map("%02x".format(_)).mkString.equals(hashedPassword) } } - } /** - * Creating a [[UserProfileV1]] of the requested type. - * - * @return a [[UserProfileV1]] - */ - def ofType(userProfileType: UserProfileType): UserProfileV1 = { - + * Creating a [[UserProfileV1]] of the requested type. + * + * @return a [[UserProfileV1]] + */ + def ofType(userProfileType: UserProfileType): UserProfileV1 = userProfileType match { case UserProfileTypeV1.SHORT => val oldUserData = userData @@ -299,7 +302,6 @@ case class UserProfileV1(userData: UserDataV1 = UserDataV1(lang = "en"), ) case _ => throw BadRequestException(s"The requested userProfileType: $userProfileType is invalid.") } - } def getDigest: String = { val md = java.security.MessageDigest.getInstance("SHA-1") @@ -308,71 +310,69 @@ case class UserProfileV1(userData: UserDataV1 = UserDataV1(lang = "en"), md.digest(value).map("%02x".format(_)).mkString } - def setSessionId(sessionId: String): UserProfileV1 = { + def setSessionId(sessionId: String): UserProfileV1 = UserProfileV1( userData = userData, groups = groups, permissionData = permissionData, sessionId = Some(sessionId) ) - } - def isAnonymousUser: Boolean = { + def isAnonymousUser: Boolean = userData.user_id.isEmpty - } - def isActive: Boolean = { + def isActive: Boolean = userData.status.getOrElse(false) - } def toJsValue: JsValue = UserV1JsonProtocol.userProfileV1Format.write(this) } /** - * Represents basic information about a user. - * - * @param user_id The user's IRI. - * @param email The user's email address. - * @param password The user's hashed password. - * @param token The API token. Can be used instead of email/password for authentication. - * @param firstname The user's given name. - * @param lastname The user's surname. - * @param status The user's status. - * @param lang The ISO 639-1 code of the user's preferred language. - */ -case class UserDataV1(user_id: Option[IRI] = None, - email: Option[String] = None, - password: Option[String] = None, - token: Option[String] = None, - firstname: Option[String] = None, - lastname: Option[String] = None, - status: Option[Boolean] = Some(true), - lang: String) { - - def fullname: Option[String] = { + * Represents basic information about a user. + * + * @param user_id The user's IRI. + * @param email The user's email address. + * @param password The user's hashed password. + * @param token The API token. Can be used instead of email/password for authentication. + * @param firstname The user's given name. + * @param lastname The user's surname. + * @param status The user's status. + * @param lang The ISO 639-1 code of the user's preferred language. + */ +case class UserDataV1( + user_id: Option[IRI] = None, + email: Option[String] = None, + password: Option[String] = None, + token: Option[String] = None, + firstname: Option[String] = None, + lastname: Option[String] = None, + status: Option[Boolean] = Some(true), + lang: String +) { + + def fullname: Option[String] = (firstname, lastname) match { case (Some(firstnameStr), Some(lastnameStr)) => Some(firstnameStr + " " + lastnameStr) case (Some(firstnameStr), None) => Some(firstnameStr) case (None, Some(lastnameStr)) => Some(lastnameStr) case (None, None) => None } - } def toJsValue: JsValue = UserV1JsonProtocol.userDataV1Format.write(this) } /** - * UserProfile types: - * restricted: everything without sensitive information, i.e. token, password. - * full: everything. - * - * Mainly used in combination with the 'ofType' method, to make sure that a request receiving this information - * also returns the user profile of the correct type. Should be used in cases where we don't want to expose - * sensitive information to the outside world. Since in API V1 [[UserDataV1]] is returned with some responses, - * we use 'restricted' in those cases. - */ + * UserProfile types: + * restricted: everything without sensitive information, i.e. token, password. + * full: everything. + * + * Mainly used in combination with the 'ofType' method, to make sure that a request receiving this information + * also returns the user profile of the correct type. Should be used in cases where we don't want to expose + * sensitive information to the outside world. Since in API V1 [[UserDataV1]] is returned with some responses, + * we use 'restricted' in those cases. + */ object UserProfileTypeV1 extends Enumeration { /* TODO: Extend to incorporate user privacy wishes */ @@ -385,26 +385,25 @@ object UserProfileTypeV1 extends Enumeration { val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an - * [[InconsistentRepositoryDataException]]. - * - * @param name the name of the value. - * @return the requested value. - */ - def lookup(name: String): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an + * [[InconsistentRepositoryDataException]]. + * + * @param name the name of the value. + * @return the requested value. + */ + def lookup(name: String): Value = valueMap.get(name) match { case Some(value) => value case None => throw InconsistentRepositoryDataException(s"User profile type not supported: $name") } - } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting /** - * A spray-json protocol for formatting objects as JSON. - */ + * A spray-json protocol for formatting objects as JSON. + */ object UserV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol @@ -421,5 +420,6 @@ object UserV1JsonProtocol implicit val userProjectAdminMembershipsGetResponseV1Format : RootJsonFormat[UserProjectAdminMembershipsGetResponseV1] = jsonFormat1(UserProjectAdminMembershipsGetResponseV1) implicit val userGroupMembershipsGetResponseV1Format: RootJsonFormat[UserGroupMembershipsGetResponseV1] = jsonFormat1( - UserGroupMembershipsGetResponseV1) + UserGroupMembershipsGetResponseV1 + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/valuemessages/ValueMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/valuemessages/ValueMessagesV1.scala index 779db23ebe..ba86ee5d83 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/valuemessages/ValueMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/responder/valuemessages/ValueMessagesV1.scala @@ -43,61 +43,65 @@ import spray.json._ // API requests /** - * Represents an API request payload that asks the Knora API server to create a new value of a resource property - * (as opposed to a new version of an existing value). - * - * @param res_id the IRI of the resource in which the value is to be added. - * @param prop the property that is to receive the value. - * @param richtext_value a rich-text object to be used in the value. - * @param int_value an integer literal to be used in the value. - * @param decimal_value a decimal literal to be used in the value. - * @param date_value a date object to be used in the value. - * @param color_value a colour literal to be used in the value. - * @param geom_value a geometry literal to be used in the value. - * @param comment a comment to add to the value. - */ -case class CreateValueApiRequestV1(res_id: IRI, - prop: IRI, - richtext_value: Option[CreateRichtextV1] = None, - link_value: Option[IRI] = None, - int_value: Option[Int] = None, - decimal_value: Option[BigDecimal] = None, - boolean_value: Option[Boolean] = None, - uri_value: Option[String] = None, - date_value: Option[String] = None, - color_value: Option[String] = None, - geom_value: Option[String] = None, - hlist_value: Option[IRI] = None, - interval_value: Option[Seq[BigDecimal]] = None, - time_value: Option[String] = None, - geoname_value: Option[String] = None, - comment: Option[String] = None) { + * Represents an API request payload that asks the Knora API server to create a new value of a resource property + * (as opposed to a new version of an existing value). + * + * @param res_id the IRI of the resource in which the value is to be added. + * @param prop the property that is to receive the value. + * @param richtext_value a rich-text object to be used in the value. + * @param int_value an integer literal to be used in the value. + * @param decimal_value a decimal literal to be used in the value. + * @param date_value a date object to be used in the value. + * @param color_value a colour literal to be used in the value. + * @param geom_value a geometry literal to be used in the value. + * @param comment a comment to add to the value. + */ +case class CreateValueApiRequestV1( + res_id: IRI, + prop: IRI, + richtext_value: Option[CreateRichtextV1] = None, + link_value: Option[IRI] = None, + int_value: Option[Int] = None, + decimal_value: Option[BigDecimal] = None, + boolean_value: Option[Boolean] = None, + uri_value: Option[String] = None, + date_value: Option[String] = None, + color_value: Option[String] = None, + geom_value: Option[String] = None, + hlist_value: Option[IRI] = None, + interval_value: Option[Seq[BigDecimal]] = None, + time_value: Option[String] = None, + geoname_value: Option[String] = None, + comment: Option[String] = None +) { // Make sure only one value is given. - if (List( - richtext_value, - link_value, - int_value, - decimal_value, - boolean_value, - uri_value, - date_value, - color_value, - geom_value, - hlist_value, - interval_value, - time_value, - geoname_value - ).flatten.size > 1) { + if ( + List( + richtext_value, + link_value, + int_value, + decimal_value, + boolean_value, + uri_value, + date_value, + color_value, + geom_value, + hlist_value, + interval_value, + time_value, + geoname_value + ).flatten.size > 1 + ) { throw BadRequestException(s"Different value types were submitted for property $prop") } /** - * Returns the type of the given value. - * - * @return a value type IRI. - */ - def getValueClassIri: IRI = { + * Returns the type of the given value. + * + * @return a value type IRI. + */ + def getValueClassIri: IRI = if (richtext_value.nonEmpty) OntologyConstants.KnoraBase.TextValue else if (link_value.nonEmpty) OntologyConstants.KnoraBase.LinkValue else if (int_value.nonEmpty) OntologyConstants.KnoraBase.IntValue @@ -112,32 +116,33 @@ case class CreateValueApiRequestV1(res_id: IRI, else if (time_value.nonEmpty) OntologyConstants.KnoraBase.TimeValue else if (geoname_value.nonEmpty) OntologyConstants.KnoraBase.GeonameValue else throw BadRequestException("No value specified") - } } /** - * Represents a richtext object consisting of text, text attributes and resource references. - * - * @param utf8str a mere string in case of a text without any markup. - * @param xml xml in case of a text with markup. - * @param mapping_id IRI of the mapping used to transform XML to standoff. - */ -case class CreateRichtextV1(utf8str: Option[String] = None, - language: Option[String] = None, - xml: Option[String] = None, - mapping_id: Option[IRI] = None) { + * Represents a richtext object consisting of text, text attributes and resource references. + * + * @param utf8str a mere string in case of a text without any markup. + * @param xml xml in case of a text with markup. + * @param mapping_id IRI of the mapping used to transform XML to standoff. + */ +case class CreateRichtextV1( + utf8str: Option[String] = None, + language: Option[String] = None, + xml: Option[String] = None, + mapping_id: Option[IRI] = None +) { def toJsValue: JsValue = ApiValueV1JsonProtocol.createRichtextV1Format.write(this) } /** - * Represents a file value to be added to a Knora resource. - * - * @param originalFilename the original name of the file. - * @param originalMimeType the original mime type of the file. - * @param filename the name of the file to be attached to a Knora-resource (file is temporarily stored by SIPI). - */ + * Represents a file value to be added to a Knora resource. + * + * @param originalFilename the original name of the file. + * @param originalMimeType the original mime type of the file. + * @param filename the name of the file to be attached to a Knora-resource (file is temporarily stored by SIPI). + */ case class CreateFileV1(originalFilename: String, originalMimeType: String, filename: String) { def toJsValue: JsValue = ApiValueV1JsonProtocol.createFileV1Format.write(this) @@ -145,40 +150,42 @@ case class CreateFileV1(originalFilename: String, originalMimeType: String, file } /** - * Represents an API request payload that asks the Knora API server to change a value of a resource property (i.e. to - * update its version history). - * - * @param richtext_value a rich-text object to be used in the value. - * @param int_value an integer literal to be used in the value. - * @param decimal_value a decimal literal to be used in the value. - * @param date_value a date object to be used in the value. - * @param color_value a colour literal to be used in the value. - * @param geom_value a geometry literal to be used in the value. - * @param comment a comment to add to the value. - */ -case class ChangeValueApiRequestV1(richtext_value: Option[CreateRichtextV1] = None, - link_value: Option[IRI] = None, - int_value: Option[Int] = None, - decimal_value: Option[BigDecimal] = None, - boolean_value: Option[Boolean] = None, - uri_value: Option[String] = None, - date_value: Option[String] = None, - color_value: Option[String] = None, - geom_value: Option[String] = None, - hlist_value: Option[IRI] = None, - interval_value: Option[Seq[BigDecimal]] = None, - time_value: Option[String] = None, - geoname_value: Option[String] = None, - comment: Option[String] = None) { + * Represents an API request payload that asks the Knora API server to change a value of a resource property (i.e. to + * update its version history). + * + * @param richtext_value a rich-text object to be used in the value. + * @param int_value an integer literal to be used in the value. + * @param decimal_value a decimal literal to be used in the value. + * @param date_value a date object to be used in the value. + * @param color_value a colour literal to be used in the value. + * @param geom_value a geometry literal to be used in the value. + * @param comment a comment to add to the value. + */ +case class ChangeValueApiRequestV1( + richtext_value: Option[CreateRichtextV1] = None, + link_value: Option[IRI] = None, + int_value: Option[Int] = None, + decimal_value: Option[BigDecimal] = None, + boolean_value: Option[Boolean] = None, + uri_value: Option[String] = None, + date_value: Option[String] = None, + color_value: Option[String] = None, + geom_value: Option[String] = None, + hlist_value: Option[IRI] = None, + interval_value: Option[Seq[BigDecimal]] = None, + time_value: Option[String] = None, + geoname_value: Option[String] = None, + comment: Option[String] = None +) { /** - * Returns the type of the given value. - * - * TODO: make sure that only one value is given. - * - * @return a value type IRI. - */ - def getValueClassIri: IRI = { + * Returns the type of the given value. + * + * TODO: make sure that only one value is given. + * + * @return a value type IRI. + */ + def getValueClassIri: IRI = if (richtext_value.nonEmpty) OntologyConstants.KnoraBase.TextValue else if (link_value.nonEmpty) OntologyConstants.KnoraBase.LinkValue else if (int_value.nonEmpty) OntologyConstants.KnoraBase.IntValue @@ -193,16 +200,15 @@ case class ChangeValueApiRequestV1(richtext_value: Option[CreateRichtextV1] = No else if (time_value.nonEmpty) OntologyConstants.KnoraBase.TimeValue else if (geoname_value.nonEmpty) OntologyConstants.KnoraBase.GeonameValue else throw BadRequestException("No value specified") - } } /** - * Represents an API request payload that asks the Knora API server to change the file attached to a resource - * (i. e. to create a new version of its file value). - * - * @param file the name of a file that has been uploaded to Sipi's temporary storage. - */ + * Represents an API request payload that asks the Knora API server to change the file attached to a resource + * (i. e. to create a new version of its file value). + * + * @param file the name of a file that has been uploaded to Sipi's temporary storage. + */ case class ChangeFileValueApiRequestV1(file: String) { def toJsValue: JsValue = ApiValueV1JsonProtocol.changeFileValueApiRequestV1Format.write(this) @@ -212,310 +218,322 @@ case class ChangeFileValueApiRequestV1(file: String) { // Messages /** - * An abstract trait representing a message that can be sent to [[org.knora.webapi.responders.v1.ValuesResponderV1]]. - */ + * An abstract trait representing a message that can be sent to [[org.knora.webapi.responders.v1.ValuesResponderV1]]. + */ sealed trait ValuesResponderRequestV1 extends KnoraRequestV1 /** - * Represents a request for a (current) value. A successful response will be a [[ValueGetResponseV1]]. - * - * @param valueIri the IRI of the value requested. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ + * Represents a request for a (current) value. A successful response will be a [[ValueGetResponseV1]]. + * + * @param valueIri the IRI of the value requested. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ case class ValueGetRequestV1(valueIri: IRI, featureFactoryConfig: FeatureFactoryConfig, userProfile: UserADM) extends ValuesResponderRequestV1 /** - * Represents a request for the details of a reification node describing a direct link between two resources. - * A successful response will be a [[ValueGetResponseV1]] containing a [[LinkValueV1]]. - * - * @param subjectIri the IRI of the resource that is the source of the link. - * @param predicateIri the IRI of the property that links the two resources. - * @param objectIri the IRI of the resource that is the target of the link. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ -case class LinkValueGetRequestV1(subjectIri: IRI, - predicateIri: IRI, - objectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM) - extends ValuesResponderRequestV1 + * Represents a request for the details of a reification node describing a direct link between two resources. + * A successful response will be a [[ValueGetResponseV1]] containing a [[LinkValueV1]]. + * + * @param subjectIri the IRI of the resource that is the source of the link. + * @param predicateIri the IRI of the property that links the two resources. + * @param objectIri the IRI of the resource that is the target of the link. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ +case class LinkValueGetRequestV1( + subjectIri: IRI, + predicateIri: IRI, + objectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM +) extends ValuesResponderRequestV1 /** - * Provides details of a Knora value. A successful response will be a [[ValueGetResponseV1]]. - * - * @param value the single requested value. - * @param valuetype the IRI of the value's type. - * @param valuecreator the username of the user who created the value. - * @param valuecreatorname the name of the user who created the value. - * @param valuecreationdate the date when the value was created. - * @param comment the comment on the value, if any. - * @param rights the user's permission on the value. - */ -case class ValueGetResponseV1(valuetype: IRI, - value: ApiValueV1, - valuecreator: String, - valuecreatorname: String, - valuecreationdate: String, - comment: Option[String] = None, - rights: Int) - extends KnoraResponseV1 { + * Provides details of a Knora value. A successful response will be a [[ValueGetResponseV1]]. + * + * @param value the single requested value. + * @param valuetype the IRI of the value's type. + * @param valuecreator the username of the user who created the value. + * @param valuecreatorname the name of the user who created the value. + * @param valuecreationdate the date when the value was created. + * @param comment the comment on the value, if any. + * @param rights the user's permission on the value. + */ +case class ValueGetResponseV1( + valuetype: IRI, + value: ApiValueV1, + valuecreator: String, + valuecreatorname: String, + valuecreationdate: String, + comment: Option[String] = None, + rights: Int +) extends KnoraResponseV1 { def toJsValue: JsValue = ApiValueV1JsonProtocol.valueGetResponseV1Format.write(this) } /** - * Represents a request for the version history of a value. A successful response will be a [[ValueVersionHistoryGetResponseV1]]. - * - * @param resourceIri the IRI of the resource that the value belongs to. - * @param propertyIri the IRI of the property that points to the value. - * @param currentValueIri the IRI of the current version of the value. - * @param userProfile the profile of the user making the request. - */ -case class ValueVersionHistoryGetRequestV1(resourceIri: IRI, - propertyIri: IRI, - currentValueIri: IRI, - userProfile: UserADM) - extends ValuesResponderRequestV1 + * Represents a request for the version history of a value. A successful response will be a [[ValueVersionHistoryGetResponseV1]]. + * + * @param resourceIri the IRI of the resource that the value belongs to. + * @param propertyIri the IRI of the property that points to the value. + * @param currentValueIri the IRI of the current version of the value. + * @param userProfile the profile of the user making the request. + */ +case class ValueVersionHistoryGetRequestV1( + resourceIri: IRI, + propertyIri: IRI, + currentValueIri: IRI, + userProfile: UserADM +) extends ValuesResponderRequestV1 /** - * Provides the version history of a value. - * - * @param valueVersions a list of the versions of the value, from newest to oldest. - */ + * Provides the version history of a value. + * + * @param valueVersions a list of the versions of the value, from newest to oldest. + */ case class ValueVersionHistoryGetResponseV1(valueVersions: Seq[ValueVersionV1]) extends KnoraResponseV1 { def toJsValue: JsValue = ApiValueV1JsonProtocol.valueVersionHistoryGetResponseV1Format.write(this) } /** - * Represents a request to add a new value of a resource property (as opposed to a new version of an existing value). A - * successful response will be an [[CreateValueResponseV1]]. - * - * @param resourceIndex the index of the resource - * @param resourceIri the IRI of the resource to which the value should be added. - * @param propertyIri the IRI of the property that should receive the value. - * @param value the value to be added. - * @param comment an optional comment on the value. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the ID of this API request. - */ -case class CreateValueRequestV1(resourceIndex: Int = 0, - resourceIri: IRI, - propertyIri: IRI, - value: UpdateValueV1, - comment: Option[String] = None, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV1 + * Represents a request to add a new value of a resource property (as opposed to a new version of an existing value). A + * successful response will be an [[CreateValueResponseV1]]. + * + * @param resourceIndex the index of the resource + * @param resourceIri the IRI of the resource to which the value should be added. + * @param propertyIri the IRI of the property that should receive the value. + * @param value the value to be added. + * @param comment an optional comment on the value. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the ID of this API request. + */ +case class CreateValueRequestV1( + resourceIndex: Int = 0, + resourceIri: IRI, + propertyIri: IRI, + value: UpdateValueV1, + comment: Option[String] = None, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV1 /** - * Represents a response to a [[CreateValueRequestV1]]. - * - * @param value the value that was added. - * @param comment an optional comment on the value. - * @param id the IRI of the value that was added. - * @param rights a code representing the requesting user's permissions on the value. - */ + * Represents a response to a [[CreateValueRequestV1]]. + * + * @param value the value that was added. + * @param comment an optional comment on the value. + * @param id the IRI of the value that was added. + * @param rights a code representing the requesting user's permissions on the value. + */ case class CreateValueResponseV1(value: ApiValueV1, comment: Option[String] = None, id: IRI, rights: Int) extends KnoraResponseV1 { def toJsValue: JsValue = ApiValueV1JsonProtocol.createValueResponseV1Format.write(this) } /** - * Represents a value that should have been created using the SPARQL returned in a - * [[GenerateSparqlToCreateMultipleValuesResponseV1]]. To verify that the value was in fact created, send a - * [[VerifyMultipleValueCreationRequestV1]]. - * - * @param newValueIri the IRI of the value that should have been created. - * @param value an [[UpdateValueV1]] representing the value that should have been created. - */ + * Represents a value that should have been created using the SPARQL returned in a + * [[GenerateSparqlToCreateMultipleValuesResponseV1]]. To verify that the value was in fact created, send a + * [[VerifyMultipleValueCreationRequestV1]]. + * + * @param newValueIri the IRI of the value that should have been created. + * @param value an [[UpdateValueV1]] representing the value that should have been created. + */ case class UnverifiedValueV1(newValueIri: IRI, value: UpdateValueV1) /** - * Requests verification that new values were created. - * - * @param resourceIri the IRI of the resource in which the values should have been created. - * @param unverifiedValues a [[Map]] of property IRIs to [[UnverifiedValueV1]] objects - * describing the values that should have been created for each property. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - */ -case class VerifyMultipleValueCreationRequestV1(resourceIri: IRI, - unverifiedValues: Map[IRI, Seq[UnverifiedValueV1]], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM) - extends ValuesResponderRequestV1 + * Requests verification that new values were created. + * + * @param resourceIri the IRI of the resource in which the values should have been created. + * @param unverifiedValues a [[Map]] of property IRIs to [[UnverifiedValueV1]] objects + * describing the values that should have been created for each property. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + */ +case class VerifyMultipleValueCreationRequestV1( + resourceIri: IRI, + unverifiedValues: Map[IRI, Seq[UnverifiedValueV1]], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM +) extends ValuesResponderRequestV1 /** - * In response to a [[VerifyMultipleValueCreationRequestV1]], indicates that all requested values were - * created successfully. - * - * @param verifiedValues information about the values that were created. - */ + * In response to a [[VerifyMultipleValueCreationRequestV1]], indicates that all requested values were + * created successfully. + * + * @param verifiedValues information about the values that were created. + */ case class VerifyMultipleValueCreationResponseV1(verifiedValues: Map[IRI, Seq[CreateValueResponseV1]]) /** - * A holder for an [[UpdateValueV1]] along with an optional comment. - * - * @param updateValueV1 the [[UpdateValueV1]]. - * @param comment an optional comment on the value. - */ + * A holder for an [[UpdateValueV1]] along with an optional comment. + * + * @param updateValueV1 the [[UpdateValueV1]]. + * @param comment an optional comment on the value. + */ case class CreateValueV1WithComment(updateValueV1: UpdateValueV1, comment: Option[String] = None) /** - * Requests SPARQL for creating multiple values in a new, empty resource. The resource ''must'' be a new, empty - * resource, i.e. it must have no values. This message is used only internally by Knora, and is not part of the Knora - * v1 API. All pre-update checks must already have been performed before this message is sent. Specifically, the - * sender must ensure that: - * - * - The requesting user has permission to add values to the resource. - * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed - * to point to it. - * - The resource class has a suitable cardinality for each submitted value. - * - All required values are provided. - * - * In the collection of values to be created, standoff links in text values are allowed to point either to the IRIs - * of resources that already exist in the triplestore, or to the client's IDs for resources that are being created - * as part of a bulk import. If client resource IDs are used in standoff links, `clientResourceIDsToResourceIris` - * must map those IDs to the real IRIs of the resources that are to be created. - * - * @param projectIri the project the values belong to. - * @param resourceIri the resource the values will be attached to. - * @param resourceClassIri the IRI of the resource's OWL class. - * @param defaultPropertyAccessPermissions the default object access permissions of each property attached to the resource class. - * @param values the values to be added, with optional comments. - * @param clientResourceIDsToResourceIris a map of client resource IDs (which may appear in standoff link tags - * in values) to the IRIs that will be used for those resources. - * @param creationDate an xsd:dateTimeStamp that will be attached to the values. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the user that is creating the values. - */ -case class GenerateSparqlToCreateMultipleValuesRequestV1(projectIri: IRI, - resourceIri: IRI, - resourceClassIri: IRI, - defaultPropertyAccessPermissions: Map[IRI, String], - values: Map[IRI, Seq[CreateValueV1WithComment]], - clientResourceIDsToResourceIris: Map[String, IRI], - creationDate: Instant, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV1 + * Requests SPARQL for creating multiple values in a new, empty resource. The resource ''must'' be a new, empty + * resource, i.e. it must have no values. This message is used only internally by Knora, and is not part of the Knora + * v1 API. All pre-update checks must already have been performed before this message is sent. Specifically, the + * sender must ensure that: + * + * - The requesting user has permission to add values to the resource. + * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed + * to point to it. + * - The resource class has a suitable cardinality for each submitted value. + * - All required values are provided. + * + * In the collection of values to be created, standoff links in text values are allowed to point either to the IRIs + * of resources that already exist in the triplestore, or to the client's IDs for resources that are being created + * as part of a bulk import. If client resource IDs are used in standoff links, `clientResourceIDsToResourceIris` + * must map those IDs to the real IRIs of the resources that are to be created. + * + * @param projectIri the project the values belong to. + * @param resourceIri the resource the values will be attached to. + * @param resourceClassIri the IRI of the resource's OWL class. + * @param defaultPropertyAccessPermissions the default object access permissions of each property attached to the resource class. + * @param values the values to be added, with optional comments. + * @param clientResourceIDsToResourceIris a map of client resource IDs (which may appear in standoff link tags + * in values) to the IRIs that will be used for those resources. + * @param creationDate an xsd:dateTimeStamp that will be attached to the values. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the user that is creating the values. + */ +case class GenerateSparqlToCreateMultipleValuesRequestV1( + projectIri: IRI, + resourceIri: IRI, + resourceClassIri: IRI, + defaultPropertyAccessPermissions: Map[IRI, String], + values: Map[IRI, Seq[CreateValueV1WithComment]], + clientResourceIDsToResourceIris: Map[String, IRI], + creationDate: Instant, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV1 /** - * Represents a response to a [[GenerateSparqlToCreateMultipleValuesRequestV1]], providing a string that can be included - * in the `INSERT DATA` clause of a SPARQL update operation to create the requested values. - * - * After executing the SPARQL update, the receiver can check whether the values were actually created by sending a - * [[VerifyMultipleValueCreationRequestV1]]. - * - * @param insertSparql a string containing statements that must be inserted into the INSERT clause of the SPARQL - * update that will create the values. - * @param unverifiedValues a map of property IRIs to [[UnverifiedValueV1]] objects describing - * the values that should have been created. - */ -case class GenerateSparqlToCreateMultipleValuesResponseV1(insertSparql: String, - unverifiedValues: Map[IRI, Seq[UnverifiedValueV1]]) + * Represents a response to a [[GenerateSparqlToCreateMultipleValuesRequestV1]], providing a string that can be included + * in the `INSERT DATA` clause of a SPARQL update operation to create the requested values. + * + * After executing the SPARQL update, the receiver can check whether the values were actually created by sending a + * [[VerifyMultipleValueCreationRequestV1]]. + * + * @param insertSparql a string containing statements that must be inserted into the INSERT clause of the SPARQL + * update that will create the values. + * @param unverifiedValues a map of property IRIs to [[UnverifiedValueV1]] objects describing + * the values that should have been created. + */ +case class GenerateSparqlToCreateMultipleValuesResponseV1( + insertSparql: String, + unverifiedValues: Map[IRI, Seq[UnverifiedValueV1]] +) /** - * Represents a request to change the value of a property (by updating its version history). A successful response will - * be a [[ChangeValueResponseV1]]. - * - * @param valueIri the IRI of the current value. - * @param value the new value, or [[None]] if only the value's comment is being changed. - * @param comment an optional comment on the value. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the ID of this API request. - */ -case class ChangeValueRequestV1(valueIri: IRI, - value: UpdateValueV1, - comment: Option[String] = None, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV1 + * Represents a request to change the value of a property (by updating its version history). A successful response will + * be a [[ChangeValueResponseV1]]. + * + * @param valueIri the IRI of the current value. + * @param value the new value, or [[None]] if only the value's comment is being changed. + * @param comment an optional comment on the value. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the ID of this API request. + */ +case class ChangeValueRequestV1( + valueIri: IRI, + value: UpdateValueV1, + comment: Option[String] = None, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV1 /** - * Represents a request to change the comment on a value. A successful response will be a [[ChangeValueResponseV1]]. - * - * @param valueIri the IRI of the current value. - * @param comment the comment to be added to the new version of the value. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the ID of this API request. - */ -case class ChangeCommentRequestV1(valueIri: IRI, - comment: Option[String], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV1 + * Represents a request to change the comment on a value. A successful response will be a [[ChangeValueResponseV1]]. + * + * @param valueIri the IRI of the current value. + * @param comment the comment to be added to the new version of the value. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the ID of this API request. + */ +case class ChangeCommentRequestV1( + valueIri: IRI, + comment: Option[String], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV1 /** - * Represents a response to an [[ChangeValueRequestV1]]. - * - * @param value the value that was added. - * @param comment an optional comment on the value. - * @param id the IRI of the value that was added. - */ + * Represents a response to an [[ChangeValueRequestV1]]. + * + * @param value the value that was added. + * @param comment an optional comment on the value. + * @param id the IRI of the value that was added. + */ case class ChangeValueResponseV1(value: ApiValueV1, comment: Option[String] = None, id: IRI, rights: Int) extends KnoraResponseV1 { def toJsValue: JsValue = ApiValueV1JsonProtocol.changeValueResponseV1Format.write(this) } /** - * Represents a request to mark a value as deleted. - * - * @param valueIri the IRI of the value to be marked as deleted. - * @param deleteComment an optional comment explaining why the value is being deleted. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the ID of this API request. - */ -case class DeleteValueRequestV1(valueIri: IRI, - deleteComment: Option[String] = None, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV1 + * Represents a request to mark a value as deleted. + * + * @param valueIri the IRI of the value to be marked as deleted. + * @param deleteComment an optional comment explaining why the value is being deleted. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the ID of this API request. + */ +case class DeleteValueRequestV1( + valueIri: IRI, + deleteComment: Option[String] = None, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV1 /** - * Represents a response to a [[DeleteValueRequestV1]]. - * - * @param id the IRI of the value that was marked as deleted. If this was a `LinkValue`, a new version of it - * will have been created, and `id` will the IRI of that new version. Otherwise, `id` will be the IRI - * submitted in the [[DeleteValueRequestV1]]. For an explanation of this behaviour, see the chapter - * ''Triplestore Updates'' in the Knora API server design documentation. - */ + * Represents a response to a [[DeleteValueRequestV1]]. + * + * @param id the IRI of the value that was marked as deleted. If this was a `LinkValue`, a new version of it + * will have been created, and `id` will the IRI of that new version. Otherwise, `id` will be the IRI + * submitted in the [[DeleteValueRequestV1]]. For an explanation of this behaviour, see the chapter + * ''Triplestore Updates'' in the Knora API server design documentation. + */ case class DeleteValueResponseV1(id: IRI) extends KnoraResponseV1 { def toJsValue: JsValue = ApiValueV1JsonProtocol.deleteValueResponseV1Format.write(this) } /** - * Represents a request to change (update) the file value(s) of a given resource. - * In case of an image, two file valueshave to be changed: thumbnail and full quality. - * - * @param resourceIri the resource whose files value(s) should be changed. - * @param file a file that has been uploaded to Sipi's temporary storage. - * @param featureFactoryConfig the feature factory configuration. - */ -case class ChangeFileValueRequestV1(resourceIri: IRI, - file: FileValueV1, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM) - extends ValuesResponderRequestV1 + * Represents a request to change (update) the file value(s) of a given resource. + * In case of an image, two file valueshave to be changed: thumbnail and full quality. + * + * @param resourceIri the resource whose files value(s) should be changed. + * @param file a file that has been uploaded to Sipi's temporary storage. + * @param featureFactoryConfig the feature factory configuration. + */ +case class ChangeFileValueRequestV1( + resourceIri: IRI, + file: FileValueV1, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM +) extends ValuesResponderRequestV1 /** - * Represents a response to a [[ChangeFileValueRequestV1]]. - * Possibly, two file values have been changed (thumb and full quality). - * - * @param locations the updated file value(s). - */ + * Represents a response to a [[ChangeFileValueRequestV1]]. + * Possibly, two file values have been changed (thumb and full quality). + * + * @param locations the updated file value(s). + */ case class ChangeFileValueResponseV1(locations: Vector[LocationV1], projectADM: ProjectADM) extends KnoraResponseV1 with UpdateResultInProject { @@ -526,65 +544,67 @@ case class ChangeFileValueResponseV1(locations: Vector[LocationV1], projectADM: // Components of messages /** - * The value of a Knora property, either as represented internally by Knora or as returned to clients in - * Knora API v1. - */ + * The value of a Knora property, either as represented internally by Knora or as returned to clients in + * Knora API v1. + */ sealed trait ValueV1 { /** - * The IRI of the Knora value type corresponding to the type of this `ValueV1`. - */ + * The IRI of the Knora value type corresponding to the type of this `ValueV1`. + */ def valueTypeIri: IRI } /** - * The value of a Knora property as represented to clients in Knora API v1. An [[ApiValueV1]] can be serialised as - * JSON for use in the API. - */ + * The value of a Knora property as represented to clients in Knora API v1. An [[ApiValueV1]] can be serialised as + * JSON for use in the API. + */ sealed trait ApiValueV1 extends ValueV1 with Jsonable /** - * The value of a Knora property as represented in an update request. - */ + * The value of a Knora property as represented in an update request. + */ sealed trait UpdateValueV1 extends ValueV1 { /** - * Returns `true` if creating this [[UpdateValueV1]] as a new value would duplicate the specified other value. - * This means that if resource `R` has property `P` with value `V1`, and `V1` is a duplicate of `V2`, the API server - * should not add another instance of property `P` with value `V2`. It does not necessarily mean that `V1 == V2`. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ + * Returns `true` if creating this [[UpdateValueV1]] as a new value would duplicate the specified other value. + * This means that if resource `R` has property `P` with value `V1`, and `V1` is a duplicate of `V2`, the API server + * should not add another instance of property `P` with value `V2`. It does not necessarily mean that `V1 == V2`. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ def isDuplicateOfOtherValue(other: ApiValueV1): Boolean /** - * Returns `true` if this [[UpdateValueV1]] would be redundant as a new version of an existing value. This means - * that if resource `R` has property `P` with value `V1`, and `V2` is redundant given `V1`, we should not `V2` - * as a new version of `V1`. It does not necessarily mean that `V1 == V2`. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ + * Returns `true` if this [[UpdateValueV1]] would be redundant as a new version of an existing value. This means + * that if resource `R` has property `P` with value `V1`, and `V2` is redundant given `V1`, we should not `V2` + * as a new version of `V1`. It does not necessarily mean that `V1 == V2`. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ def isRedundant(currentVersion: ApiValueV1): Boolean } /** - * Represents a Knora API v1 property value object and some associated information. - * - * @param valueObjectIri the IRI of the value object. - * @param valueV1 a [[ApiValueV1]] containing the object's literal value. - */ -case class ValueObjectV1(valueObjectIri: IRI, - valueV1: ApiValueV1, - valuePermission: Option[Int] = None, - comment: Option[String] = None, - order: Int = 0) + * Represents a Knora API v1 property value object and some associated information. + * + * @param valueObjectIri the IRI of the value object. + * @param valueV1 a [[ApiValueV1]] containing the object's literal value. + */ +case class ValueObjectV1( + valueObjectIri: IRI, + valueV1: ApiValueV1, + valuePermission: Option[Int] = None, + comment: Option[String] = None, + order: Int = 0 +) /** - * An enumeration of the types of calendars Knora supports. Note: do not use the `withName` method to get instances - * of the values of this enumeration; use `lookup` instead, because it reports errors better. - */ + * An enumeration of the types of calendars Knora supports. Note: do not use the `withName` method to get instances + * of the values of this enumeration; use `lookup` instead, because it reports errors better. + */ object KnoraCalendarV1 extends Enumeration { val JULIAN: Value = Value(0, "JULIAN") val GREGORIAN: Value = Value(1, "GREGORIAN") @@ -594,24 +614,23 @@ object KnoraCalendarV1 extends Enumeration { val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an - * [[InconsistentRepositoryDataException]]. - * - * @param name the name of the value. - * @return the requested value. - */ - def lookup(name: String): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an + * [[InconsistentRepositoryDataException]]. + * + * @param name the name of the value. + * @return the requested value. + */ + def lookup(name: String): Value = valueMap.get(name) match { case Some(value) => value case None => throw InconsistentRepositoryDataException(s"Calendar type not supported: $name") } - } } /** - * An enumeration of the types of calendar precisions Knora supports. Note: do not use the `withName` method to get instances - * of the values of this enumeration; use `lookup` instead, because it reports errors better. - */ + * An enumeration of the types of calendar precisions Knora supports. Note: do not use the `withName` method to get instances + * of the values of this enumeration; use `lookup` instead, because it reports errors better. + */ object KnoraPrecisionV1 extends Enumeration { val DAY: Value = Value(0, "DAY") val MONTH: Value = Value(1, "MONTH") @@ -620,33 +639,33 @@ object KnoraPrecisionV1 extends Enumeration { val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an - * [[InconsistentRepositoryDataException]]. - * - * @param name the name of the value. - * @return the requested value. - */ - def lookup(name: String): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an + * [[InconsistentRepositoryDataException]]. + * + * @param name the name of the value. + * @return the requested value. + */ + def lookup(name: String): Value = valueMap.get(name) match { case Some(value) => value case None => throw InconsistentRepositoryDataException(s"Calendar precision not supported: $name") } - } } /** - * - * Represents a [[StandoffTagV2]] for a standoff tag of a certain type (standoff tag class) that is about to be created in the triplestore. - * - * @param standoffNode the standoff node to be created. - * @param standoffTagInstanceIri the standoff node's IRI. - * @param startParentIri the IRI of the parent of the start tag. - * @param endParentIri the IRI of the parent of the end tag, if any. - */ -case class CreateStandoffTagV1InTriplestore(standoffNode: StandoffTagV2, - standoffTagInstanceIri: IRI, - startParentIri: Option[IRI] = None, - endParentIri: Option[IRI] = None) + * Represents a [[StandoffTagV2]] for a standoff tag of a certain type (standoff tag class) that is about to be created in the triplestore. + * + * @param standoffNode the standoff node to be created. + * @param standoffTagInstanceIri the standoff node's IRI. + * @param startParentIri the IRI of the parent of the start tag. + * @param endParentIri the IRI of the parent of the end tag, if any. + */ +case class CreateStandoffTagV1InTriplestore( + standoffNode: StandoffTagV2, + standoffTagInstanceIri: IRI, + startParentIri: Option[IRI] = None, + endParentIri: Option[IRI] = None +) sealed trait TextValueV1 { @@ -657,21 +676,22 @@ sealed trait TextValueV1 { } /** - * Represents a text value with standoff markup. - * - * @param utf8str text in mere utf8 representation (including newlines and carriage returns). - * @param language the language of the text, if known. - * @param standoff attributes of the text in standoff format. For each attribute, several ranges may be given (a list of [[StandoffTagV2]]). - * @param resource_reference referred Knora resources. - * @param mapping the mapping used to create standoff from another format. - */ -case class TextValueWithStandoffV1(utf8str: String, - language: Option[String] = None, - standoff: Seq[StandoffTagV2], - resource_reference: Set[IRI] = Set.empty[IRI], - mappingIri: IRI, - mapping: MappingXMLtoStandoff) - extends TextValueV1 + * Represents a text value with standoff markup. + * + * @param utf8str text in mere utf8 representation (including newlines and carriage returns). + * @param language the language of the text, if known. + * @param standoff attributes of the text in standoff format. For each attribute, several ranges may be given (a list of [[StandoffTagV2]]). + * @param resource_reference referred Knora resources. + * @param mapping the mapping used to create standoff from another format. + */ +case class TextValueWithStandoffV1( + utf8str: String, + language: Option[String] = None, + standoff: Seq[StandoffTagV2], + resource_reference: Set[IRI] = Set.empty[IRI], + mappingIri: IRI, + mapping: MappingXMLtoStandoff +) extends TextValueV1 with UpdateValueV1 with ApiValueV1 { @@ -708,11 +728,11 @@ case class TextValueWithStandoffV1(utf8str: String, } /** - * A convenience method that creates an IRI for each [[StandoffTagV2]] and resolves internal references to standoff node Iris. - * - * @return a list of [[CreateStandoffTagV1InTriplestore]] each representing a [[StandoffTagV2]] object - * along with is standoff tag class and IRI that is going to identify it in the triplestore. - */ + * A convenience method that creates an IRI for each [[StandoffTagV2]] and resolves internal references to standoff node Iris. + * + * @return a list of [[CreateStandoffTagV1InTriplestore]] each representing a [[StandoffTagV2]] object + * along with is standoff tag class and IRI that is going to identify it in the triplestore. + */ def prepareForSparqlInsert(valueIri: IRI): Seq[CreateStandoffTagV1InTriplestore] = { // create an IRI for each standoff tag @@ -723,22 +743,21 @@ case class TextValueWithStandoffV1(utf8str: String, standoffNode = standoffNode, standoffTagInstanceIri = stringFormatter.makeRandomStandoffTagIri( valueIri = valueIri, - startIndex = standoffNode.startIndex) // generate IRI for new standoff node + startIndex = standoffNode.startIndex + ) // generate IRI for new standoff node ) } // collect all the standoff tags that contain XML ids and // map the XML ids to standoff node Iris - val iDsToStandoffNodeIris: Map[IRI, IRI] = standoffTagsWithOriginalXMLIDs - .filter { standoffTag: CreateStandoffTagV1InTriplestore => + val iDsToStandoffNodeIris: Map[IRI, IRI] = standoffTagsWithOriginalXMLIDs.filter { + standoffTag: CreateStandoffTagV1InTriplestore => // filter those tags out that have an XML id standoffTag.standoffNode.originalXMLID.isDefined - } - .map { standoffTagWithID: CreateStandoffTagV1InTriplestore => - // return the XML id as a key and the standoff IRI as the value - standoffTagWithID.standoffNode.originalXMLID.get -> standoffTagWithID.standoffTagInstanceIri - } - .toMap + }.map { standoffTagWithID: CreateStandoffTagV1InTriplestore => + // return the XML id as a key and the standoff IRI as the value + standoffTagWithID.standoffNode.originalXMLID.get -> standoffTagWithID.standoffTagInstanceIri + }.toMap // Map the start index of each tag to its IRI, so we can resolve references to parent tags as references to // tag IRIs. We only care about start indexes here, because only hierarchical tags can be parents, and @@ -767,8 +786,12 @@ case class TextValueWithStandoffV1(utf8str: String, // return standoff tag with updated attributes standoffTag.copy( standoffNode = standoffTag.standoffNode.copy(attributes = attributesWithStandoffNodeIriReferences), - startParentIri = startParentIndex.map(parentIndex => startIndexesToStandoffNodeIris(parentIndex)), // If there's a start parent index, get its IRI, otherwise None - endParentIri = endParentIndex.map(parentIndex => startIndexesToStandoffNodeIris(parentIndex)) // If there's an end parent index, get its IRI, otherwise None + startParentIri = startParentIndex.map(parentIndex => + startIndexesToStandoffNodeIris(parentIndex) + ), // If there's a start parent index, get its IRI, otherwise None + endParentIri = endParentIndex.map(parentIndex => + startIndexesToStandoffNodeIris(parentIndex) + ) // If there's an end parent index, get its IRI, otherwise None ) } @@ -776,15 +799,14 @@ case class TextValueWithStandoffV1(utf8str: String, } /** - * Returns `true` if the specified object is a [[TextValueV1]] and has the same `utf8str` as this one. We - * assume that it doesn't make sense for a resource to have two different text values associated with the - * same property, containing the same text but different markup. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { - + * Returns `true` if the specified object is a [[TextValueV1]] and has the same `utf8str` as this one. We + * assume that it doesn't make sense for a resource to have two different text values associated with the + * same property, containing the same text but different markup. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case otherText: TextValueV1 => // unescape utf8str since it contains escaped sequences while the string returned by the triplestore does not @@ -792,25 +814,23 @@ case class TextValueWithStandoffV1(utf8str: String, case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } override def toString: String = utf8str /** - * It's OK to add a new version of a text value as long as something has been changed in it, even if it's only the markup. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { - + * It's OK to add a new version of a text value as long as something has been changed in it, even if it's only the markup. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case _: TextValueSimpleV1 => false case textValueWithStandoffV1: TextValueWithStandoffV1 => // compare utf8str (unescape utf8str since it contains escaped sequences while the string returned by the triplestore does not) - val utf8strIdentical - : Boolean = stringFormatter.fromSparqlEncodedString(utf8str) == textValueWithStandoffV1.utf8str + val utf8strIdentical: Boolean = + stringFormatter.fromSparqlEncodedString(utf8str) == textValueWithStandoffV1.utf8str // Compare standoff tags. val thisComparableStandoff = StandoffTagUtilV2.makeComparableStandoffCollection(standoff) @@ -823,15 +843,14 @@ case class TextValueWithStandoffV1(utf8str: String, case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a text value without standoff markup. - * - * @param utf8str the text. - * @param language the language of the text, if known. - */ + * Represents a text value without standoff markup. + * + * @param utf8str the text. + * @param language the language of the text, if known. + */ case class TextValueSimpleV1(utf8str: String, language: Option[String] = None) extends TextValueV1 with UpdateValueV1 @@ -839,7 +858,7 @@ case class TextValueSimpleV1(utf8str: String, language: Option[String] = None) def valueTypeIri: IRI = OntologyConstants.KnoraBase.TextValue - def toJsValue: JsValue = { + def toJsValue: JsValue = language match { case Some(lang) => JsObject( @@ -850,58 +869,56 @@ case class TextValueSimpleV1(utf8str: String, language: Option[String] = None) case None => JsObject("utf8str" -> JsString(utf8str)) } - } /** - * Returns `true` if the specified object is a [[TextValueV1]] and has the same `utf8str` as this one. We - * assume that it doesn't make sense for a resource to have two different text values associated with the - * same property, containing the same text but different markup. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Returns `true` if the specified object is a [[TextValueV1]] and has the same `utf8str` as this one. We + * assume that it doesn't make sense for a resource to have two different text values associated with the + * same property, containing the same text but different markup. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case otherText: TextValueV1 => otherText.utf8str == utf8str case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } override def toString: String = utf8str /** - * It's OK to add a new version of a text value as long as something has been changed in it, even if it's only the markup. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * It's OK to add a new version of a text value as long as something has been changed in it, even if it's only the markup. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case textValueSimpleV1: TextValueSimpleV1 => textValueSimpleV1 == this case _: TextValueWithStandoffV1 => false case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a direct link from one resource to another. - * - * @param targetResourceIri the IRI of the resource that the link points to. - * @param valueLabel the `rdfs:label` of the resource referred to. - * @param valueResourceClass the IRI of the OWL class of the resource that the link points to. - * @param valueResourceClassLabel the label of the OWL class of the resource that the link points to. - * @param valueResourceClassIcon the icon of the OWL class of the resource that the link points to. - */ -case class LinkV1(targetResourceIri: IRI, - valueLabel: Option[String] = None, - valueResourceClass: Option[IRI] = None, - valueResourceClassLabel: Option[String] = None, - valueResourceClassIcon: Option[String] = None) - extends ApiValueV1 { + * Represents a direct link from one resource to another. + * + * @param targetResourceIri the IRI of the resource that the link points to. + * @param valueLabel the `rdfs:label` of the resource referred to. + * @param valueResourceClass the IRI of the OWL class of the resource that the link points to. + * @param valueResourceClassLabel the label of the OWL class of the resource that the link points to. + * @param valueResourceClassIcon the icon of the OWL class of the resource that the link points to. + */ +case class LinkV1( + targetResourceIri: IRI, + valueLabel: Option[String] = None, + valueResourceClass: Option[IRI] = None, + valueResourceClassLabel: Option[String] = None, + valueResourceClassIcon: Option[String] = None +) extends ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.LinkValue @@ -911,15 +928,15 @@ case class LinkV1(targetResourceIri: IRI, } /** - * Represents a `knora-base:LinkValue`, i.e. a reification of a link between two resources. - * - * @param subjectIri the IRI of the resource that is the source of the link. - * @param predicateIri the IRI of the property that links the two resources. - * @param objectIri the IRI of the resource that is the target of the link. - * @param referenceCount the reference count of the `LinkValue`. If the link property is `knora-base:hasStandoffLinkTo`, - * the reference count can be any integer greater than or equal to 0. Otherwise, the reference - * count can only be 0 or 1. - */ + * Represents a `knora-base:LinkValue`, i.e. a reification of a link between two resources. + * + * @param subjectIri the IRI of the resource that is the source of the link. + * @param predicateIri the IRI of the property that links the two resources. + * @param objectIri the IRI of the resource that is the target of the link. + * @param referenceCount the reference count of the `LinkValue`. If the link property is `knora-base:hasStandoffLinkTo`, + * the reference count can be any integer greater than or equal to 0. Otherwise, the reference + * count can only be 0 or 1. + */ case class LinkValueV1(subjectIri: IRI, predicateIri: IRI, objectIri: IRI, referenceCount: Int) extends ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.LinkValue @@ -927,49 +944,47 @@ case class LinkValueV1(subjectIri: IRI, predicateIri: IRI, objectIri: IRI, refer } /** - * Represents a request to update a link. - * - * @param targetResourceIri the IRI of the resource that the link should point to. - * @param targetExists `true` if the link target already exists, `false` if it is going to be created in the - * same transaction. - */ + * Represents a request to update a link. + * + * @param targetResourceIri the IRI of the resource that the link should point to. + * @param targetExists `true` if the link target already exists, `false` if it is going to be created in the + * same transaction. + */ case class LinkUpdateV1(targetResourceIri: IRI, targetExists: Boolean = true) extends UpdateValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.LinkValue /** - * It doesn't make sense to add a link to a resource when we already have a link to the same resource. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { - + * It doesn't make sense to add a link to a resource when we already have a link to the same resource. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case linkV1: LinkV1 => targetResourceIri == linkV1.targetResourceIri case linkValueV1: LinkValueV1 => targetResourceIri == linkValueV1.objectIri case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } override def toString: String = targetResourceIri /** - * A link isn't really changed if the new version points to the same resource as the old version. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ + * A link isn't really changed if the new version points to the same resource as the old version. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ override def isRedundant(currentVersion: ApiValueV1): Boolean = isDuplicateOfOtherValue(currentVersion) } /** - * Represents a request to create a link to a resource that hasn't been created yet, and is known only - * by the ID that the client has provided for it. Instances of this class will be replaced by instances - * of [[LinkUpdateV1]] during the preparation for the update. - * - * @param clientIDForTargetResource the client's ID for the target resource. - */ + * Represents a request to create a link to a resource that hasn't been created yet, and is known only + * by the ID that the client has provided for it. Instances of this class will be replaced by instances + * of [[LinkUpdateV1]] during the preparation for the update. + * + * @param clientIDForTargetResource the client's ID for the target resource. + */ case class LinkToClientIDUpdateV1(clientIDForTargetResource: String) extends UpdateValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.LinkValue @@ -981,58 +996,54 @@ case class LinkToClientIDUpdateV1(clientIDForTargetResource: String) extends Upd } /** - * Represents the IRI of a Knora hierarchical list. - * - * @param hierarchicalListIri the IRI of the hierarchical list. - */ + * Represents the IRI of a Knora hierarchical list. + * + * @param hierarchicalListIri the IRI of the hierarchical list. + */ case class HierarchicalListValueV1(hierarchicalListIri: IRI) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.ListValue def toJsValue: JsValue = JsString(hierarchicalListIri) - override def toString: String = { + override def toString: String = // TODO: implement this correctly // the string representation is the rdfs:label of the list node - hierarchicalListIri - } /** - * Checks if a new list value would duplicate an existing list value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new list value would duplicate an existing list value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case listValueV1: HierarchicalListValueV1 => listValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a list value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a list value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case listValueV1: HierarchicalListValueV1 => listValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents an integer value. - * - * @param ival the integer value. - */ + * Represents an integer value. + * + * @param ival the integer value. + */ case class IntegerValueV1(ival: Int) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.IntValue @@ -1042,39 +1053,37 @@ case class IntegerValueV1(ival: Int) extends UpdateValueV1 with ApiValueV1 { override def toString: String = ival.toString /** - * Checks if a new integer value would duplicate an existing integer value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new integer value would duplicate an existing integer value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case integerValueV1: IntegerValueV1 => integerValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of an integer value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of an integer value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case integerValueV1: IntegerValueV1 => integerValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a boolean value. - * - * @param bval the boolean value. - */ + * Represents a boolean value. + * + * @param bval the boolean value. + */ case class BooleanValueV1(bval: Boolean) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.BooleanValue @@ -1084,34 +1093,33 @@ case class BooleanValueV1(bval: Boolean) extends UpdateValueV1 with ApiValueV1 { override def toString: String = bval.toString /** - * Checks if a new boolean value would duplicate an existing boolean value. Always returns `true`, because it - * does not make sense to have two instances of the same boolean property. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ + * Checks if a new boolean value would duplicate an existing boolean value. Always returns `true`, because it + * does not make sense to have two instances of the same boolean property. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = true /** - * Checks if a new version of an boolean value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of an boolean value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case booleanValueV1: BooleanValueV1 => booleanValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a URI value. - * - * @param uri the URI value. - */ + * Represents a URI value. + * + * @param uri the URI value. + */ case class UriValueV1(uri: String) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.UriValue @@ -1121,39 +1129,37 @@ case class UriValueV1(uri: String) extends UpdateValueV1 with ApiValueV1 { override def toString: String = uri /** - * Checks if a new URI value would duplicate an existing URI value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new URI value would duplicate an existing URI value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case uriValueV1: UriValueV1 => uriValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of an integer value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of an integer value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case uriValueV1: UriValueV1 => uriValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents an arbitrary-precision decimal value. - * - * @param dval the decimal value. - */ + * Represents an arbitrary-precision decimal value. + * + * @param dval the decimal value. + */ case class DecimalValueV1(dval: BigDecimal) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.DecimalValue @@ -1162,41 +1168,39 @@ case class DecimalValueV1(dval: BigDecimal) extends UpdateValueV1 with ApiValueV override def toString: String = dval.toString /** - * Checks if a new decimal value would duplicate an existing decimal value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new decimal value would duplicate an existing decimal value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case decimalValueV1: DecimalValueV1 => decimalValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a decimal value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a decimal value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case decimalValueV1: DecimalValueV1 => decimalValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a time interval value. - * - * @param timeval1 an `xsd:decimal` representing the beginning of the interval. - * @param timeval2 an `xsd:decimal` representing the end of the interval. - */ + * Represents a time interval value. + * + * @param timeval1 an `xsd:decimal` representing the beginning of the interval. + * @param timeval2 an `xsd:decimal` representing the end of the interval. + */ case class IntervalValueV1(timeval1: BigDecimal, timeval2: BigDecimal) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.IntervalValue @@ -1209,39 +1213,37 @@ case class IntervalValueV1(timeval1: BigDecimal, timeval2: BigDecimal) extends U override def toString: String = s"$timeval1 - $timeval2" /** - * Checks if a new interval value would duplicate an existing interval value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new interval value would duplicate an existing interval value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case intervalValueV1: IntervalValueV1 => intervalValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of this interval value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of this interval value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case intervalValueV1: IntervalValueV1 => intervalValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a timestamp value. - * - * @param timeStamp an `xsd:dateTimeStamp`. - */ + * Represents a timestamp value. + * + * @param timeStamp an `xsd:dateTimeStamp`. + */ case class TimeValueV1(timeStamp: Instant) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.TimeValue @@ -1251,59 +1253,57 @@ case class TimeValueV1(timeStamp: Instant) extends UpdateValueV1 with ApiValueV1 override def toString: String = s"$timeStamp" /** - * Checks if a new interval value would duplicate an existing interval value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new interval value would duplicate an existing interval value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case timeValueV1: TimeValueV1 => timeValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of this interval value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of this interval value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case timeValueV1: TimeValueV1 => timeValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a date value as a period bounded by Julian Day Numbers. Knora stores dates internally in this format. - * - * @param dateval1 the beginning of the date (a Julian day number). - * @param dateval2 the end of the date (a Julian day number). - * @param calendar the preferred calendar for representing the date. - * @param dateprecision1 the precision of the beginning of the date. - * @param dateprecision2 the precision of the end of the date. - */ -case class JulianDayNumberValueV1(dateval1: Int, - dateval2: Int, - calendar: KnoraCalendarV1.Value, - dateprecision1: KnoraPrecisionV1.Value, - dateprecision2: KnoraPrecisionV1.Value) - extends UpdateValueV1 { + * Represents a date value as a period bounded by Julian Day Numbers. Knora stores dates internally in this format. + * + * @param dateval1 the beginning of the date (a Julian day number). + * @param dateval2 the end of the date (a Julian day number). + * @param calendar the preferred calendar for representing the date. + * @param dateprecision1 the precision of the beginning of the date. + * @param dateprecision2 the precision of the end of the date. + */ +case class JulianDayNumberValueV1( + dateval1: Int, + dateval2: Int, + calendar: KnoraCalendarV1.Value, + dateprecision1: KnoraPrecisionV1.Value, + dateprecision2: KnoraPrecisionV1.Value +) extends UpdateValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.DateValue - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case dateValueV1: DateValueV1 => DateUtilV1.julianDayNumberValueV1ToDateValueV1(this) == other case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } override def isRedundant(currentVersion: ApiValueV1): Boolean = isDuplicateOfOtherValue(currentVersion) @@ -1326,29 +1326,28 @@ case class JulianDayNumberValueV1(dateval1: Int, } /** - * Represents a date value as represented in Knora API v1. - * - * A [[DateValueV1]] can represent either single date or a period with start and end dates (`dateval1` and `dateval2`). - * If it represents a single date, `dateval1` will have a value but `dateval2` will be `None`. Both `dateval1` and `dateval2` - * can indicate degrees of uncertainty, using the following formats: - * - * - `YYYY-MM-DD` specifies a particular day, with no uncertainty. - * - `YYYY-MM` indicates that the year and the month are known, but that the day of the month is uncertain. In effect, this specifies a range of possible dates, from the first day of the month to the last day of the month. - * - `YYYY` indicates that only the year is known. In effect, this specifies a range of possible dates, from the first day of the year to the last day of the year. - * - * The year and month values refer to years and months in the calendar specified by `calendar`. - * - * @param dateval1 the start date of the period. - * @param dateval2 the end date of the period, if any. - * @param calendar the type of calendar used in the date. - */ + * Represents a date value as represented in Knora API v1. + * + * A [[DateValueV1]] can represent either single date or a period with start and end dates (`dateval1` and `dateval2`). + * If it represents a single date, `dateval1` will have a value but `dateval2` will be `None`. Both `dateval1` and `dateval2` + * can indicate degrees of uncertainty, using the following formats: + * + * - `YYYY-MM-DD` specifies a particular day, with no uncertainty. + * - `YYYY-MM` indicates that the year and the month are known, but that the day of the month is uncertain. In effect, this specifies a range of possible dates, from the first day of the month to the last day of the month. + * - `YYYY` indicates that only the year is known. In effect, this specifies a range of possible dates, from the first day of the year to the last day of the year. + * + * The year and month values refer to years and months in the calendar specified by `calendar`. + * + * @param dateval1 the start date of the period. + * @param dateval2 the end date of the period, if any. + * @param calendar the type of calendar used in the date. + */ case class DateValueV1(dateval1: String, dateval2: String, era1: String, era2: String, calendar: KnoraCalendarV1.Value) extends ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.DateValue - override def toString: String = { - + override def toString: String = // if date1 and date2 are identical, it's not a period. if (dateval1 == dateval2) { // one exact day @@ -1358,16 +1357,14 @@ case class DateValueV1(dateval1: String, dateval2: String, era1: String, era2: S dateval1 + " " + era1 + " - " + dateval2 + " " + era2 } - } - def toJsValue: JsValue = ApiValueV1JsonProtocol.dateValueV1Format.write(this) } /** - * Represents an RGB color value. - * - * @param color a hexadecimal string containing the RGB color value. - */ + * Represents an RGB color value. + * + * @param color a hexadecimal string containing the RGB color value. + */ case class ColorValueV1(color: String) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.ColorValue @@ -1377,39 +1374,37 @@ case class ColorValueV1(color: String) extends UpdateValueV1 with ApiValueV1 { override def toString: String = color /** - * Checks if a new color value would equal an existing color value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new color value would equal an existing color value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case colorValueV1: ColorValueV1 => colorValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of this color value would equal the existing version of this color value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of this color value would equal the existing version of this color value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case colorValueV1: ColorValueV1 => colorValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a geometric shape. - * - * @param geom A string containing JSON that describes the shape. TODO: don't use JSON for this (issue 169). - */ + * Represents a geometric shape. + * + * @param geom A string containing JSON that describes the shape. TODO: don't use JSON for this (issue 169). + */ case class GeomValueV1(geom: String) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.GeomValue @@ -1419,39 +1414,37 @@ case class GeomValueV1(geom: String) extends UpdateValueV1 with ApiValueV1 { override def toString: String = geom /** - * Checks if a new geom value would duplicate an existing geom value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new geom value would duplicate an existing geom value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case geomValueV1: GeomValueV1 => geomValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a geom value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a geom value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case geomValueV1: GeomValueV1 => geomValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * Represents a [[http://www.geonames.org/ GeoNames]] code. - * - * @param geonameCode a string representing the GeoNames code. - */ + * Represents a [[http://www.geonames.org/ GeoNames]] code. + * + * @param geonameCode a string representing the GeoNames code. + */ case class GeonameValueV1(geonameCode: String) extends UpdateValueV1 with ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.GeonameValue @@ -1461,37 +1454,35 @@ case class GeonameValueV1(geonameCode: String) extends UpdateValueV1 with ApiVal override def toString: String = geonameCode /** - * Checks if a new GeoName value would duplicate an existing GeoName value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new GeoName value would duplicate an existing GeoName value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case geonameValueV1: GeonameValueV1 => geonameValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a GeoName value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a GeoName value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case geonameValueV1: GeonameValueV1 => geonameValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } } /** - * The data describing a binary file of any type that can be sent to Knora. - */ + * The data describing a binary file of any type that can be sent to Knora. + */ sealed trait FileValueV1 extends UpdateValueV1 with ApiValueV1 { val internalMimeType: String val internalFilename: String @@ -1503,22 +1494,23 @@ sealed trait FileValueV1 extends UpdateValueV1 with ApiValueV1 { } /** - * A representation of a digital image. - * - * @param internalMimeType the MIME-type of the internal representation. - * @param internalFilename the internal filename of the object. - * @param originalFilename the original filename of the object at the time of the import. - * @param dimX the X dimension of the object. - * @param dimY the Y dimension of the object. - */ -case class StillImageFileValueV1(internalMimeType: String, - internalFilename: String, - originalFilename: Option[String] = None, - originalMimeType: Option[String] = None, - projectShortcode: String, - dimX: Int, - dimY: Int) - extends FileValueV1 { + * A representation of a digital image. + * + * @param internalMimeType the MIME-type of the internal representation. + * @param internalFilename the internal filename of the object. + * @param originalFilename the original filename of the object at the time of the import. + * @param dimX the X dimension of the object. + * @param dimY the Y dimension of the object. + */ +case class StillImageFileValueV1( + internalMimeType: String, + internalFilename: String, + originalFilename: Option[String] = None, + originalMimeType: Option[String] = None, + projectShortcode: String, + dimX: Int, + dimY: Int +) extends FileValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.StillImageFileValue @@ -1527,34 +1519,32 @@ case class StillImageFileValueV1(internalMimeType: String, override def toString: String = internalFilename /** - * Checks if a new still image file value would duplicate an existing still image file value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new still image file value would duplicate an existing still image file value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case stillImageFileValueV1: StillImageFileValueV1 => stillImageFileValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a still image file value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a still image file value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case stillImageFileValueV1: StillImageFileValueV1 => stillImageFileValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } - override def toFileValueContentV2: FileValueContentV2 = { + override def toFileValueContentV2: FileValueContentV2 = StillImageFileValueContentV2( ontologySchema = InternalSchema, fileValue = FileValueV2( @@ -1566,28 +1556,28 @@ case class StillImageFileValueV1(internalMimeType: String, dimX = dimX, dimY = dimY ) - } } /** - * A representation of a document in a binary format. - * - * @param internalMimeType the MIME-type of the internal representation. - * @param internalFilename the internal filename of the object. - * @param originalFilename the original filename of the object at the time of the import. - * @param pageCount the number of pages in the document. - * @param dimX the X dimension of the object. - * @param dimY the Y dimension of the object. - */ -case class DocumentFileValueV1(internalMimeType: String, - internalFilename: String, - originalFilename: Option[String] = None, - originalMimeType: Option[String] = None, - projectShortcode: String, - pageCount: Option[Int], - dimX: Option[Int], - dimY: Option[Int]) - extends FileValueV1 { + * A representation of a document in a binary format. + * + * @param internalMimeType the MIME-type of the internal representation. + * @param internalFilename the internal filename of the object. + * @param originalFilename the original filename of the object at the time of the import. + * @param pageCount the number of pages in the document. + * @param dimX the X dimension of the object. + * @param dimY the Y dimension of the object. + */ +case class DocumentFileValueV1( + internalMimeType: String, + internalFilename: String, + originalFilename: Option[String] = None, + originalMimeType: Option[String] = None, + projectShortcode: String, + pageCount: Option[Int], + dimX: Option[Int], + dimY: Option[Int] +) extends FileValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.DocumentFileValue def toJsValue: JsValue = ApiValueV1JsonProtocol.documentFileValueV1Format.write(this) @@ -1595,34 +1585,32 @@ case class DocumentFileValueV1(internalMimeType: String, override def toString: String = internalFilename /** - * Checks if a new document file value would duplicate an existing document file value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new document file value would duplicate an existing document file value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case documentFileValueV1: DocumentFileValueV1 => documentFileValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a document file value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a document file value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case documentFileValueV1: DocumentFileValueV1 => documentFileValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } - override def toFileValueContentV2: FileValueContentV2 = { + override def toFileValueContentV2: FileValueContentV2 = DocumentFileValueContentV2( ontologySchema = InternalSchema, fileValue = FileValueV2( @@ -1635,16 +1623,16 @@ case class DocumentFileValueV1(internalMimeType: String, dimX = dimX, dimY = dimY ) - } } -case class AudioFileValueV1(internalMimeType: String, - internalFilename: String, - originalFilename: Option[String], - originalMimeType: Option[String] = None, - projectShortcode: String, - duration: Option[BigDecimal] = None) - extends FileValueV1 { +case class AudioFileValueV1( + internalMimeType: String, + internalFilename: String, + originalFilename: Option[String], + originalMimeType: Option[String] = None, + projectShortcode: String, + duration: Option[BigDecimal] = None +) extends FileValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.AudioFileValue @@ -1653,34 +1641,32 @@ case class AudioFileValueV1(internalMimeType: String, override def toString: String = internalFilename /** - * Checks if a new moving image file value would duplicate an existing moving image file value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new moving image file value would duplicate an existing moving image file value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case audioFileValueV1: AudioFileValueV1 => audioFileValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a moving image file value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a moving image file value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case audioFileValueV1: AudioFileValueV1 => audioFileValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } - override def toFileValueContentV2: FileValueContentV2 = { + override def toFileValueContentV2: FileValueContentV2 = AudioFileValueContentV2( ontologySchema = InternalSchema, fileValue = FileValueV2( @@ -1691,19 +1677,19 @@ case class AudioFileValueV1(internalMimeType: String, ), duration = duration ) - } } -case class MovingImageFileValueV1(internalMimeType: String, - internalFilename: String, - originalFilename: Option[String], - originalMimeType: Option[String] = None, - projectShortcode: String, - dimX: Int, - dimY: Int, - fps: Option[BigDecimal] = None, - duration: Option[BigDecimal] = None) - extends FileValueV1 { +case class MovingImageFileValueV1( + internalMimeType: String, + internalFilename: String, + originalFilename: Option[String], + originalMimeType: Option[String] = None, + projectShortcode: String, + dimX: Int, + dimY: Int, + fps: Option[BigDecimal] = None, + duration: Option[BigDecimal] = None +) extends FileValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.MovingImageFileValue @@ -1712,34 +1698,32 @@ case class MovingImageFileValueV1(internalMimeType: String, override def toString: String = internalFilename /** - * Checks if a new moving image file value would duplicate an existing moving image file value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new moving image file value would duplicate an existing moving image file value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case movingImageFileValueV1: MovingImageFileValueV1 => movingImageFileValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a moving image file value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a moving image file value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case movingImageFileValueV1: MovingImageFileValueV1 => movingImageFileValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } - override def toFileValueContentV2: FileValueContentV2 = { + override def toFileValueContentV2: FileValueContentV2 = MovingImageFileValueContentV2( ontologySchema = InternalSchema, fileValue = FileValueV2( @@ -1753,15 +1737,15 @@ case class MovingImageFileValueV1(internalMimeType: String, fps = fps, duration = duration ) - } } -case class TextFileValueV1(internalMimeType: String, - internalFilename: String, - originalFilename: Option[String], - originalMimeType: Option[String] = None, - projectShortcode: String) - extends FileValueV1 { +case class TextFileValueV1( + internalMimeType: String, + internalFilename: String, + originalFilename: Option[String], + originalMimeType: Option[String] = None, + projectShortcode: String +) extends FileValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.TextFileValue @@ -1770,34 +1754,32 @@ case class TextFileValueV1(internalMimeType: String, override def toString: String = internalFilename /** - * Checks if a new text file value would duplicate an existing text file value. - * - * @param other another [[ValueV1]]. - * @return `true` if `other` is a duplicate of `this`. - */ - override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = { + * Checks if a new text file value would duplicate an existing text file value. + * + * @param other another [[ValueV1]]. + * @return `true` if `other` is a duplicate of `this`. + */ + override def isDuplicateOfOtherValue(other: ApiValueV1): Boolean = other match { case textFileValueV1: TextFileValueV1 => textFileValueV1 == this case otherValue => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${otherValue.valueTypeIri}") } - } /** - * Checks if a new version of a text file value would be redundant given the current version of the value. - * - * @param currentVersion the current version of the value. - * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. - */ - override def isRedundant(currentVersion: ApiValueV1): Boolean = { + * Checks if a new version of a text file value would be redundant given the current version of the value. + * + * @param currentVersion the current version of the value. + * @return `true` if this [[UpdateValueV1]] is redundant given `currentVersion`. + */ + override def isRedundant(currentVersion: ApiValueV1): Boolean = currentVersion match { case textFileValueV1: TextFileValueV1 => textFileValueV1 == this case other => throw InconsistentRepositoryDataException(s"Cannot compare a $valueTypeIri to a ${other.valueTypeIri}") } - } - override def toFileValueContentV2: FileValueContentV2 = { + override def toFileValueContentV2: FileValueContentV2 = TextFileValueContentV2( ontologySchema = InternalSchema, fileValue = FileValueV2( @@ -1807,16 +1789,15 @@ case class TextFileValueV1(internalMimeType: String, originalMimeType = Some(internalMimeType) ) ) - } } /** - * Represents information about a version of a value. - * - * @param valueObjectIri the IRI of the version. - * @param valueCreationDate the timestamp of the version. - * @param previousValue the IRI of the previous version. - */ + * Represents information about a version of a value. + * + * @param valueObjectIri the IRI of the version. + * @param valueCreationDate the timestamp of the version. + * @param previousValue the IRI of the previous version. + */ case class ValueVersionV1(valueObjectIri: IRI, valueCreationDate: Option[String], previousValue: Option[IRI]) extends ApiValueV1 { def valueTypeIri: IRI = OntologyConstants.KnoraBase.LinkValue @@ -1828,15 +1809,15 @@ case class ValueVersionV1(valueObjectIri: IRI, valueCreationDate: Option[String] // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON for property values. - */ + * A spray-json protocol for generating Knora API v1 JSON for property values. + */ object ApiValueV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with NullOptions { import org.knora.webapi.messages.v1.responder.resourcemessages.ResourceV1JsonProtocol._ /** - * Converts between [[KnoraCalendarV1]] objects and [[JsValue]] objects. - */ + * Converts between [[KnoraCalendarV1]] objects and [[JsValue]] objects. + */ implicit object KnoraCalendarV1JsonFormat extends JsonFormat[KnoraCalendarV1.Value] { def read(jsonVal: JsValue): KnoraCalendarV1.Value = jsonVal match { case JsString(str) => KnoraCalendarV1.lookup(str) @@ -1846,9 +1827,10 @@ object ApiValueV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol def write(calendarV1Value: KnoraCalendarV1.Value): JsValue = JsString(calendarV1Value.toString) } - /** å - * Converts between [[KnoraPrecisionV1]] objects and [[JsValue]] objects. - */ + /** + * å + * Converts between [[KnoraPrecisionV1]] objects and [[JsValue]] objects. + */ implicit object KnoraPrecisionV1JsonFormat extends JsonFormat[KnoraPrecisionV1.Value] { def read(jsonVal: JsValue): KnoraPrecisionV1.Value = jsonVal match { case JsString(str) => KnoraPrecisionV1.lookup(str) @@ -1859,33 +1841,33 @@ object ApiValueV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol } /** - * Converts between [[ApiValueV1]] objects and [[JsValue]] objects. - */ + * Converts between [[ApiValueV1]] objects and [[JsValue]] objects. + */ implicit object ValueV1JsonFormat extends JsonFormat[ApiValueV1] { /** - * Not implemented. - */ + * Not implemented. + */ def read(jsonVal: JsValue): ApiValueV1 = ??? /** - * Converts an [[ApiValueV1]] to a [[JsValue]]. - * - * @param valueV1 a [[ApiValueV1]] - * @return a [[JsValue]]. - */ + * Converts an [[ApiValueV1]] to a [[JsValue]]. + * + * @param valueV1 a [[ApiValueV1]] + * @return a [[JsValue]]. + */ def write(valueV1: ApiValueV1): JsValue = valueV1.toJsValue } implicit object ChangeFileValueResponseV1Format extends JsonFormat[ChangeFileValueResponseV1] { override def read(json: JsValue): ChangeFileValueResponseV1 = ??? - override def write(obj: ChangeFileValueResponseV1): JsValue = { + override def write(obj: ChangeFileValueResponseV1): JsValue = JsObject( Map( "locations" -> obj.locations.toJson - )) - } + ) + ) } implicit val createFileV1Format: RootJsonFormat[CreateFileV1] = jsonFormat3(CreateFileV1) @@ -1899,15 +1881,19 @@ object ApiValueV1JsonProtocol extends SprayJsonSupport with DefaultJsonProtocol implicit val valueVersionV1Format: JsonFormat[ValueVersionV1] = jsonFormat3(ValueVersionV1) implicit val linkValueV1Format: JsonFormat[LinkValueV1] = jsonFormat4(LinkValueV1) implicit val valueVersionHistoryGetResponseV1Format: RootJsonFormat[ValueVersionHistoryGetResponseV1] = jsonFormat1( - ValueVersionHistoryGetResponseV1) + ValueVersionHistoryGetResponseV1 + ) implicit val createRichtextV1Format: RootJsonFormat[CreateRichtextV1] = jsonFormat4(CreateRichtextV1) implicit val createValueApiRequestV1Format: RootJsonFormat[CreateValueApiRequestV1] = jsonFormat16( - CreateValueApiRequestV1) + CreateValueApiRequestV1 + ) implicit val createValueResponseV1Format: RootJsonFormat[CreateValueResponseV1] = jsonFormat4(CreateValueResponseV1) implicit val changeValueApiRequestV1Format: RootJsonFormat[ChangeValueApiRequestV1] = jsonFormat14( - ChangeValueApiRequestV1) + ChangeValueApiRequestV1 + ) implicit val changeValueResponseV1Format: RootJsonFormat[ChangeValueResponseV1] = jsonFormat4(ChangeValueResponseV1) implicit val deleteValueResponseV1Format: RootJsonFormat[DeleteValueResponseV1] = jsonFormat1(DeleteValueResponseV1) implicit val changeFileValueApiRequestV1Format: RootJsonFormat[ChangeFileValueApiRequestV1] = jsonFormat1( - ChangeFileValueApiRequestV1) + ChangeFileValueApiRequestV1 + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v1/routing/authenticationmessages/AuthenticationMessagesV1.scala b/webapi/src/main/scala/org/knora/webapi/messages/v1/routing/authenticationmessages/AuthenticationMessagesV1.scala index c3844fa56c..d7eff0c2bd 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v1/routing/authenticationmessages/AuthenticationMessagesV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v1/routing/authenticationmessages/AuthenticationMessagesV1.scala @@ -29,20 +29,20 @@ import spray.json._ // API requests /** - * Represents an API request payload that asks the Knora API server to create a new session - * - * @param username - * @param password - */ + * Represents an API request payload that asks the Knora API server to create a new session + * + * @param username + * @param password + */ case class CreateSessionApiRequestV1(username: String, password: String) /** - * Representing user's credentials (iri, email, password) - * - * @param userIri the user's IRI. - * @param email the user's email. - * @param password the user's password. - */ + * Representing user's credentials (iri, email, password) + * + * @param userIri the user's IRI. + * @param email the user's email. + * @param password the user's password. + */ case class CredentialsV1(userIri: IRI, email: String, password: String) { def urlEncodedIri = java.net.URLEncoder.encode(userIri, "utf-8") @@ -51,10 +51,10 @@ case class CredentialsV1(userIri: IRI, email: String, password: String) { } /** - * Representing user's credentials - * - * @param user the user's information. - */ + * Representing user's credentials + * + * @param user the user's information. + */ case class CredentialsADM(user: UserADM, password: String) { def iri = user.id @@ -72,10 +72,11 @@ case class CredentialsADM(user: UserADM, password: String) { // JSON formatting /** - * A spray-json protocol for generating Knora API v1 JSON for property values. - */ + * A spray-json protocol for generating Knora API v1 JSON for property values. + */ object AuthenticateV1JsonProtocol extends DefaultJsonProtocol with NullOptions with SprayJsonSupport { implicit val createSessionApiRequestV1Format: RootJsonFormat[CreateSessionApiRequestV1] = jsonFormat2( - CreateSessionApiRequestV1) + CreateSessionApiRequestV1 + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraRequestV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraRequestV2.scala index ed2f322e25..bb938e7579 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraRequestV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraRequestV2.scala @@ -32,24 +32,24 @@ import org.knora.webapi.settings.KnoraSettingsImpl import scala.concurrent.{ExecutionContext, Future} /** - * A tagging trait for messages that can be sent to Knora API v2 responders. - */ + * A tagging trait for messages that can be sent to Knora API v2 responders. + */ trait KnoraRequestV2 /** - * A trait for request messages that are constructed as an [[RdfModel]]. - */ + * A trait for request messages that are constructed as an [[RdfModel]]. + */ trait KnoraRdfModelRequestV2 { /** - * An [[RdfModel]] representing the request. - */ + * An [[RdfModel]] representing the request. + */ val rdfModel: RdfModel /** - * Returns a Turtle representation of the graph. - */ - def toTurtle(featureFactoryConfig: FeatureFactoryConfig): String = { + * Returns a Turtle representation of the graph. + */ + def toTurtle(featureFactoryConfig: FeatureFactoryConfig): String = RdfFeatureFactory .getRdfFormatUtil(featureFactoryConfig) .format( @@ -57,35 +57,36 @@ trait KnoraRdfModelRequestV2 { rdfFormat = Turtle, prettyPrint = false ) - } } /** - * A trait for objects that can generate case class instances based on JSON-LD input. - * - * @tparam C the type of the case class that can be generated. - */ + * A trait for objects that can generate case class instances based on JSON-LD input. + * + * @tparam C the type of the case class that can be generated. + */ trait KnoraJsonLDRequestReaderV2[C] { /** - * Converts JSON-LD input into a case class instance. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[C] + * Converts JSON-LD input into a case class instance. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[C] } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala index b7f79eea95..0d468fbc4e 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/KnoraResponseV2.scala @@ -92,7 +92,7 @@ trait KnoraJsonLDResponseV2 extends KnoraResponseV2 { case nonJsonLD: NonJsonLD => // Some other format. Convert the JSON-LD document to an RDF model. val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) - val rdfModel: RdfModel = jsonLDDocument.toRdfModel(rdfFormatUtil.getRdfModelFactory) + val rdfModel: RdfModel = jsonLDDocument.toRdfModel(rdfFormatUtil.getRdfModelFactory) // Convert the model to the requested format. rdfFormatUtil.format( @@ -147,7 +147,7 @@ trait KnoraTurtleResponseV2 extends KnoraResponseV2 { case _ => // Some other format. Parse the Turtle to an RdfModel. val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) - val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtle, rdfFormat = Turtle) + val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtle, rdfFormat = Turtle) // Return the model in the requested format. rdfFormatUtil.format( diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/listsmessages/ListsMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/listsmessages/ListsMessagesV2.scala index e79d944e8a..9707a28df3 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/listsmessages/ListsMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/listsmessages/ListsMessagesV2.scala @@ -32,70 +32,73 @@ import org.knora.webapi.messages.{OntologyConstants, StringFormatter} import org.knora.webapi.settings.KnoraSettingsImpl /** - * An abstract trait representing a Knora v2 API request message that can be sent to `ListsResponderV2`. - */ + * An abstract trait representing a Knora v2 API request message that can be sent to `ListsResponderV2`. + */ sealed trait ListsResponderRequestV2 extends KnoraRequestV2 /** - * Requests a list. A successful response will be a [[ListGetResponseV2]] - * - * @param listIri the IRI of the list (Iri of the list's root node). - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ + * Requests a list. A successful response will be a [[ListGetResponseV2]] + * + * @param listIri the IRI of the list (Iri of the list's root node). + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ case class ListGetRequestV2(listIri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ListsResponderRequestV2 /** - * An abstract trait providing a convenience method for language handling. - */ + * An abstract trait providing a convenience method for language handling. + */ trait ListResponderResponseV2 { /** - * Given an Iri and a [[StringLiteralSequenceV2]], gets he string value in the user's preferred language. - * - * @param iri the Iri pointing to the string value. - * @param stringVals the string values to choose from. - * @param userLang the user's preferred language. - * @param fallbackLang the fallback language if the preferred language is not available. - * @return a [[Map[IRI, JsonLDString]] (empty in case no string value is available). - */ - def makeMapIriToJSONLDString(iri: IRI, - stringVals: StringLiteralSequenceV2, - userLang: String, - fallbackLang: String): Map[IRI, JsonLDString] = { + * Given an Iri and a [[StringLiteralSequenceV2]], gets he string value in the user's preferred language. + * + * @param iri the Iri pointing to the string value. + * @param stringVals the string values to choose from. + * @param userLang the user's preferred language. + * @param fallbackLang the fallback language if the preferred language is not available. + * @return a [[Map[IRI, JsonLDString]] (empty in case no string value is available). + */ + def makeMapIriToJSONLDString( + iri: IRI, + stringVals: StringLiteralSequenceV2, + userLang: String, + fallbackLang: String + ): Map[IRI, JsonLDString] = Map( iri -> stringVals.getPreferredLanguage(userLang, fallbackLang) - ).collect { - case (iri: IRI, Some(strVal: String)) => iri -> JsonLDString(strVal) + ).collect { case (iri: IRI, Some(strVal: String)) => + iri -> JsonLDString(strVal) } - } } /** - * Represents a response to a [[ListGetRequestV2]]. - * - * @param list the list to be returned. - * @param userLang the user's preferred language. - * @param fallbackLang the fallback language if the preferred language is not available. - */ + * Represents a response to a [[ListGetRequestV2]]. + * + * @param list the list to be returned. + * @param userLang the user's preferred language. + * @param fallbackLang the fallback language if the preferred language is not available. + */ case class ListGetResponseV2(list: ListADM, userLang: String, fallbackLang: String) extends KnoraJsonLDResponseV2 with ListResponderResponseV2 { - def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Given a [[ListNodeADM]], constructs a [[JsonLDObject]]. - * - * @param node the node to be turned into JSON-LD. - * @return a [[JsonLDObject]] representing the node. - */ + * Given a [[ListNodeADM]], constructs a [[JsonLDObject]]. + * + * @param node the node to be turned into JSON-LD. + * @return a [[JsonLDObject]] representing the node. + */ def makeNode(node: ListChildNodeADM): JsonLDObject = { val label: Map[IRI, JsonLDString] = @@ -105,14 +108,17 @@ case class ListGetResponseV2(list: ListADM, userLang: String, fallbackLang: Stri val position: Map[IRI, JsonLDInt] = Map( OntologyConstants.KnoraBase.ListNodePosition.toSmartIri.toOntologySchema(ApiV2Complex).toString -> JsonLDInt( - node.position)) + node.position + ) + ) val children: Map[IRI, JsonLDArray] = if (node.children.nonEmpty) { Map( OntologyConstants.KnoraBase.HasSubListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString -> JsonLDArray( node.children.map { childNode: ListChildNodeADM => makeNode(childNode) // recursion - }) + } + ) ) } else { // no children (abort condition for recursion) @@ -128,7 +134,8 @@ case class ListGetResponseV2(list: ListADM, userLang: String, fallbackLang: Stri Map( "@id" -> JsonLDString(node.id), "@type" -> JsonLDString( - OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString) + OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString + ) ) ++ position ++ nodeHasRootNode ++ children ++ label ++ comment ) } @@ -146,7 +153,9 @@ case class ListGetResponseV2(list: ListADM, userLang: String, fallbackLang: Stri OntologyConstants.KnoraBase.HasSubListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString -> JsonLDArray( list.children.map { childNode: ListNodeADM => makeNode(childNode.asInstanceOf[ListChildNodeADM]) - })) + } + ) + ) } else { Map.empty[IRI, JsonLDArray] } @@ -160,47 +169,53 @@ case class ListGetResponseV2(list: ListADM, userLang: String, fallbackLang: Stri Map( "@id" -> JsonLDString(listinfo.id), "@type" -> JsonLDString( - OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString), + OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString + ), OntologyConstants.KnoraBase.IsRootNode.toSmartIri.toOntologySchema(ApiV2Complex).toString -> JsonLDBoolean(true) - ) ++ project ++ children ++ label ++ comment) + ) ++ project ++ children ++ label ++ comment + ) val context = JsonLDObject( Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> JsonLDString( - OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion), + OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion + ), "rdfs" -> JsonLDString("http://www.w3.org/2000/01/rdf-schema#"), "rdf" -> JsonLDString("http://www.w3.org/1999/02/22-rdf-syntax-ns#"), "owl" -> JsonLDString("http://www.w3.org/2002/07/owl#"), "xsd" -> JsonLDString("http://www.w3.org/2001/XMLSchema#") - )) + ) + ) JsonLDDocument(body, context) } } /** - * Requests a list node. A successful response will be a [[NodeGetResponseV2]] - * - * @param nodeIri the IRI of the node to retrieve. - * @param featureFactoryConfig the feature factory configuration. - */ + * Requests a list node. A successful response will be a [[NodeGetResponseV2]] + * + * @param nodeIri the IRI of the node to retrieve. + * @param featureFactoryConfig the feature factory configuration. + */ case class NodeGetRequestV2(nodeIri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends ListsResponderRequestV2 /** - * Represents a response to a [[NodeGetRequestV2]]. - * - * @param node the node to be returned. - * @param userLang the user's preferred language. - * @param fallbackLang the fallback language if the preferred language is not available. - */ + * Represents a response to a [[NodeGetRequestV2]]. + * + * @param node the node to be returned. + * @param userLang the user's preferred language. + * @param fallbackLang the fallback language if the preferred language is not available. + */ case class NodeGetResponseV2(node: ListNodeInfoADM, userLang: String, fallbackLang: String) extends KnoraJsonLDResponseV2 with ListResponderResponseV2 { - def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -222,7 +237,8 @@ case class NodeGetResponseV2(node: ListNodeInfoADM, userLang: String, fallbackLa Map( "@id" -> JsonLDString(root.id), "@type" -> JsonLDString( - OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString) + OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString + ) ) ++ rootNode ++ label ++ comment ++ position ) } @@ -237,17 +253,21 @@ case class NodeGetResponseV2(node: ListNodeInfoADM, userLang: String, fallbackLa val position: Map[IRI, JsonLDInt] = Map( OntologyConstants.KnoraBase.ListNodePosition.toSmartIri.toOntologySchema(ApiV2Complex).toString -> JsonLDInt( - child.position)) + child.position + ) + ) val rootNode = Map( OntologyConstants.KnoraBase.HasRootNode.toSmartIri.toOntologySchema(ApiV2Complex).toString -> JsonLDUtil - .iriToJsonLDObject(child.hasRootNode)) + .iriToJsonLDObject(child.hasRootNode) + ) JsonLDObject( Map( "@id" -> JsonLDString(child.id), "@type" -> JsonLDString( - OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString) + OntologyConstants.KnoraBase.ListNode.toSmartIri.toOntologySchema(ApiV2Complex).toString + ) ) ++ rootNode ++ label ++ comment ++ position ) } @@ -256,12 +276,14 @@ case class NodeGetResponseV2(node: ListNodeInfoADM, userLang: String, fallbackLa val context = JsonLDObject( Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> JsonLDString( - OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion), + OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion + ), "rdfs" -> JsonLDString("http://www.w3.org/2000/01/rdf-schema#"), "rdf" -> JsonLDString("http://www.w3.org/1999/02/22-rdf-syntax-ns#"), "owl" -> JsonLDString("http://www.w3.org/2002/07/owl#"), "xsd" -> JsonLDString("http://www.w3.org/2001/XMLSchema#") - )) + ) + ) JsonLDDocument(body, context) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/metadatamessages/MetadataMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/metadatamessages/MetadataMessagesV2.scala index 657450eb6f..9712de30b8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/metadatamessages/MetadataMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/metadatamessages/MetadataMessagesV2.scala @@ -31,64 +31,68 @@ import org.knora.webapi.messages.util.rdf.RdfModel import org.knora.webapi.messages.v2.responder._ /** - * An abstract trait for messages that can be sent to `ResourcesResponderV2`. - */ + * An abstract trait for messages that can be sent to `ResourcesResponderV2`. + */ sealed trait MetadataResponderRequestV2 extends KnoraRequestV2 { /** - * The user that made the request. - */ + * The user that made the request. + */ def requestingUser: UserADM } /** - * Requests metadata about a project. A successful response will be a [[MetadataGetResponseV2]]. - * - * @param projectADM the project for which metadata is requested. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class MetadataGetRequestV2(projectADM: ProjectADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends MetadataResponderRequestV2 { + * Requests metadata about a project. A successful response will be a [[MetadataGetResponseV2]]. + * + * @param projectADM the project for which metadata is requested. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class MetadataGetRequestV2( + projectADM: ProjectADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends MetadataResponderRequestV2 { val projectIri: IRI = projectADM.id // Ensure that the project isn't the system project or the shared ontologies project. - if (projectIri == OntologyConstants.KnoraAdmin.SystemProject || projectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { + if ( + projectIri == OntologyConstants.KnoraAdmin.SystemProject || projectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { throw BadRequestException(s"Metadata cannot be requested from project <$projectIri>") } } /** - * Represents metadata about a project. - * - * @param turtle project metadata in Turtle format. - */ + * Represents metadata about a project. + * + * @param turtle project metadata in Turtle format. + */ case class MetadataGetResponseV2(turtle: String) extends KnoraTurtleResponseV2 /** - * A request to create or update metadata about a project. If metadata already exists - * for the project, it will be replaced by the metadata in this message. A successful response - * will be a [[SuccessResponseV2]]. - * - * @param rdfModel the project metadata to be stored. - * @param projectADM the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ -case class MetadataPutRequestV2(rdfModel: RdfModel, - projectADM: ProjectADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends KnoraRdfModelRequestV2 + * A request to create or update metadata about a project. If metadata already exists + * for the project, it will be replaced by the metadata in this message. A successful response + * will be a [[SuccessResponseV2]]. + * + * @param rdfModel the project metadata to be stored. + * @param projectADM the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ +case class MetadataPutRequestV2( + rdfModel: RdfModel, + projectADM: ProjectADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends KnoraRdfModelRequestV2 with MetadataResponderRequestV2 { /** - * The project IRI. - */ + * The project IRI. + */ val projectIri: IRI = projectADM.id // Check if the requesting user is allowed to create project metadata. @@ -98,7 +102,9 @@ case class MetadataPutRequestV2(rdfModel: RdfModel, } // Ensure that the project isn't the system project or the shared ontologies project. - if (projectIri == OntologyConstants.KnoraAdmin.SystemProject || projectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { + if ( + projectIri == OntologyConstants.KnoraAdmin.SystemProject || projectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { throw BadRequestException(s"Metadata cannot be created in project <$projectIri>") } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala index 422b44c518..a7ba00594f 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala @@ -26,9 +26,9 @@ import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.Knora import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} /** - * Rules for converting `knora-base` (or an ontology based on it) into `knora-api` in the [[ApiV2Complex]] schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Rules for converting `knora-base` (or an ontology based on it) into `knora-api` in the [[ApiV2Complex]] schema. + * See also [[OntologyConstants.CorrespondingIris]]. + */ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformationRules { private implicit val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -133,7 +133,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "user has permission", + LanguageCodes.EN -> "user has permission" ) ), makePredicate( @@ -153,7 +153,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "ARK URL", + LanguageCodes.EN -> "ARK URL" ) ), makePredicate( @@ -173,7 +173,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "version ARK URL", + LanguageCodes.EN -> "version ARK URL" ) ), makePredicate( @@ -193,7 +193,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "version date", + LanguageCodes.EN -> "version date" ) ), makePredicate( @@ -213,7 +213,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "author", + LanguageCodes.EN -> "author" ) ), makePredicate( @@ -233,7 +233,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "is shared", + LanguageCodes.EN -> "is shared" ) ), makePredicate( @@ -253,7 +253,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "is shared", + LanguageCodes.EN -> "is shared" ) ), makePredicate( @@ -420,7 +420,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "new modification date", + LanguageCodes.EN -> "new modification date" ) ), makePredicate( @@ -1676,9 +1676,9 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation ) /** - * Properties to remove from `knora-base` before converting it to the [[ApiV2Complex]] schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Properties to remove from `knora-base` before converting it to the [[ApiV2Complex]] schema. + * See also [[OntologyConstants.CorrespondingIris]]. + */ override val internalPropertiesToRemove: Set[SmartIri] = Set( OntologyConstants.Rdf.Subject, OntologyConstants.Rdf.Predicate, @@ -1746,8 +1746,8 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation ).map(_.toSmartIri) /** - * Classes to remove from `knora-base` before converting it to the [[ApiV2Complex]] schema. - */ + * Classes to remove from `knora-base` before converting it to the [[ApiV2Complex]] schema. + */ override val internalClassesToRemove: Set[SmartIri] = Set( OntologyConstants.KnoraBase.MappingElement, OntologyConstants.KnoraBase.MappingComponent, @@ -1759,9 +1759,9 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation ).map(_.toSmartIri) /** - * After `knora-base` has been converted to the [[ApiV2Complex]] schema, these cardinalities must be - * added to the specified classes to obtain `knora-api`. - */ + * After `knora-base` has been converted to the [[ApiV2Complex]] schema, these cardinalities must be + * added to the specified classes to obtain `knora-api`. + */ override val externalCardinalitiesToAdd: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = Map( OntologyConstants.KnoraApiV2Complex.Resource -> ResourceCardinalities, OntologyConstants.KnoraApiV2Complex.DateBase -> DateBaseCardinalities, @@ -1783,23 +1783,21 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation OntologyConstants.KnoraApiV2Complex.DocumentFileValue -> DocumentFileValueCardinalities, OntologyConstants.KnoraApiV2Complex.MovingImageFileValue -> MovingImageFileValueCardinalities, OntologyConstants.KnoraApiV2Complex.AudioFileValue -> AudioFileValueCardinalities - ).map { - case (classIri, cardinalities) => - classIri.toSmartIri -> cardinalities.map { - case (propertyIri, cardinality) => - propertyIri.toSmartIri -> Cardinality.KnoraCardinalityInfo(cardinality) - } + ).map { case (classIri, cardinalities) => + classIri.toSmartIri -> cardinalities.map { case (propertyIri, cardinality) => + propertyIri.toSmartIri -> Cardinality.KnoraCardinalityInfo(cardinality) + } } /** - * Classes that need to be added to `knora-base`, after converting it to the [[ApiV2Complex]] schema, to obtain `knora-api`. - */ + * Classes that need to be added to `knora-base`, after converting it to the [[ApiV2Complex]] schema, to obtain `knora-api`. + */ override val externalClassesToAdd: Map[SmartIri, ReadClassInfoV2] = Map.empty[SmartIri, ReadClassInfoV2] /** - * Properties that need to be added to `knora-base`, after converting it to the [[ApiV2Complex]] schema, to obtain `knora-api`. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Properties that need to be added to `knora-base`, after converting it to the [[ApiV2Complex]] schema, to obtain `knora-api`. + * See also [[OntologyConstants.CorrespondingIris]]. + */ override val externalPropertiesToAdd: Map[SmartIri, ReadPropertyInfoV2] = Set( Label, Result, @@ -1881,48 +1879,51 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation // Convenience functions for building ontology entities, to make the code above more concise. /** - * Makes a [[PredicateInfoV2]]. - * - * @param predicateIri the IRI of the predicate. - * @param objects the non-language-specific objects of the predicate. - * @param objectsWithLang the language-specific objects of the predicate. - * @return a [[PredicateInfoV2]]. - */ - private def makePredicate(predicateIri: IRI, - objects: Seq[OntologyLiteralV2] = Seq.empty[OntologyLiteralV2], - objectsWithLang: Map[String, String] = Map.empty[String, String]): PredicateInfoV2 = { + * Makes a [[PredicateInfoV2]]. + * + * @param predicateIri the IRI of the predicate. + * @param objects the non-language-specific objects of the predicate. + * @param objectsWithLang the language-specific objects of the predicate. + * @return a [[PredicateInfoV2]]. + */ + private def makePredicate( + predicateIri: IRI, + objects: Seq[OntologyLiteralV2] = Seq.empty[OntologyLiteralV2], + objectsWithLang: Map[String, String] = Map.empty[String, String] + ): PredicateInfoV2 = PredicateInfoV2( predicateIri = predicateIri.toSmartIri, - objects = objects ++ objectsWithLang.map { - case (lang, str) => StringLiteralV2(str, Some(lang)) + objects = objects ++ objectsWithLang.map { case (lang, str) => + StringLiteralV2(str, Some(lang)) } ) - } /** - * Makes a [[ReadPropertyInfoV2]]. - * - * @param propertyIri the IRI of the property. - * @param propertyType the type of the property (owl:ObjectProperty, owl:DatatypeProperty, or rdf:Property). - * @param isResourceProp true if this is a subproperty of `knora-api:hasValue` or `knora-api:hasLinkTo`. - * @param subPropertyOf the set of direct superproperties of this property. - * @param isEditable true if this is a Knora resource property that can be edited via the Knora API. - * @param isLinkValueProp true if the property points to a link value (reification). - * @param predicates the property's predicates. - * @param subjectType the required type of the property's subject. - * @param objectType the required type of the property's object. - * @return a [[ReadPropertyInfoV2]]. - */ - private def makeProperty(propertyIri: IRI, - propertyType: IRI, - isResourceProp: Boolean = false, - subPropertyOf: Set[IRI] = Set.empty[IRI], - isEditable: Boolean = false, - isLinkProp: Boolean = false, - isLinkValueProp: Boolean = false, - predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2], - subjectType: Option[IRI] = None, - objectType: Option[IRI] = None): ReadPropertyInfoV2 = { + * Makes a [[ReadPropertyInfoV2]]. + * + * @param propertyIri the IRI of the property. + * @param propertyType the type of the property (owl:ObjectProperty, owl:DatatypeProperty, or rdf:Property). + * @param isResourceProp true if this is a subproperty of `knora-api:hasValue` or `knora-api:hasLinkTo`. + * @param subPropertyOf the set of direct superproperties of this property. + * @param isEditable true if this is a Knora resource property that can be edited via the Knora API. + * @param isLinkValueProp true if the property points to a link value (reification). + * @param predicates the property's predicates. + * @param subjectType the required type of the property's subject. + * @param objectType the required type of the property's object. + * @return a [[ReadPropertyInfoV2]]. + */ + private def makeProperty( + propertyIri: IRI, + propertyType: IRI, + isResourceProp: Boolean = false, + subPropertyOf: Set[IRI] = Set.empty[IRI], + isEditable: Boolean = false, + isLinkProp: Boolean = false, + isLinkValueProp: Boolean = false, + predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2], + subjectType: Option[IRI] = None, + objectType: Option[IRI] = None + ): ReadPropertyInfoV2 = { val propTypePred = makePredicate( predicateIri = OntologyConstants.Rdf.Type, objects = Seq(SmartIriLiteralV2(propertyType.toSmartIri)) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2SimpleTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2SimpleTransformationRules.scala index e1e5cca003..a0d37906e1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2SimpleTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2SimpleTransformationRules.scala @@ -26,9 +26,9 @@ import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.Knora import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} /** - * Rules for converting `knora-base` (or an ontology based on it) into `knora-api` in the [[ApiV2Simple]] schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Rules for converting `knora-base` (or an ontology based on it) into `knora-api` in the [[ApiV2Simple]] schema. + * See also [[OntologyConstants.CorrespondingIris]]. + */ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationRules { private implicit val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -117,7 +117,7 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "ARK URL", + LanguageCodes.EN -> "ARK URL" ) ), makePredicate( @@ -137,7 +137,7 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR makePredicate( predicateIri = OntologyConstants.Rdfs.Label, objectsWithLang = Map( - LanguageCodes.EN -> "version ARK URL", + LanguageCodes.EN -> "version ARK URL" ) ), makePredicate( @@ -235,7 +235,8 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR datatypeInfo = DatatypeInfoV2( onDatatype = OntologyConstants.Xsd.String.toSmartIri, pattern = Some( - "(GREGORIAN|JULIAN|ISLAMIC):\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?(:\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?)?") + "(GREGORIAN|JULIAN|ISLAMIC):\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?(:\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?)?" + ) ), predicates = Seq( makePredicate( @@ -412,9 +413,9 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR ) /** - * Properties to remove from `knora-base` before converting it to the [[ApiV2Simple]] schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Properties to remove from `knora-base` before converting it to the [[ApiV2Simple]] schema. + * See also [[OntologyConstants.CorrespondingIris]]. + */ override val internalPropertiesToRemove: Set[SmartIri] = Set( OntologyConstants.KnoraBase.OntologyVersion, OntologyConstants.KnoraBase.CreationDate, @@ -506,9 +507,9 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR ).map(_.toSmartIri) /** - * Classes to remove from `knora-base` before converting it to the [[ApiV2Simple]] schema. Standoff classes - * are removed, too, but aren't included here, because this is taken care of in [[ReadOntologyV2]]. - */ + * Classes to remove from `knora-base` before converting it to the [[ApiV2Simple]] schema. Standoff classes + * are removed, too, but aren't included here, because this is taken care of in [[ReadOntologyV2]]. + */ override val internalClassesToRemove: Set[SmartIri] = Set( OntologyConstants.KnoraBase.ValueBase, OntologyConstants.KnoraBase.DateBase, @@ -545,22 +546,20 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR ).map(_.toSmartIri) /** - * After `knora-base` has been converted to the [[ApiV2Simple]] schema, these cardinalities must be - * added to the specified classes to obtain `knora-api`. - */ + * After `knora-base` has been converted to the [[ApiV2Simple]] schema, these cardinalities must be + * added to the specified classes to obtain `knora-api`. + */ override val externalCardinalitiesToAdd: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] = Map( OntologyConstants.KnoraBase.Resource -> ResourceCardinalites - ).map { - case (classIri, cardinalities) => - classIri.toSmartIri.toOntologySchema(ApiV2Simple) -> cardinalities.map { - case (propertyIri, cardinality) => - propertyIri.toSmartIri.toOntologySchema(ApiV2Simple) -> Cardinality.KnoraCardinalityInfo(cardinality) - } + ).map { case (classIri, cardinalities) => + classIri.toSmartIri.toOntologySchema(ApiV2Simple) -> cardinalities.map { case (propertyIri, cardinality) => + propertyIri.toSmartIri.toOntologySchema(ApiV2Simple) -> Cardinality.KnoraCardinalityInfo(cardinality) + } } /** - * Classes that need to be added to `knora-base`, after converting it to the [[ApiV2Simple]] schema, to obtain `knora-api`. - */ + * Classes that need to be added to `knora-base`, after converting it to the [[ApiV2Simple]] schema, to obtain `knora-api`. + */ override val externalClassesToAdd: Map[SmartIri, ReadClassInfoV2] = Set( File, Date, @@ -574,9 +573,9 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR }.toMap /** - * Properties that need to be added to `knora-base`, after converting it to the [[ApiV2Simple]] schema, to obtain `knora-api`. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Properties that need to be added to `knora-base`, after converting it to the [[ApiV2Simple]] schema, to obtain `knora-api`. + * See also [[OntologyConstants.CorrespondingIris]]. + */ override val externalPropertiesToAdd: Map[SmartIri, ReadPropertyInfoV2] = Set( Label, Result, @@ -597,41 +596,44 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR // Convenience functions for building ontology entities, to make the code above more concise. /** - * Makes a [[PredicateInfoV2]]. - * - * @param predicateIri the IRI of the predicate. - * @param objects the non-language-specific objects of the predicate. - * @param objectsWithLang the language-specific objects of the predicate. - * @return a [[PredicateInfoV2]]. - */ - private def makePredicate(predicateIri: IRI, - objects: Seq[OntologyLiteralV2] = Seq.empty[OntologyLiteralV2], - objectsWithLang: Map[String, String] = Map.empty[String, String]): PredicateInfoV2 = { + * Makes a [[PredicateInfoV2]]. + * + * @param predicateIri the IRI of the predicate. + * @param objects the non-language-specific objects of the predicate. + * @param objectsWithLang the language-specific objects of the predicate. + * @return a [[PredicateInfoV2]]. + */ + private def makePredicate( + predicateIri: IRI, + objects: Seq[OntologyLiteralV2] = Seq.empty[OntologyLiteralV2], + objectsWithLang: Map[String, String] = Map.empty[String, String] + ): PredicateInfoV2 = PredicateInfoV2( predicateIri = predicateIri.toSmartIri, - objects = objects ++ objectsWithLang.map { - case (lang, str) => StringLiteralV2(str, Some(lang)) + objects = objects ++ objectsWithLang.map { case (lang, str) => + StringLiteralV2(str, Some(lang)) } ) - } /** - * Makes a [[ReadPropertyInfoV2]]. - * - * @param propertyIri the IRI of the property. - * @param propertyType the type of the property (owl:ObjectProperty, owl:DatatypeProperty, or rdf:Property). - * @param subPropertyOf the set of direct superproperties of this property. - * @param predicates the property's predicates. - * @param subjectType the required type of the property's subject. - * @param objectType the required type of the property's object. - * @return a [[ReadPropertyInfoV2]]. - */ - private def makeProperty(propertyIri: IRI, - propertyType: IRI, - subPropertyOf: Set[IRI] = Set.empty[IRI], - predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2], - subjectType: Option[IRI] = None, - objectType: Option[IRI] = None): ReadPropertyInfoV2 = { + * Makes a [[ReadPropertyInfoV2]]. + * + * @param propertyIri the IRI of the property. + * @param propertyType the type of the property (owl:ObjectProperty, owl:DatatypeProperty, or rdf:Property). + * @param subPropertyOf the set of direct superproperties of this property. + * @param predicates the property's predicates. + * @param subjectType the required type of the property's subject. + * @param objectType the required type of the property's object. + * @return a [[ReadPropertyInfoV2]]. + */ + private def makeProperty( + propertyIri: IRI, + propertyType: IRI, + subPropertyOf: Set[IRI] = Set.empty[IRI], + predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2], + subjectType: Option[IRI] = None, + objectType: Option[IRI] = None + ): ReadPropertyInfoV2 = { val propTypePred = makePredicate( predicateIri = OntologyConstants.Rdf.Type, objects = Seq(SmartIriLiteralV2(propertyType.toSmartIri)) @@ -666,16 +668,18 @@ object KnoraBaseToApiV2SimpleTransformationRules extends OntologyTransformationR } /** - * Makes a [[ReadClassInfoV2]] representing an rdfs:Datatype. - * - * @param datatypeIri the IRI of the datatype. - * @param datatypeInfo a [[DatatypeInfoV2]] describing the datatype. - * @param predicates the predicates of the datatype. - * @return a [[ReadClassInfoV2]]. - */ - private def makeDatatype(datatypeIri: IRI, - datatypeInfo: DatatypeInfoV2, - predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2]): ReadClassInfoV2 = { + * Makes a [[ReadClassInfoV2]] representing an rdfs:Datatype. + * + * @param datatypeIri the IRI of the datatype. + * @param datatypeInfo a [[DatatypeInfoV2]] describing the datatype. + * @param predicates the predicates of the datatype. + * @return a [[ReadClassInfoV2]]. + */ + private def makeDatatype( + datatypeIri: IRI, + datatypeInfo: DatatypeInfoV2, + predicates: Seq[PredicateInfoV2] = Seq.empty[PredicateInfoV2] + ): ReadClassInfoV2 = { val rdfType = OntologyConstants.Rdf.Type.toSmartIri -> PredicateInfoV2( predicateIri = OntologyConstants.Rdf.Type.toSmartIri, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala index 5abdcd1f3f..71d59cf7a2 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyMessagesV2.scala @@ -371,7 +371,7 @@ object OntologyUpdateHelper { } val predicateInfoToUpdate = predicatesWithNewData.values.head - val predicateToUpdate = predicateInfoToUpdate.predicateIri + val predicateToUpdate = predicateInfoToUpdate.predicateIri if (!LabelAndCommentPredicates.contains(predicateToUpdate.toString)) { throw BadRequestException(s"Invalid predicate: $predicateToUpdate") @@ -453,9 +453,9 @@ object CreatePropertyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateProperty // Get the property definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) - val propertyInfoContent = propertyUpdateInfo.propertyInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) + val propertyInfoContent = propertyUpdateInfo.propertyInfoContent val lastModificationDate = propertyUpdateInfo.lastModificationDate // Check that the knora-api:subjectType (if provided) and the knora-api:objectType point to valid entity IRIs. @@ -562,9 +562,9 @@ object CreateClassRequestV2 extends KnoraJsonLDRequestReaderV2[CreateClassReques // Get the class definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate // The request must provide an rdfs:label and an rdfs:comment. @@ -648,9 +648,9 @@ object AddCardinalitiesToClassRequestV2 extends KnoraJsonLDRequestReaderV2[AddCa ): AddCardinalitiesToClassRequestV2 = { // Get the class definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate // The request must provide cardinalities. @@ -741,9 +741,9 @@ object ChangeCardinalitiesRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeCar featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM ): ChangeCardinalitiesRequestV2 = { - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate ChangeCardinalitiesRequestV2( @@ -817,9 +817,9 @@ object CanDeleteCardinalitiesFromClassRequestV2 featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM ): CanDeleteCardinalitiesFromClassRequestV2 = { - val inputOntology = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntology) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntology = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntology) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate CanDeleteCardinalitiesFromClassRequestV2( @@ -892,9 +892,9 @@ object DeleteCardinalitiesFromClassRequestV2 extends KnoraJsonLDRequestReaderV2[ featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM ): DeleteCardinalitiesFromClassRequestV2 = { - val inputOntology = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntology) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntology = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntology) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate DeleteCardinalitiesFromClassRequestV2( @@ -1047,9 +1047,9 @@ object ChangePropertyGuiElementRequest extends KnoraJsonLDRequestReaderV2[Change ): ChangePropertyGuiElementRequest = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) - val propertyInfoContent = propertyUpdateInfo.propertyInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) + val propertyInfoContent = propertyUpdateInfo.propertyInfoContent val lastModificationDate = propertyUpdateInfo.lastModificationDate val newGuiElement: Option[SmartIri] = @@ -1153,10 +1153,10 @@ object ChangePropertyLabelsOrCommentsRequestV2 featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM ): ChangePropertyLabelsOrCommentsRequestV2 = { - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) - val propertyInfoContent = propertyUpdateInfo.propertyInfoContent - val lastModificationDate = propertyUpdateInfo.lastModificationDate + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val propertyUpdateInfo = OntologyUpdateHelper.getPropertyDef(inputOntologiesV2) + val propertyInfoContent = propertyUpdateInfo.propertyInfoContent + val lastModificationDate = propertyUpdateInfo.lastModificationDate val predicateInfoToUpdate = OntologyUpdateHelper.getLabelsOrComments(propertyInfoContent) ChangePropertyLabelsOrCommentsRequestV2( @@ -1237,10 +1237,10 @@ object ChangeClassLabelsOrCommentsRequestV2 extends KnoraJsonLDRequestReaderV2[C featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM ): ChangeClassLabelsOrCommentsRequestV2 = { - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent - val lastModificationDate = classUpdateInfo.lastModificationDate + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent + val lastModificationDate = classUpdateInfo.lastModificationDate val predicateInfoToUpdate = OntologyUpdateHelper.getLabelsOrComments(classInfoContent) ChangeClassLabelsOrCommentsRequestV2( @@ -1293,9 +1293,9 @@ object ChangeGuiOrderRequestV2 extends KnoraJsonLDRequestReaderV2[ChangeGuiOrder ): ChangeGuiOrderRequestV2 = { // Get the class definition and the ontology's last modification date from the JSON-LD. - val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) - val classInfoContent = classUpdateInfo.classInfoContent + val inputOntologiesV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val classUpdateInfo = OntologyUpdateHelper.getClassDef(inputOntologiesV2) + val classInfoContent = classUpdateInfo.classInfoContent val lastModificationDate = classUpdateInfo.lastModificationDate // The request must provide cardinalities. @@ -1377,10 +1377,10 @@ object ChangeOntologyMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Change featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM ): ChangeOntologyMetadataRequestV2 = { - val inputOntologyV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) - val inputMetadata = inputOntologyV2.ontologyMetadata - val ontologyIri = inputMetadata.ontologyIri - val label: Option[String] = inputMetadata.label + val inputOntologyV2 = InputOntologyV2.fromJsonLD(jsonLDDocument) + val inputMetadata = inputOntologyV2.ontologyMetadata + val ontologyIri = inputMetadata.ontologyIri + val label: Option[String] = inputMetadata.label val comment: Option[String] = inputMetadata.comment val lastModificationDate = inputMetadata.lastModificationDate.getOrElse( throw BadRequestException("No knora-api:lastModificationDate submitted") @@ -1775,10 +1775,10 @@ case class ReadOntologyV2( val context = JsonLDUtil.makeContext( fixedPrefixes = Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> knoraApiPrefixExpansion, - "rdf" -> OntologyConstants.Rdf.RdfPrefixExpansion, - "rdfs" -> OntologyConstants.Rdfs.RdfsPrefixExpansion, - "owl" -> OntologyConstants.Owl.OwlPrefixExpansion, - "xsd" -> OntologyConstants.Xsd.XsdPrefixExpansion + "rdf" -> OntologyConstants.Rdf.RdfPrefixExpansion, + "rdfs" -> OntologyConstants.Rdfs.RdfsPrefixExpansion, + "owl" -> OntologyConstants.Owl.OwlPrefixExpansion, + "xsd" -> OntologyConstants.Xsd.XsdPrefixExpansion ) ++ salsahGuiPrefix, knoraOntologiesNeedingPrefixes = otherKnoraOntologiesUsed ) @@ -1813,7 +1813,7 @@ case class ReadOntologyV2( } }.toVector - val allEntities = jsonClasses ++ jsonProperties ++ jsonIndividuals + val allEntities = jsonClasses ++ jsonProperties ++ jsonIndividuals val allEntitiesSorted = allEntities.sortBy(_.value(JsonLDKeywords.ID)) // Assemble the JSON-LD document. @@ -2055,9 +2055,9 @@ case class ReadOntologyMetadataV2(ontologies: Set[OntologyMetadataV2]) val context = JsonLDObject( Map( OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> JsonLDString(knoraApiOntologyPrefixExpansion), - "xsd" -> JsonLDString(OntologyConstants.Xsd.XsdPrefixExpansion), - "rdfs" -> JsonLDString(OntologyConstants.Rdfs.RdfsPrefixExpansion), - "owl" -> JsonLDString(OntologyConstants.Owl.OwlPrefixExpansion) + "xsd" -> JsonLDString(OntologyConstants.Xsd.XsdPrefixExpansion), + "rdfs" -> JsonLDString(OntologyConstants.Rdfs.RdfsPrefixExpansion), + "owl" -> JsonLDString(OntologyConstants.Owl.OwlPrefixExpansion) ) ) @@ -2213,9 +2213,9 @@ object Cardinality extends Enumeration { type Cardinality = Value - val MayHaveOne: Value = Value(0, "0-1") - val MayHaveMany: Value = Value(1, "0-n") - val MustHaveOne: Value = Value(2, "1") + val MayHaveOne: Value = Value(0, "0-1") + val MayHaveMany: Value = Value(1, "0-n") + val MustHaveOne: Value = Value(2, "1") val MustHaveSome: Value = Value(3, "1-n") val valueMap: Map[String, Value] = values.map(v => (v.toString, v)).toMap @@ -2811,7 +2811,7 @@ case class ReadClassInfoV2( val knoraResourcePropertiesInTargetSchema = knoraResourcePropertiesConsideringLinkValueProps.map(_.toOntologySchema(targetSchema)) - val linkPropertiesInTargetSchema = linkProperties.map(_.toOntologySchema(targetSchema)) + val linkPropertiesInTargetSchema = linkProperties.map(_.toOntologySchema(targetSchema)) val linkValuePropertiesInTargetSchema = linkValuePropsForSchema.map(_.toOntologySchema(targetSchema)) val fileValuePropertiesInTargetSchema = fileValueProperties.map(_.toOntologySchema(targetSchema)) @@ -2857,7 +2857,7 @@ case class ReadClassInfoV2( val prop2card: (IRI, JsonLDInt) = cardinalityInfo.cardinality match { case Cardinality.MayHaveMany => OntologyConstants.Owl.MinCardinality -> JsonLDInt(0) case Cardinality.MayHaveOne => OntologyConstants.Owl.MaxCardinality -> JsonLDInt(1) - case Cardinality.MustHaveOne => OntologyConstants.Owl.Cardinality -> JsonLDInt(1) + case Cardinality.MustHaveOne => OntologyConstants.Owl.Cardinality -> JsonLDInt(1) case Cardinality.MustHaveSome => OntologyConstants.Owl.MinCardinality -> JsonLDInt(1) } @@ -2880,7 +2880,7 @@ case class ReadClassInfoV2( JsonLDObject( Map( - JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Restriction), + JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Restriction), OntologyConstants.Owl.OnProperty -> JsonLDUtil.iriToJsonLDObject(propertyIri.toString), prop2card ) ++ isInheritedStatement ++ guiOrderStatement @@ -2950,7 +2950,7 @@ case class ReadClassInfoV2( } Map( - JsonLDKeywords.ID -> JsonLDString(entityInfoContent.classIri.toString), + JsonLDKeywords.ID -> JsonLDString(entityInfoContent.classIri.toString), JsonLDKeywords.TYPE -> JsonLDArray(entityInfoContent.getRdfTypes.map(typeIri => JsonLDString(typeIri.toString))) ) ++ jsonSubClassOfStatement ++ resourceIconStatement ++ isKnoraResourceClassStatement ++ isStandoffClassStatement ++ canBeInstantiatedStatement ++ isValueClassStatement ++ jsonRestriction @@ -3088,7 +3088,7 @@ case class ReadPropertyInfoV2( } Map( - JsonLDKeywords.ID -> JsonLDString(entityInfoContent.propertyIri.toString), + JsonLDKeywords.ID -> JsonLDString(entityInfoContent.propertyIri.toString), JsonLDKeywords.TYPE -> JsonLDArray(entityInfoContent.getRdfTypes.map(typeIri => JsonLDString(typeIri.toString))) ) ++ jsonSubPropertyOfStatement ++ subjectTypeStatement ++ objectTypeStatement ++ isResourcePropStatement ++ isEditableStatement ++ isLinkValuePropertyStatement ++ @@ -3483,7 +3483,7 @@ case class PropertyInfoContentV2( if (sourcePropertyType.toString == OntologyConstants.Owl.ObjectProperty) { // Yes. See if we need to change it to a datatype property. Does it have a knora-base:objectClassConstraint? - val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri + val objectClassConstraintIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri val maybeObjectType: Option[SmartIri] = getPredicateIriObject(objectClassConstraintIri) maybeObjectType match { @@ -3845,7 +3845,7 @@ case class OntologyMetadataV2( } Map( - JsonLDKeywords.ID -> JsonLDString(ontologyIri.toString), + JsonLDKeywords.ID -> JsonLDString(ontologyIri.toString), JsonLDKeywords.TYPE -> JsonLDString(OntologyConstants.Owl.Ontology) ) ++ projectIriStatement ++ labelStatement ++ commentStatement ++ lastModDateStatement ++ isSharedStatement ++ isBuiltInStatement } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala index be4c2cc5fa..10531634d7 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/OntologyTransformationRules.scala @@ -24,62 +24,61 @@ import org.knora.webapi.messages.{OntologyConstants, SmartIri} import org.knora.webapi.{ApiV2Complex, ApiV2Schema, ApiV2Simple} /** - * A trait for objects that provide rules for converting an ontology from the internal schema to an external schema. - * * See also [[OntologyConstants.CorrespondingIris]]. - */ + * A trait for objects that provide rules for converting an ontology from the internal schema to an external schema. + * * See also [[OntologyConstants.CorrespondingIris]]. + */ trait OntologyTransformationRules { /** - * The metadata to be used for the transformed ontology. - */ + * The metadata to be used for the transformed ontology. + */ val ontologyMetadata: OntologyMetadataV2 /** - * Properties to remove from the ontology before converting it to the target schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Properties to remove from the ontology before converting it to the target schema. + * See also [[OntologyConstants.CorrespondingIris]]. + */ val internalPropertiesToRemove: Set[SmartIri] /** - * Classes to remove from the ontology before converting it to the target schema. - */ + * Classes to remove from the ontology before converting it to the target schema. + */ val internalClassesToRemove: Set[SmartIri] /** - * After the ontology has been converted to the target schema, these cardinalities must be - * added to the specified classes. - */ + * After the ontology has been converted to the target schema, these cardinalities must be + * added to the specified classes. + */ val externalCardinalitiesToAdd: Map[SmartIri, Map[SmartIri, KnoraCardinalityInfo]] /** - * Classes that need to be added to the ontology after converting it to the target schema. - */ + * Classes that need to be added to the ontology after converting it to the target schema. + */ val externalClassesToAdd: Map[SmartIri, ReadClassInfoV2] /** - * Properties that need to be added to the ontology after converting it to the target schema. - * See also [[OntologyConstants.CorrespondingIris]]. - */ + * Properties that need to be added to the ontology after converting it to the target schema. + * See also [[OntologyConstants.CorrespondingIris]]. + */ val externalPropertiesToAdd: Map[SmartIri, ReadPropertyInfoV2] } /** - * Finds the appropriate [[OntologyTransformationRules]] for an ontology and a target schema. - */ + * Finds the appropriate [[OntologyTransformationRules]] for an ontology and a target schema. + */ object OntologyTransformationRules { /** - * Given an ontology IRI and a target ontology schema, returns the [[OntologyTransformationRules]] describing how to - * convert the ontology to the target schema. - * - * @param ontologyIri the IRI of the ontology being transformed. - * @param targetSchema the target ontology schema. - * @return the appropriate [[OntologyTransformationRules]]. - */ - def getTransformationRules(ontologyIri: SmartIri, targetSchema: ApiV2Schema): OntologyTransformationRules = { + * Given an ontology IRI and a target ontology schema, returns the [[OntologyTransformationRules]] describing how to + * convert the ontology to the target schema. + * + * @param ontologyIri the IRI of the ontology being transformed. + * @param targetSchema the target ontology schema. + * @return the appropriate [[OntologyTransformationRules]]. + */ + def getTransformationRules(ontologyIri: SmartIri, targetSchema: ApiV2Schema): OntologyTransformationRules = targetSchema match { case ApiV2Simple => KnoraBaseToApiV2SimpleTransformationRules case ApiV2Complex => KnoraBaseToApiV2ComplexTransformationRules } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala index 908821f409..beee101360 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala @@ -50,110 +50,117 @@ import org.knora.webapi.util._ import scala.concurrent.{ExecutionContext, Future} /** - * An abstract trait for messages that can be sent to `ResourcesResponderV2`. - */ + * An abstract trait for messages that can be sent to `ResourcesResponderV2`. + */ sealed trait ResourcesResponderRequestV2 extends KnoraRequestV2 { /** - * The user that made the request. - */ + * The user that made the request. + */ def requestingUser: UserADM } /** - * Requests a description of a resource. A successful response will be a [[ReadResourcesSequenceV2]]. - * - * @param resourceIris the IRIs of the resources to be queried. - * @param propertyIri if defined, requests only the values of the specified explicit property. - * @param withDeleted if defined, returns a deleted resource or a deleted value. - * @param valueUuid if defined, requests only the value with the specified UUID. - * @param versionDate if defined, requests the state of the resources at the specified time in the past. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ResourcesGetRequestV2(resourceIris: Seq[IRI], - propertyIri: Option[SmartIri] = None, - valueUuid: Option[UUID] = None, - versionDate: Option[Instant] = None, - withDeleted: Boolean = false, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption] = Set.empty, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 + * Requests a description of a resource. A successful response will be a [[ReadResourcesSequenceV2]]. + * + * @param resourceIris the IRIs of the resources to be queried. + * @param propertyIri if defined, requests only the values of the specified explicit property. + * @param withDeleted if defined, returns a deleted resource or a deleted value. + * @param valueUuid if defined, requests only the value with the specified UUID. + * @param versionDate if defined, requests the state of the resources at the specified time in the past. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ResourcesGetRequestV2( + resourceIris: Seq[IRI], + propertyIri: Option[SmartIri] = None, + valueUuid: Option[UUID] = None, + versionDate: Option[Instant] = None, + withDeleted: Boolean = false, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption] = Set.empty, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 /** - * Requests a preview of one or more resources. A successful response will be a [[ReadResourcesSequenceV2]]. - * - * @param resourceIris the IRIs of the resources to obtain a preview for. - * @param withDeletedResource indicates if a preview of deleted resource should be returned. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ResourcesPreviewGetRequestV2(resourceIris: Seq[IRI], - withDeletedResource: Boolean = false, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 + * Requests a preview of one or more resources. A successful response will be a [[ReadResourcesSequenceV2]]. + * + * @param resourceIris the IRIs of the resources to obtain a preview for. + * @param withDeletedResource indicates if a preview of deleted resource should be returned. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ResourcesPreviewGetRequestV2( + resourceIris: Seq[IRI], + withDeletedResource: Boolean = false, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 /** - * Requests a IIIF manifest for the images that are `knora-base:isPartOf` the specified - * resource. - * - * @param resourceIri the resource IRI. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ResourceIIIFManifestGetRequestV2(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 + * Requests a IIIF manifest for the images that are `knora-base:isPartOf` the specified + * resource. + * + * @param resourceIri the resource IRI. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ResourceIIIFManifestGetRequestV2( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 /** - * Represents a IIIF manifest for the images that are `knora-base:isPartOf` the specified - * resource. - * - * @param manifest the IIIF manifest. - */ + * Represents a IIIF manifest for the images that are `knora-base:isPartOf` the specified + * resource. + * + * @param manifest the IIIF manifest. + */ case class ResourceIIIFManifestGetResponseV2(manifest: JsonLDDocument) extends KnoraJsonLDResponseV2 { - override protected def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = manifest + override protected def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = manifest } /** - * Requests the version history of the values of a resource. - * - * @param resourceIri the IRI of the resource. - * @param withDeletedResource indicates if the version history of deleted resources should be returned or not. - * @param startDate the start of the time period to return, inclusive. - * @param endDate the end of the time period to return, exclusive. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ResourceVersionHistoryGetRequestV2(resourceIri: IRI, - withDeletedResource: Boolean = false, - startDate: Option[Instant] = None, - endDate: Option[Instant] = None, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 + * Requests the version history of the values of a resource. + * + * @param resourceIri the IRI of the resource. + * @param withDeletedResource indicates if the version history of deleted resources should be returned or not. + * @param startDate the start of the time period to return, inclusive. + * @param endDate the end of the time period to return, exclusive. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ResourceVersionHistoryGetRequestV2( + resourceIri: IRI, + withDeletedResource: Boolean = false, + startDate: Option[Instant] = None, + endDate: Option[Instant] = None, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 /** - * Requests the full version history of a resource and its values as events. - * - * @param resourceIri the IRI of the resource. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ResourceHistoryEventsGetRequestV2(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 { + * Requests the full version history of a resource and its values as events. + * + * @param resourceIri the IRI of the resource. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ResourceHistoryEventsGetRequestV2( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeIri(resourceIri, throw BadRequestException(s"Invalid resource IRI: $resourceIri")) if (!stringFormatter.toSmartIri(resourceIri).isKnoraResourceIri) { @@ -162,16 +169,17 @@ case class ResourceHistoryEventsGetRequestV2(resourceIri: IRI, } /** - * Requests the version history of all resources of a project. - * - * @param projectIri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ProjectResourcesWithHistoryGetRequestV2(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 { + * Requests the version history of all resources of a project. + * + * @param projectIri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ProjectResourcesWithHistoryGetRequestV2( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 { private val stringFormatter = StringFormatter.getInstanceForConstantOntologies stringFormatter.validateAndEscapeIri(projectIri, throw BadRequestException(s"Invalid project IRI: $projectIri")) if (!stringFormatter.isKnoraProjectIriStr(projectIri)) { @@ -180,27 +188,29 @@ case class ProjectResourcesWithHistoryGetRequestV2(projectIri: IRI, } /** - * Represents an item in the version history of a resource. - * - * @param versionDate the date when the modification occurred. - * @param author the IRI of the user that made the modification. - */ + * Represents an item in the version history of a resource. + * + * @param versionDate the date when the modification occurred. + * @param author the IRI of the user that made the modification. + */ case class ResourceHistoryEntry(versionDate: Instant, author: IRI) /** - * Represents the version history of the values of a resource. - */ + * Represents the version history of the values of a resource. + */ case class ResourceVersionHistoryResponseV2(history: Seq[ResourceHistoryEntry]) extends KnoraJsonLDResponseV2 { /** - * Converts the response to a data structure that can be used to generate JSON-LD. - * - * @param targetSchema the Knora API schema to be used in the JSON-LD document. - * @return a [[JsonLDDocument]] representing the response. - */ - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + * Converts the response to a data structure that can be used to generate JSON-LD. + * + * @param targetSchema the Knora API schema to be used in the JSON-LD document. + * @return a [[JsonLDDocument]] representing the response. + */ + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance if (targetSchema != ApiV2Complex) { @@ -241,55 +251,56 @@ case class ResourceVersionHistoryResponseV2(history: Seq[ResourceHistoryEntry]) } /** - * Requests a resource as TEI/XML. A successful response will be a [[ResourceTEIGetResponseV2]]. - * - * @param resourceIri the IRI of the resource to be returned in TEI/XML. - * @param textProperty the property representing the text (to be converted to the body of a TEI document). - * @param mappingIri the IRI of the mapping to be used to convert from standoff to TEI/XML, if any. Otherwise the standard mapping is assumed. - * @param gravsearchTemplateIri the gravsearch template to query the metadata for the TEI header, if provided. - * @param headerXSLTIri the IRI of the XSL transformation to convert the resource's metadata to the TEI header. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class ResourceTEIGetRequestV2(resourceIri: IRI, - textProperty: SmartIri, - mappingIri: Option[IRI], - gravsearchTemplateIri: Option[IRI], - headerXSLTIri: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 + * Requests a resource as TEI/XML. A successful response will be a [[ResourceTEIGetResponseV2]]. + * + * @param resourceIri the IRI of the resource to be returned in TEI/XML. + * @param textProperty the property representing the text (to be converted to the body of a TEI document). + * @param mappingIri the IRI of the mapping to be used to convert from standoff to TEI/XML, if any. Otherwise the standard mapping is assumed. + * @param gravsearchTemplateIri the gravsearch template to query the metadata for the TEI header, if provided. + * @param headerXSLTIri the IRI of the XSL transformation to convert the resource's metadata to the TEI header. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class ResourceTEIGetRequestV2( + resourceIri: IRI, + textProperty: SmartIri, + mappingIri: Option[IRI], + gravsearchTemplateIri: Option[IRI], + headerXSLTIri: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends ResourcesResponderRequestV2 /** - * Represents a Knora resource as TEI/XML. - * - * @param header the header of the TEI document, if given. - * @param body the body of the TEI document. - */ + * Represents a Knora resource as TEI/XML. + * + * @param header the header of the TEI document, if given. + * @param body the body of the TEI document. + */ case class ResourceTEIGetResponseV2(header: TEIHeader, body: TEIBody) { - def toXML: String = { + def toXML: String = s""" - | - |${header.toXML} - |${body.toXML} - |""".stripMargin - } + | + |${header.toXML} + |${body.toXML} + |""".stripMargin } /** - * Represents information that is going to be contained in the header of a TEI/XML document. - * - * @param headerInfo the resource representing the header information. - * @param headerXSLT XSLT to be applied to the resource's metadata in RDF/XML. - * - */ -case class TEIHeader(headerInfo: ReadResourceV2, - headerXSLT: Option[String], - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl) { - - def toXML: String = { + * Represents information that is going to be contained in the header of a TEI/XML document. + * + * @param headerInfo the resource representing the header information. + * @param headerXSLT XSLT to be applied to the resource's metadata in RDF/XML. + */ +case class TEIHeader( + headerInfo: ReadResourceV2, + headerXSLT: Option[String], + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl +) { + + def toXML: String = if (headerXSLT.nonEmpty) { val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) @@ -312,35 +323,33 @@ case class TEIHeader(headerInfo: ReadResourceV2, XMLUtil.applyXSLTransformation(xml = teiXmlHeader, xslt = headerXSLT.get) } else { s""" - | - | - | - | ${headerInfo.label} - | - | - |

- | This is the TEI/XML representation of a resource identified by the Iri ${headerInfo.resourceIri}. - |

- | - | - |

Representation of the resource's text as TEI/XML

- |
- | - | + | + | + | + | ${headerInfo.label} + | + | + |

+ | This is the TEI/XML representation of a resource identified by the Iri ${headerInfo.resourceIri}. + |

+ |
+ | + |

Representation of the resource's text as TEI/XML

+ |
+ |
+ |
""".stripMargin } - } - } /** - * Represents the actual text that is going to be converted to the body of a TEI document. - * - * @param bodyInfo the content of the text value that will be converted to TEI. - * @param teiMapping the mapping from standoff to TEI/XML. - * @param bodyXSLT the XSLT transformation that completes the generation of TEI/XML. - */ + * Represents the actual text that is going to be converted to the body of a TEI document. + * + * @param bodyInfo the content of the text value that will be converted to TEI. + * @param teiMapping the mapping from standoff to TEI/XML. + * @param bodyXSLT the XSLT transformation that completes the generation of TEI/XML. + */ case class TEIBody(bodyInfo: TextValueContentV2, teiMapping: MappingXMLtoStandoff, bodyXSLT: String) { def toXML: String = { @@ -355,92 +364,93 @@ case class TEIBody(bodyInfo: TextValueContentV2, teiMapping: MappingXMLtoStandof } /** - * Represents a Knora resource. Any implementation of `ResourceV2` is API operation specific. - */ + * Represents a Knora resource. Any implementation of `ResourceV2` is API operation specific. + */ sealed trait ResourceV2 { /** - * The IRI of the resource class. - */ + * The IRI of the resource class. + */ def resourceClassIri: SmartIri /** - * The resource's `rdfs:label`. - */ + * The resource's `rdfs:label`. + */ def label: String /** - * A map of property IRIs to [[IOValueV2]] objects. - */ + * A map of property IRIs to [[IOValueV2]] objects. + */ def values: Map[SmartIri, Seq[IOValueV2]] } /** - * Represents a Knora resource when being read back from the triplestore. - * - * @param resourceIri the IRI of the resource. - * @param label the resource's label. - * @param resourceClassIri the class the resource belongs to. - * @param attachedToUser the user that created the resource. - * @param projectADM the project that the resource belongs to. - * @param permissions the permissions that the resource grants to user groups. - * @param userPermission the permission the the requesting user has on the resource. - * @param values a map of property IRIs to values. - * @param creationDate the date when this resource was created. - * @param lastModificationDate the date when this resource was last modified. - * @param versionDate if this is a past version of the resource, the date of the version. - * @param deletionInfo if this resource has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ -case class ReadResourceV2(resourceIri: IRI, - label: String, - resourceClassIri: SmartIri, - attachedToUser: IRI, - projectADM: ProjectADM, - permissions: String, - userPermission: EntityPermission, - values: Map[SmartIri, Seq[ReadValueV2]], - creationDate: Instant, - lastModificationDate: Option[Instant], - versionDate: Option[Instant], - deletionInfo: Option[DeletionInfo]) - extends ResourceV2 + * Represents a Knora resource when being read back from the triplestore. + * + * @param resourceIri the IRI of the resource. + * @param label the resource's label. + * @param resourceClassIri the class the resource belongs to. + * @param attachedToUser the user that created the resource. + * @param projectADM the project that the resource belongs to. + * @param permissions the permissions that the resource grants to user groups. + * @param userPermission the permission the the requesting user has on the resource. + * @param values a map of property IRIs to values. + * @param creationDate the date when this resource was created. + * @param lastModificationDate the date when this resource was last modified. + * @param versionDate if this is a past version of the resource, the date of the version. + * @param deletionInfo if this resource has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ +case class ReadResourceV2( + resourceIri: IRI, + label: String, + resourceClassIri: SmartIri, + attachedToUser: IRI, + projectADM: ProjectADM, + permissions: String, + userPermission: EntityPermission, + values: Map[SmartIri, Seq[ReadValueV2]], + creationDate: Instant, + lastModificationDate: Option[Instant], + versionDate: Option[Instant], + deletionInfo: Option[DeletionInfo] +) extends ResourceV2 with KnoraReadV2[ReadResourceV2] { - override def toOntologySchema(targetSchema: ApiV2Schema): ReadResourceV2 = { + override def toOntologySchema(targetSchema: ApiV2Schema): ReadResourceV2 = copy( resourceClassIri = resourceClassIri.toOntologySchema(targetSchema), - values = values.map { - case (propertyIri, readValues) => - val propertyIriInTargetSchema = propertyIri.toOntologySchema(targetSchema) - - // In the simple schema, use link properties instead of link value properties. - val adaptedPropertyIri = if (targetSchema == ApiV2Simple) { - // If all the property's values are link values, it's a link value property. - val isLinkProp = readValues.forall { readValue => - readValue.valueContent match { - case _: LinkValueContentV2 => true - case _ => false - } + values = values.map { case (propertyIri, readValues) => + val propertyIriInTargetSchema = propertyIri.toOntologySchema(targetSchema) + + // In the simple schema, use link properties instead of link value properties. + val adaptedPropertyIri = if (targetSchema == ApiV2Simple) { + // If all the property's values are link values, it's a link value property. + val isLinkProp = readValues.forall { readValue => + readValue.valueContent match { + case _: LinkValueContentV2 => true + case _ => false } + } - // If it's a link value property, use the corresponding link property. - if (isLinkProp) { - propertyIriInTargetSchema.fromLinkValuePropToLinkProp - } else { - propertyIriInTargetSchema - } + // If it's a link value property, use the corresponding link property. + if (isLinkProp) { + propertyIriInTargetSchema.fromLinkValuePropToLinkProp } else { propertyIriInTargetSchema } + } else { + propertyIriInTargetSchema + } - adaptedPropertyIri -> readValues.map(_.toOntologySchema(targetSchema)) + adaptedPropertyIri -> readValues.map(_.toOntologySchema(targetSchema)) } ) - } - def toJsonLD(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDObject = { + def toJsonLD( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDObject = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance if (!resourceClassIri.getOntologySchema.contains(targetSchema)) { @@ -540,101 +550,103 @@ case class ReadResourceV2(resourceIri: IRI, } /** - * The value of a Knora property sent to Knora to be created in a new resource. - * - * @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]]. - * @param customValueIri the optional custom value IRI. - * @param customValueUUID the optional custom value UUID. - * @param customValueCreationDate the optional custom value creation date. - * @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults. - */ -case class CreateValueInNewResourceV2(valueContent: ValueContentV2, - customValueIri: Option[SmartIri] = None, - customValueUUID: Option[UUID] = None, - customValueCreationDate: Option[Instant] = None, - permissions: Option[String] = None) - extends IOValueV2 + * The value of a Knora property sent to Knora to be created in a new resource. + * + * @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]]. + * @param customValueIri the optional custom value IRI. + * @param customValueUUID the optional custom value UUID. + * @param customValueCreationDate the optional custom value creation date. + * @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults. + */ +case class CreateValueInNewResourceV2( + valueContent: ValueContentV2, + customValueIri: Option[SmartIri] = None, + customValueUUID: Option[UUID] = None, + customValueCreationDate: Option[Instant] = None, + permissions: Option[String] = None +) extends IOValueV2 /** - * Represents a Knora resource to be created. - * - * @param resourceIri the IRI that should be given to the resource. - * @param resourceClassIri the class the resource belongs to. - * @param label the resource's label. - * @param values the resource's values. - * @param projectADM the project that the resource should belong to. - * @param permissions the permissions to be given to the new resource. If not provided, these will be taken from defaults. - * @param creationDate the optional creation date of the resource. - */ -case class CreateResourceV2(resourceIri: Option[SmartIri], - resourceClassIri: SmartIri, - label: String, - values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], - projectADM: ProjectADM, - permissions: Option[String] = None, - creationDate: Option[Instant] = None) - extends ResourceV2 { + * Represents a Knora resource to be created. + * + * @param resourceIri the IRI that should be given to the resource. + * @param resourceClassIri the class the resource belongs to. + * @param label the resource's label. + * @param values the resource's values. + * @param projectADM the project that the resource should belong to. + * @param permissions the permissions to be given to the new resource. If not provided, these will be taken from defaults. + * @param creationDate the optional creation date of the resource. + */ +case class CreateResourceV2( + resourceIri: Option[SmartIri], + resourceClassIri: SmartIri, + label: String, + values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], + projectADM: ProjectADM, + permissions: Option[String] = None, + creationDate: Option[Instant] = None +) extends ResourceV2 { lazy val flatValues: Iterable[CreateValueInNewResourceV2] = values.values.flatten /** - * Converts this [[CreateResourceV2]] to the specified ontology schema. - * - * @param targetSchema the target ontology schema. - * @return a copy of this [[CreateResourceV2]] in the specified ontology schema. - */ - def toOntologySchema(targetSchema: OntologySchema): CreateResourceV2 = { + * Converts this [[CreateResourceV2]] to the specified ontology schema. + * + * @param targetSchema the target ontology schema. + * @return a copy of this [[CreateResourceV2]] in the specified ontology schema. + */ + def toOntologySchema(targetSchema: OntologySchema): CreateResourceV2 = copy( resourceClassIri = resourceClassIri.toOntologySchema(targetSchema), - values = values.map { - case (propertyIri, valuesToCreate) => - propertyIri.toOntologySchema(targetSchema) -> valuesToCreate.map { valueToCreate => - valueToCreate.copy( - valueContent = valueToCreate.valueContent.toOntologySchema(targetSchema) - ) - } + values = values.map { case (propertyIri, valuesToCreate) => + propertyIri.toOntologySchema(targetSchema) -> valuesToCreate.map { valueToCreate => + valueToCreate.copy( + valueContent = valueToCreate.valueContent.toOntologySchema(targetSchema) + ) + } } ) - } } /** - * Represents a request to create a resource. - * - * @param createResource the resource to be created. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ -case class CreateResourceRequestV2(createResource: CreateResourceV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV2 + * Represents a request to create a resource. + * + * @param createResource the resource to be created. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ +case class CreateResourceRequestV2( + createResource: CreateResourceV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV2 object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResourceRequestV2] { /** - * Converts JSON-LD input to a [[CreateResourceRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[CreateResourceRequestV2] = { + * Converts JSON-LD input to a [[CreateResourceRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CreateResourceRequestV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -646,12 +658,16 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource maybeCustomResourceIri: Option[SmartIri] = jsonLDDocument.maybeIDAsKnoraDataIri // Get the resource's rdfs:label. - label: String = jsonLDDocument.requireStringWithValidation(OntologyConstants.Rdfs.Label, - stringFormatter.toSparqlEncodedString) + label: String = jsonLDDocument.requireStringWithValidation( + OntologyConstants.Rdfs.Label, + stringFormatter.toSparqlEncodedString + ) // Get information about the project that the resource should be created in. - projectIri: SmartIri = jsonLDDocument.requireIriInObject(OntologyConstants.KnoraApiV2Complex.AttachedToProject, - stringFormatter.toSmartIriWithErr) + projectIri: SmartIri = jsonLDDocument.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.AttachedToProject, + stringFormatter.toSmartIriWithErr + ) projectInfoResponse: ProjectGetResponseADM <- (responderManager ? ProjectGetRequestADM( identifier = ProjectIdentifierADM(maybeIri = Some(projectIri.toString)), @@ -672,13 +688,15 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource // Get the resource's permissions. permissions: Option[String] = jsonLDDocument.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.HasPermissions, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) // Get the user who should be indicated as the creator of the resource, if specified. maybeAttachedToUserIri: Option[SmartIri] = jsonLDDocument.maybeIriInObject( OntologyConstants.KnoraApiV2Complex.AttachedToUser, - stringFormatter.toSmartIriWithErr) + stringFormatter.toSmartIriWithErr + ) maybeAttachedToUserFuture: Option[Future[UserADM]] = maybeAttachedToUserIri.map { attachedToUserIri => UserUtilADM.switchToUser( @@ -737,7 +755,8 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource maybeCustomValueIri: Option[SmartIri] = valueJsonLDObject.maybeIDAsKnoraDataIri maybeCustomValueUUID: Option[UUID] = valueJsonLDObject.maybeUUID( - OntologyConstants.KnoraApiV2Complex.ValueHasUUID) + OntologyConstants.KnoraApiV2Complex.ValueHasUUID + ) // Get the value's creation date. // TODO: creationDate for values is a bug, and will not be supported in future. Use valueCreationDate instead. @@ -747,95 +766,98 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri, validationFun = stringFormatter.xsdDateTimeStampToInstant ) - .orElse(valueJsonLDObject.maybeDatatypeValueInObject( - key = OntologyConstants.KnoraApiV2Complex.CreationDate, - expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri, - validationFun = stringFormatter.xsdDateTimeStampToInstant - )) + .orElse( + valueJsonLDObject.maybeDatatypeValueInObject( + key = OntologyConstants.KnoraApiV2Complex.CreationDate, + expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri, + validationFun = stringFormatter.xsdDateTimeStampToInstant + ) + ) maybePermissions: Option[String] = valueJsonLDObject.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.HasPermissions, - stringFormatter.toSparqlEncodedString) - } yield - CreateValueInNewResourceV2( - valueContent = valueContent, - customValueIri = maybeCustomValueIri, - customValueUUID = maybeCustomValueUUID, - customValueCreationDate = maybeCustomValueCreationDate, - permissions = maybePermissions + stringFormatter.toSparqlEncodedString ) + } yield CreateValueInNewResourceV2( + valueContent = valueContent, + customValueIri = maybeCustomValueIri, + customValueUUID = maybeCustomValueUUID, + customValueCreationDate = maybeCustomValueCreationDate, + permissions = maybePermissions + ) } propertyIri -> valueFuturesSeq }.toMap propertyValuesMap: Map[SmartIri, Seq[CreateValueInNewResourceV2]] <- ActorUtil.sequenceSeqFuturesInMap( - propertyValueFuturesMap) - } yield - CreateResourceRequestV2( - createResource = CreateResourceV2( - resourceIri = maybeCustomResourceIri, - resourceClassIri = resourceClassIri, - label = label, - values = propertyValuesMap, - projectADM = projectInfoResponse.project, - permissions = permissions, - creationDate = creationDate - ), - featureFactoryConfig = featureFactoryConfig, - requestingUser = maybeAttachedToUser.getOrElse(requestingUser), - apiRequestID = apiRequestID + propertyValueFuturesMap ) + } yield CreateResourceRequestV2( + createResource = CreateResourceV2( + resourceIri = maybeCustomResourceIri, + resourceClassIri = resourceClassIri, + label = label, + values = propertyValuesMap, + projectADM = projectInfoResponse.project, + permissions = permissions, + creationDate = creationDate + ), + featureFactoryConfig = featureFactoryConfig, + requestingUser = maybeAttachedToUser.getOrElse(requestingUser), + apiRequestID = apiRequestID + ) } } /** - * Represents a request to update a resource's metadata. - * - * @param resourceIri the IRI of the resource. - * @param resourceClassIri the IRI of the resource class. - * @param maybeLastModificationDate the resource's last modification date, if any. - * @param maybeLabel the resource's new `rdfs:label`, if any. - * @param maybePermissions the resource's new permissions, if any. - * @param maybeNewModificationDate the resource's new last modification date, if any. - * @param featureFactoryConfig the feature factory configuration. - */ -case class UpdateResourceMetadataRequestV2(resourceIri: IRI, - resourceClassIri: SmartIri, - maybeLastModificationDate: Option[Instant] = None, - maybeLabel: Option[String] = None, - maybePermissions: Option[String] = None, - maybeNewModificationDate: Option[Instant] = None, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV2 + * Represents a request to update a resource's metadata. + * + * @param resourceIri the IRI of the resource. + * @param resourceClassIri the IRI of the resource class. + * @param maybeLastModificationDate the resource's last modification date, if any. + * @param maybeLabel the resource's new `rdfs:label`, if any. + * @param maybePermissions the resource's new permissions, if any. + * @param maybeNewModificationDate the resource's new last modification date, if any. + * @param featureFactoryConfig the feature factory configuration. + */ +case class UpdateResourceMetadataRequestV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + maybeLastModificationDate: Option[Instant] = None, + maybeLabel: Option[String] = None, + maybePermissions: Option[String] = None, + maybeNewModificationDate: Option[Instant] = None, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV2 object UpdateResourceMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateResourceMetadataRequestV2] { /** - * Converts JSON-LD input into an instance of [[UpdateResourceMetadataRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[UpdateResourceMetadataRequestV2] = { + * Converts JSON-LD input into an instance of [[UpdateResourceMetadataRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[UpdateResourceMetadataRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -844,12 +866,13 @@ object UpdateResourceMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Update apiRequestID = apiRequestID ) } - } - def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): UpdateResourceMetadataRequestV2 = { + def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): UpdateResourceMetadataRequestV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val resourceIri: SmartIri = jsonLDDocument.requireIDAsKnoraDataIri @@ -870,7 +893,8 @@ object UpdateResourceMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Update jsonLDDocument.maybeStringWithValidation(OntologyConstants.Rdfs.Label, stringFormatter.toSparqlEncodedString) val maybePermissions: Option[String] = jsonLDDocument.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.HasPermissions, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) val maybeNewModificationDate: Option[Instant] = jsonLDDocument.maybeDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.NewModificationDate, @@ -897,32 +921,35 @@ object UpdateResourceMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Update } /** - * Represents a response after updating a resource's metadata. - * - * @param resourceIri the IRI of the resource. - * @param resourceClassIri the IRI of the resource class. - * @param lastModificationDate the resource's last modification date. - * @param maybeLabel the resource's new `rdfs:label`, if any. - * @param maybePermissions the resource's new permissions, if any. - * @param featureFactoryConfig the feature factory configuration. - */ -case class UpdateResourceMetadataResponseV2(resourceIri: IRI, - resourceClassIri: SmartIri, - lastModificationDate: Instant, - maybeLabel: Option[String] = None, - maybePermissions: Option[String] = None, - featureFactoryConfig: FeatureFactoryConfig) - extends KnoraJsonLDResponseV2 { + * Represents a response after updating a resource's metadata. + * + * @param resourceIri the IRI of the resource. + * @param resourceClassIri the IRI of the resource class. + * @param lastModificationDate the resource's last modification date. + * @param maybeLabel the resource's new `rdfs:label`, if any. + * @param maybePermissions the resource's new permissions, if any. + * @param featureFactoryConfig the feature factory configuration. + */ +case class UpdateResourceMetadataResponseV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + lastModificationDate: Instant, + maybeLabel: Option[String] = None, + maybePermissions: Option[String] = None, + featureFactoryConfig: FeatureFactoryConfig +) extends KnoraJsonLDResponseV2 { /** - * Converts the response to a data structure that can be used to generate JSON-LD. - * - * @param targetSchema the Knora API schema to be used in the JSON-LD document. - * @return a [[JsonLDDocument]] representing the response. - */ - override protected def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + * Converts the response to a data structure that can be used to generate JSON-LD. + * + * @param targetSchema the Knora API schema to be used in the JSON-LD document. + * @return a [[JsonLDDocument]] representing the response. + */ + override protected def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -954,7 +981,8 @@ case class UpdateResourceMetadataResponseV2(resourceIri: IRI, OntologyConstants.KnoraApiV2Complex.LastModificationDate -> JsonLDUtil.datatypeValueToJsonLDObject( value = lastModificationDate.toString, datatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri - )) + ) + ) val permissions_map = maybePermissions match { case Some(maybePermissions) => @@ -969,7 +997,8 @@ case class UpdateResourceMetadataResponseV2(resourceIri: IRI, ) val body = JsonLDObject( - resourceIri_resourceClassIri_map ++ label_map ++ lastModificationDate_map ++ permissions_map) + resourceIri_resourceClassIri_map ++ label_map ++ lastModificationDate_map ++ permissions_map + ) JsonLDDocument(body = body, context = context) @@ -977,53 +1006,54 @@ case class UpdateResourceMetadataResponseV2(resourceIri: IRI, } /** - * Represents a request to mark a resource as deleted or to erase it from the triplestore. - * - * @param resourceIri the IRI of the resource. - * @param resourceClassIri the IRI of the resource class. - * @param maybeDeleteComment a comment explaining why the resource is being marked as deleted. - * @param maybeDeleteDate a timestamp indicating when the resource was marked as deleted. If not supplied, - * the current time will be used. - * @param maybeLastModificationDate the resource's last modification date, if any. - * @param erase if `true`, the resource will be erased from the triplestore, otherwise it will be marked as deleted. - * @param featureFactoryConfig the feature factory configuration. - */ -case class DeleteOrEraseResourceRequestV2(resourceIri: IRI, - resourceClassIri: SmartIri, - maybeDeleteComment: Option[String] = None, - maybeDeleteDate: Option[Instant] = None, - maybeLastModificationDate: Option[Instant] = None, - erase: Boolean = false, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ResourcesResponderRequestV2 + * Represents a request to mark a resource as deleted or to erase it from the triplestore. + * + * @param resourceIri the IRI of the resource. + * @param resourceClassIri the IRI of the resource class. + * @param maybeDeleteComment a comment explaining why the resource is being marked as deleted. + * @param maybeDeleteDate a timestamp indicating when the resource was marked as deleted. If not supplied, + * the current time will be used. + * @param maybeLastModificationDate the resource's last modification date, if any. + * @param erase if `true`, the resource will be erased from the triplestore, otherwise it will be marked as deleted. + * @param featureFactoryConfig the feature factory configuration. + */ +case class DeleteOrEraseResourceRequestV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + maybeDeleteComment: Option[String] = None, + maybeDeleteDate: Option[Instant] = None, + maybeLastModificationDate: Option[Instant] = None, + erase: Boolean = false, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ResourcesResponderRequestV2 object DeleteOrEraseResourceRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteOrEraseResourceRequestV2] { /** - * Converts JSON-LD input into an instance of [[DeleteOrEraseResourceRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[DeleteOrEraseResourceRequestV2] = { + * Converts JSON-LD input into an instance of [[DeleteOrEraseResourceRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[DeleteOrEraseResourceRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -1032,12 +1062,13 @@ object DeleteOrEraseResourceRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteO apiRequestID = apiRequestID ) } - } - def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): DeleteOrEraseResourceRequestV2 = { + def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): DeleteOrEraseResourceRequestV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val resourceIri: SmartIri = jsonLDDocument.requireIDAsKnoraDataIri @@ -1056,7 +1087,8 @@ object DeleteOrEraseResourceRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteO val maybeDeleteComment: Option[String] = jsonLDDocument.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.DeleteComment, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) val maybeDeleteDate: Option[Instant] = jsonLDDocument.maybeDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.DeleteDate, @@ -1078,43 +1110,41 @@ object DeleteOrEraseResourceRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteO } /** - * Represents a sequence of resources read back from Knora. - * - * @param resources a sequence of resources that the user has permission to see. - * @param hiddenResourceIris the IRIs of resources that were requested but that the user did not have permission to see. - * @param mayHaveMoreResults `true` if more resources matching the request may be available. - */ -case class ReadResourcesSequenceV2(resources: Seq[ReadResourceV2], - hiddenResourceIris: Set[IRI] = Set.empty, - mayHaveMoreResults: Boolean = false) - extends KnoraJsonLDResponseV2 + * Represents a sequence of resources read back from Knora. + * + * @param resources a sequence of resources that the user has permission to see. + * @param hiddenResourceIris the IRIs of resources that were requested but that the user did not have permission to see. + * @param mayHaveMoreResults `true` if more resources matching the request may be available. + */ +case class ReadResourcesSequenceV2( + resources: Seq[ReadResourceV2], + hiddenResourceIris: Set[IRI] = Set.empty, + mayHaveMoreResults: Boolean = false +) extends KnoraJsonLDResponseV2 with KnoraReadV2[ReadResourcesSequenceV2] with UpdateResultInProject { - override def toOntologySchema(targetSchema: ApiV2Schema): ReadResourcesSequenceV2 = { + override def toOntologySchema(targetSchema: ApiV2Schema): ReadResourcesSequenceV2 = copy( resources = resources.map(_.toOntologySchema(targetSchema)) ) - } private def getOntologiesFromResource(resource: ReadResourceV2): Set[SmartIri] = { val propertyIriOntologies: Set[SmartIri] = resource.values.keySet.map(_.getOntologyFromEntity) - val valueOntologies: Set[SmartIri] = resource.values.values.flatten - .collect { - case readLinkValueV2: ReadLinkValueV2 => - readLinkValueV2.valueContent.nestedResource.map(nested => getOntologiesFromResource(nested)) - } - .flatten - .flatten - .toSet + val valueOntologies: Set[SmartIri] = resource.values.values.flatten.collect { + case readLinkValueV2: ReadLinkValueV2 => + readLinkValueV2.valueContent.nestedResource.map(nested => getOntologiesFromResource(nested)) + }.flatten.flatten.toSet propertyIriOntologies ++ valueOntologies + resource.resourceClassIri.getOntologyFromEntity } - private def generateJsonLD(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + private def generateJsonLD( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Generate JSON-LD for the resources. @@ -1129,11 +1159,9 @@ case class ReadResourcesSequenceV2(resources: Seq[ReadResourceV2], // Make JSON-LD prefixes for the project-specific ontologies used in the response. - val projectSpecificOntologiesUsed: Set[SmartIri] = resources - .flatMap { resource => - getOntologiesFromResource(resource) - } - .toSet + val projectSpecificOntologiesUsed: Set[SmartIri] = resources.flatMap { resource => + getOntologiesFromResource(resource) + }.toSet .filter(!_.isKnoraBuiltInDefinitionIri) // Make the knora-api prefix for the target schema. @@ -1176,25 +1204,26 @@ case class ReadResourcesSequenceV2(resources: Seq[ReadResourceV2], } - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption] = Set.empty): JsonLDDocument = { + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] = Set.empty + ): JsonLDDocument = toOntologySchema(targetSchema).generateJsonLD( targetSchema = targetSchema, settings = settings, schemaOptions = schemaOptions ) - } /** - * Checks that a [[ReadResourcesSequenceV2]] contains exactly one resource, and returns that resource. - * - * @param requestedResourceIri the IRI of the expected resource. - * @return the resource. - * @throws NotFoundException if the resource is not found. - * @throws ForbiddenException if the user does not have permission to see the requested resource. - * @throws BadRequestException if more than one resource was returned. - */ + * Checks that a [[ReadResourcesSequenceV2]] contains exactly one resource, and returns that resource. + * + * @param requestedResourceIri the IRI of the expected resource. + * @return the resource. + * @throws NotFoundException if the resource is not found. + * @throws ForbiddenException if the user does not have permission to see the requested resource. + * @throws BadRequestException if more than one resource was returned. + */ def toResource(requestedResourceIri: IRI)(implicit stringFormatter: StringFormatter): ReadResourceV2 = { if (hiddenResourceIris.contains(requestedResourceIri)) { throw ForbiddenException(s"You do not have permission to see resource <$requestedResourceIri>") @@ -1210,42 +1239,45 @@ case class ReadResourcesSequenceV2(resources: Seq[ReadResourceV2], if (resources.head.resourceIri != requestedResourceIri) { throw NotFoundException( - s"Expected resource <$requestedResourceIri>, but <${resources.head.resourceIri}> was returned") + s"Expected resource <$requestedResourceIri>, but <${resources.head.resourceIri}> was returned" + ) } resources.head } /** - * Checks that requested resources were found and that the user has permission to see them. If not, throws an exception. - * - * @param targetResourceIris the IRIs to be checked. - * @param resourcesSequence the result of requesting those IRIs. - * @throws NotFoundException if the requested resources are not found. - * @throws ForbiddenException if the user does not have permission to see the requested resources. - */ + * Checks that requested resources were found and that the user has permission to see them. If not, throws an exception. + * + * @param targetResourceIris the IRIs to be checked. + * @param resourcesSequence the result of requesting those IRIs. + * @throws NotFoundException if the requested resources are not found. + * @throws ForbiddenException if the user does not have permission to see the requested resources. + */ def checkResourceIris(targetResourceIris: Set[IRI], resourcesSequence: ReadResourcesSequenceV2): Unit = { val hiddenTargetResourceIris: Set[IRI] = targetResourceIris.intersect(resourcesSequence.hiddenResourceIris) if (hiddenTargetResourceIris.nonEmpty) { throw ForbiddenException( - s"You do not have permission to see one or more resources: ${hiddenTargetResourceIris.map(iri => s"<$iri>").mkString(", ")}") + s"You do not have permission to see one or more resources: ${hiddenTargetResourceIris.map(iri => s"<$iri>").mkString(", ")}" + ) } val missingResourceIris: Set[IRI] = targetResourceIris -- resourcesSequence.resources.map(_.resourceIri).toSet if (missingResourceIris.nonEmpty) { throw NotFoundException( - s"One or more resources were not found: ${missingResourceIris.map(iri => s"<$iri>").mkString(", ")}") + s"One or more resources were not found: ${missingResourceIris.map(iri => s"<$iri>").mkString(", ")}" + ) } } /** - * Considers this [[ReadResourcesSequenceV2]] to be the result of an update operation in a single project - * (since Knora never updates resources in more than one project at a time), and returns information about that - * project. Throws [[AssertionException]] if this [[ReadResourcesSequenceV2]] is empty or refers to more than one - * project. - */ + * Considers this [[ReadResourcesSequenceV2]] to be the result of an update operation in a single project + * (since Knora never updates resources in more than one project at a time), and returns information about that + * project. Throws [[AssertionException]] if this [[ReadResourcesSequenceV2]] is empty or refers to more than one + * project. + */ override def projectADM: ProjectADM = { if (resources.isEmpty) { throw AssertionException("ReadResourcesSequenceV2 is empty") @@ -1262,61 +1294,60 @@ case class ReadResourcesSequenceV2(resources: Seq[ReadResourceV2], } /** - * Requests a graph of resources that are reachable via links to or from a given resource. A successful response - * will be a [[GraphDataGetResponseV2]]. - * - * @param resourceIri the IRI of the initial resource. - * @param depth the maximum depth of the graph, counting from the initial resource. - * @param inbound `true` to query inbound links. - * @param outbound `true` to query outbound links. - * @param excludeProperty the IRI of a link property to exclude from the results. - * @param requestingUser the user making the request. - */ -case class GraphDataGetRequestV2(resourceIri: IRI, - depth: Int, - inbound: Boolean, - outbound: Boolean, - excludeProperty: Option[SmartIri], - requestingUser: UserADM) - extends ResourcesResponderRequestV2 { + * Requests a graph of resources that are reachable via links to or from a given resource. A successful response + * will be a [[GraphDataGetResponseV2]]. + * + * @param resourceIri the IRI of the initial resource. + * @param depth the maximum depth of the graph, counting from the initial resource. + * @param inbound `true` to query inbound links. + * @param outbound `true` to query outbound links. + * @param excludeProperty the IRI of a link property to exclude from the results. + * @param requestingUser the user making the request. + */ +case class GraphDataGetRequestV2( + resourceIri: IRI, + depth: Int, + inbound: Boolean, + outbound: Boolean, + excludeProperty: Option[SmartIri], + requestingUser: UserADM +) extends ResourcesResponderRequestV2 { if (!(inbound || outbound)) { throw BadRequestException("No link direction selected") } } /** - * Represents a node (i.e. a resource) in a resource graph. - * - * @param resourceIri the IRI of the resource. - * @param resourceLabel the label of the resource. - * @param resourceClassIri the IRI of the resource's OWL class. - */ + * Represents a node (i.e. a resource) in a resource graph. + * + * @param resourceIri the IRI of the resource. + * @param resourceLabel the label of the resource. + * @param resourceClassIri the IRI of the resource's OWL class. + */ case class GraphNodeV2(resourceIri: IRI, resourceClassIri: SmartIri, resourceLabel: String) extends KnoraReadV2[GraphNodeV2] { - override def toOntologySchema(targetSchema: ApiV2Schema): GraphNodeV2 = { + override def toOntologySchema(targetSchema: ApiV2Schema): GraphNodeV2 = copy(resourceClassIri = resourceClassIri.toOntologySchema(targetSchema)) - } } /** - * Represents an edge (i.e. a link) in a resource graph. - * - * @param source the resource that is the source of the link. - * @param propertyIri the link property that links the source to the target. - * @param target the resource that is the target of the link. - */ + * Represents an edge (i.e. a link) in a resource graph. + * + * @param source the resource that is the source of the link. + * @param propertyIri the link property that links the source to the target. + * @param target the resource that is the target of the link. + */ case class GraphEdgeV2(source: IRI, propertyIri: SmartIri, target: IRI) extends KnoraReadV2[GraphEdgeV2] { - override def toOntologySchema(targetSchema: ApiV2Schema): GraphEdgeV2 = { + override def toOntologySchema(targetSchema: ApiV2Schema): GraphEdgeV2 = copy(propertyIri = propertyIri.toOntologySchema(targetSchema)) - } } /** - * Represents a graph of resources. - * - * @param nodes the nodes in the graph. - * @param edges the edges in the graph. - */ + * Represents a graph of resources. + * + * @param nodes the nodes in the graph. + * @param edges the edges in the graph. + */ case class GraphDataGetResponseV2(nodes: Seq[GraphNodeV2], edges: Seq[GraphEdgeV2], ontologySchema: OntologySchema) extends KnoraJsonLDResponseV2 with KnoraReadV2[GraphDataGetResponseV2] { @@ -1379,7 +1410,8 @@ case class GraphDataGetResponseV2(nodes: Seq[GraphNodeV2], edges: Seq[GraphEdgeV case (propertyIri: SmartIri, propertyEdges: Seq[GraphEdgeV2]) => val sortedPropertyEdges = propertyEdges.sortBy(_.target) propertyIri.toString -> JsonLDArray( - sortedPropertyEdges.map(propertyEdge => JsonLDUtil.iriToJsonLDObject(propertyEdge.target))) + sortedPropertyEdges.map(propertyEdge => JsonLDUtil.iriToJsonLDObject(propertyEdge.target)) + ) }.toMap messages.util.rdf.JsonLDObject(jsonLDNodeMap ++ jsonLDNodeEdges) @@ -1397,63 +1429,68 @@ case class GraphDataGetResponseV2(nodes: Seq[GraphNodeV2], edges: Seq[GraphEdgeV JsonLDDocument(body = body, context = context) } - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = toOntologySchema(targetSchema).generateJsonLD(targetSchema, settings) - } - override def toOntologySchema(targetSchema: ApiV2Schema): GraphDataGetResponseV2 = { + override def toOntologySchema(targetSchema: ApiV2Schema): GraphDataGetResponseV2 = GraphDataGetResponseV2( nodes = nodes.map(_.toOntologySchema(targetSchema)), edges = edges.map(_.toOntologySchema(targetSchema)), ontologySchema = targetSchema ) - } } /** - * Represents the version history of a resource or a value as events. - * - * @param eventType the type of the operation that is one of [[ResourceAndValueEventsUtil]] - * @param versionDate the version date of the event. - * @param author the user which had performed the operation. - * @param eventBody the request body in the form of [[ResourceOrValueEventBody]] needed for the operation indicated - * by eventType. - */ -case class ResourceAndValueHistoryEvent(eventType: String, - versionDate: Instant, - author: IRI, - eventBody: ResourceOrValueEventBody) + * Represents the version history of a resource or a value as events. + * + * @param eventType the type of the operation that is one of [[ResourceAndValueEventsUtil]] + * @param versionDate the version date of the event. + * @param author the user which had performed the operation. + * @param eventBody the request body in the form of [[ResourceOrValueEventBody]] needed for the operation indicated + * by eventType. + */ +case class ResourceAndValueHistoryEvent( + eventType: String, + versionDate: Instant, + author: IRI, + eventBody: ResourceOrValueEventBody +) abstract class ResourceOrValueEventBody /** - * Represents a resource event (create or delete) body with all the information required for the request body of this operation. - * @param resourceIri the IRI of the resource. - * @param resourceClassIri the class of the resource. - * @param label the label of the resource. - * @param values the values of the resource at creation time. - * @param permissions the permissions assigned to the new resource. - * @param lastModificationDate the last modification date of the resource. - * @param creationDate the creation date of the resource. - * @param deletionInfo the deletion info of the resource. - * @param projectADM the project which the resource belongs to. - */ -case class ResourceEventBody(resourceIri: IRI, - resourceClassIri: SmartIri, - label: Option[String] = None, - values: Map[SmartIri, Seq[ValueContentV2]] = Map.empty[SmartIri, Seq[ValueContentV2]], - permissions: Option[String] = None, - lastModificationDate: Option[Instant] = None, - creationDate: Option[Instant] = None, - deletionInfo: Option[DeletionInfo] = None, - projectADM: ProjectADM) - extends ResourceOrValueEventBody { - - def toJsonLD(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDObject = { + * Represents a resource event (create or delete) body with all the information required for the request body of this operation. + * @param resourceIri the IRI of the resource. + * @param resourceClassIri the class of the resource. + * @param label the label of the resource. + * @param values the values of the resource at creation time. + * @param permissions the permissions assigned to the new resource. + * @param lastModificationDate the last modification date of the resource. + * @param creationDate the creation date of the resource. + * @param deletionInfo the deletion info of the resource. + * @param projectADM the project which the resource belongs to. + */ +case class ResourceEventBody( + resourceIri: IRI, + resourceClassIri: SmartIri, + label: Option[String] = None, + values: Map[SmartIri, Seq[ValueContentV2]] = Map.empty[SmartIri, Seq[ValueContentV2]], + permissions: Option[String] = None, + lastModificationDate: Option[Instant] = None, + creationDate: Option[Instant] = None, + deletionInfo: Option[DeletionInfo] = None, + projectADM: ProjectADM +) extends ResourceOrValueEventBody { + + def toJsonLD( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDObject = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val propertiesAndValuesAsJsonLD: Map[IRI, JsonLDArray] = values.map { @@ -1509,20 +1546,20 @@ case class ResourceEventBody(resourceIri: IRI, } /** - * Represents an update resource Metadata event body with all the information required for the request body of this operation. - * The version history of metadata changes are not kept, however every time metadata of a resource has changed, its lastModificationDate - * is updated accordingly. An event is thus necessary to update the last modification date of the resource. - * @param resourceIri the IRI of the resource. - * @param resourceClassIri the class of the resource. - * @param lastModificationDate the last modification date of the resource. - * @param newModificationDate the new modification date of the resource. - - */ -case class ResourceMetadataEventBody(resourceIri: IRI, - resourceClassIri: SmartIri, - lastModificationDate: Instant, - newModificationDate: Instant) - extends ResourceOrValueEventBody { + * Represents an update resource Metadata event body with all the information required for the request body of this operation. + * The version history of metadata changes are not kept, however every time metadata of a resource has changed, its lastModificationDate + * is updated accordingly. An event is thus necessary to update the last modification date of the resource. + * @param resourceIri the IRI of the resource. + * @param resourceClassIri the class of the resource. + * @param lastModificationDate the last modification date of the resource. + * @param newModificationDate the new modification date of the resource. + */ +case class ResourceMetadataEventBody( + resourceIri: IRI, + resourceClassIri: SmartIri, + lastModificationDate: Instant, + newModificationDate: Instant +) extends ResourceOrValueEventBody { def toJsonLD: JsonLDObject = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -1545,40 +1582,43 @@ case class ResourceMetadataEventBody(resourceIri: IRI, } /** - * Represents a value event (create/update content/update permission/delete) body with all the information required for - * the request body of the operation. - * @param resourceIri the IRI of the resource. - * @param resourceClassIri the class of the resource. - * @param projectADM the project which the resource belongs to. - * @param propertyIri the IRI of the property. - * @param valueIri the IRI of the value. - * @param valueTypeIri the type of the value. - * @param valueContent the content of the value. - * @param valueUUID the UUID of the value. - * @param valueCreationDate the creation date of the value. - * @param previousValueIri in the case of update value/ delete value operation, this indicates the previous value IRI. - * @param permissions the permissions assigned to the value. - * @param valueComment the comment given for the value operation. - * @param deletionInfo in case of delete value operation, it contains the date of deletion and the given comment. - */ -case class ValueEventBody(resourceIri: IRI, - resourceClassIri: SmartIri, - projectADM: ProjectADM, - propertyIri: SmartIri, - valueIri: IRI, - valueTypeIri: SmartIri, - valueContent: Option[ValueContentV2] = None, - valueUUID: Option[UUID] = None, - valueCreationDate: Option[Instant] = None, - previousValueIri: Option[IRI] = None, - permissions: Option[String] = None, - valueComment: Option[String] = None, - deletionInfo: Option[DeletionInfo] = None) - extends ResourceOrValueEventBody { - - def toJsonLD(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDObject = { + * Represents a value event (create/update content/update permission/delete) body with all the information required for + * the request body of the operation. + * @param resourceIri the IRI of the resource. + * @param resourceClassIri the class of the resource. + * @param projectADM the project which the resource belongs to. + * @param propertyIri the IRI of the property. + * @param valueIri the IRI of the value. + * @param valueTypeIri the type of the value. + * @param valueContent the content of the value. + * @param valueUUID the UUID of the value. + * @param valueCreationDate the creation date of the value. + * @param previousValueIri in the case of update value/ delete value operation, this indicates the previous value IRI. + * @param permissions the permissions assigned to the value. + * @param valueComment the comment given for the value operation. + * @param deletionInfo in case of delete value operation, it contains the date of deletion and the given comment. + */ +case class ValueEventBody( + resourceIri: IRI, + resourceClassIri: SmartIri, + projectADM: ProjectADM, + propertyIri: SmartIri, + valueIri: IRI, + valueTypeIri: SmartIri, + valueContent: Option[ValueContentV2] = None, + valueUUID: Option[UUID] = None, + valueCreationDate: Option[Instant] = None, + previousValueIri: Option[IRI] = None, + permissions: Option[String] = None, + valueComment: Option[String] = None, + deletionInfo: Option[DeletionInfo] = None +) extends ResourceOrValueEventBody { + + def toJsonLD( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDObject = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val contentAsJsonLD: Option[(String, JsonLDValue)] = valueContent.map { content => @@ -1624,7 +1664,7 @@ case class ValueEventBody(resourceIri: IRI, JsonLDKeywords.TYPE -> JsonLDString(valueTypeIri.toString), OntologyConstants.KnoraApiV2Complex.ResourceIri -> JsonLDString(resourceIri), OntologyConstants.KnoraApiV2Complex.ResourceClassIri -> JsonLDString(resourceClassIri.toString), - OntologyConstants.Rdf.Property -> JsonLDString(propertyIri.toString), + OntologyConstants.Rdf.Property -> JsonLDString(propertyIri.toString) ) ++ previousValueAsJsonLD ++ contentAsJsonLD ++ valueUUIDAsJsonLD ++ valueCreationDateAsJsonLD ++ valuePermissionsAsJSONLD ++ deletionInfoAsJsonLD ++ valueHasCommentAsJsonLD ) @@ -1632,20 +1672,22 @@ case class ValueEventBody(resourceIri: IRI, } /** - * Represents the resource and value history events. - */ + * Represents the resource and value history events. + */ case class ResourceAndValueVersionHistoryResponseV2(historyEvents: Seq[ResourceAndValueHistoryEvent]) extends KnoraJsonLDResponseV2 { /** - * Converts the response to a data structure that can be used to generate JSON-LD. - * - * @param targetSchema the Knora API schema to be used in the JSON-LD document. - * @return a [[JsonLDDocument]] representing the response. - */ - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + * Converts the response to a data structure that can be used to generate JSON-LD. + * + * @param targetSchema the Knora API schema to be used in the JSON-LD document. + * @return a [[JsonLDDocument]] representing the response. + */ + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance if (targetSchema != ApiV2Complex) { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala index 7473e31dfb..b311e948c9 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala @@ -1,8 +1,8 @@ package org.knora.webapi.messages.v2.responder.resourcemessages /** - * Contains string constants for resource and value event types. - */ + * Contains string constants for resource and value event types. + */ object ResourceAndValueEventsUtil { val CREATE_RESOURCE_EVENT = "createdResource" diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/searchmessages/SearchMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/searchmessages/SearchMessagesV2.scala index cbef95eb0e..93952ba11b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/searchmessages/SearchMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/searchmessages/SearchMessagesV2.scala @@ -29,159 +29,167 @@ import org.knora.webapi.settings.KnoraSettingsImpl import org.knora.webapi.{ApiV2Schema, IRI, SchemaOption} /** - * An abstract trait for messages that can be sent to `SearchResponderV2`. - */ + * An abstract trait for messages that can be sent to `SearchResponderV2`. + */ sealed trait SearchResponderRequestV2 extends KnoraRequestV2 { def requestingUser: UserADM } /** - * Requests the amount of results (resources count) of a given fulltext search. A successful response will be a [[ResourceCountV2]]. - * - * @param searchValue the values to search for. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class FullTextSearchCountRequestV2(searchValue: String, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - limitToStandoffClass: Option[SmartIri], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Requests the amount of results (resources count) of a given fulltext search. A successful response will be a [[ResourceCountV2]]. + * + * @param searchValue the values to search for. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class FullTextSearchCountRequestV2( + searchValue: String, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + limitToStandoffClass: Option[SmartIri], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 /** - * Requests a fulltext search. A successful response will be a [[org.knora.webapi.messages.v2.responder.resourcemessages.ReadResourcesSequenceV2]]. - * - * @param searchValue the values to search for. - * @param offset the offset to be used for paging. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param returnFiles if true, return any file value value attached to each matching resource. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class FulltextSearchRequestV2(searchValue: String, - offset: Int, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - limitToStandoffClass: Option[SmartIri], - returnFiles: Boolean, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Requests a fulltext search. A successful response will be a [[org.knora.webapi.messages.v2.responder.resourcemessages.ReadResourcesSequenceV2]]. + * + * @param searchValue the values to search for. + * @param offset the offset to be used for paging. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param returnFiles if true, return any file value value attached to each matching resource. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class FulltextSearchRequestV2( + searchValue: String, + offset: Int, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + limitToStandoffClass: Option[SmartIri], + returnFiles: Boolean, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 /** - * - * Requests the amount of results (resources count) of a given Gravsearch query. A successful response will be a [[ResourceCountV2]]. - * - * @param constructQuery a Sparql construct query provided by the client. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class GravsearchCountRequestV2(constructQuery: ConstructQuery, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Requests the amount of results (resources count) of a given Gravsearch query. A successful response will be a [[ResourceCountV2]]. + * + * @param constructQuery a Sparql construct query provided by the client. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class GravsearchCountRequestV2( + constructQuery: ConstructQuery, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 /** - * - * Performs a Gravsearch query. A successful response will be a [[org.knora.webapi.messages.v2.responder.resourcemessages.ReadResourcesSequenceV2]]. - * - * @param constructQuery a Sparql construct query provided by the client. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class GravsearchRequestV2(constructQuery: ConstructQuery, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption] = Set.empty[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Performs a Gravsearch query. A successful response will be a [[org.knora.webapi.messages.v2.responder.resourcemessages.ReadResourcesSequenceV2]]. + * + * @param constructQuery a Sparql construct query provided by the client. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class GravsearchRequestV2( + constructQuery: ConstructQuery, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption] = Set.empty[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 /** - * Requests a search of resources by their label. A successful response will be a [[ResourceCountV2]]. - * - * @param searchValue the values to search for. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class SearchResourceByLabelCountRequestV2(searchValue: String, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Requests a search of resources by their label. A successful response will be a [[ResourceCountV2]]. + * + * @param searchValue the values to search for. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class SearchResourceByLabelCountRequestV2( + searchValue: String, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 /** - * Requests a search of resources by their label. A successful response will be a [[org.knora.webapi.messages.v2.responder.resourcemessages.ReadResourcesSequenceV2]]. - * - * @param searchValue the values to search for. - * @param offset the offset to be used for paging. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class SearchResourceByLabelRequestV2(searchValue: String, - offset: Int, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Requests a search of resources by their label. A successful response will be a [[org.knora.webapi.messages.v2.responder.resourcemessages.ReadResourcesSequenceV2]]. + * + * @param searchValue the values to search for. + * @param offset the offset to be used for paging. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class SearchResourceByLabelRequestV2( + searchValue: String, + offset: Int, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 /** - * Represents the number of resources found by a search query. - */ + * Represents the number of resources found by a search query. + */ case class ResourceCountV2(numberOfResources: Int) extends KnoraJsonLDResponseV2 { - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = JsonLDDocument( body = JsonLDObject( Map( OntologyConstants.SchemaOrg.NumberOfItems -> JsonLDInt(numberOfResources) - )), + ) + ), context = JsonLDObject( Map( "schema" -> JsonLDString(OntologyConstants.SchemaOrg.SchemaOrgPrefixExpansion) - )) + ) + ) ) - } } /** - * Requests resources of the specified class from the specified project. - * - * @param projectIri the IRI of the project. - * @param resourceClass the IRI of the resource class, in the complex schema. - * @param orderByProperty the IRI of the property that the resources are to be ordered by, in the complex schema. - * @param page the page number of the results page to be returned. - * @param targetSchema the schema of the response. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class SearchResourcesByProjectAndClassRequestV2(projectIri: SmartIri, - resourceClass: SmartIri, - orderByProperty: Option[SmartIri], - page: Int, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends SearchResponderRequestV2 + * Requests resources of the specified class from the specified project. + * + * @param projectIri the IRI of the project. + * @param resourceClass the IRI of the resource class, in the complex schema. + * @param orderByProperty the IRI of the property that the resources are to be ordered by, in the complex schema. + * @param page the page number of the results page to be returned. + * @param targetSchema the schema of the response. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class SearchResourcesByProjectAndClassRequestV2( + projectIri: SmartIri, + resourceClass: SmartIri, + orderByProperty: Option[SmartIri], + page: Int, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends SearchResponderRequestV2 diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/standoffmessages/StandoffMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/standoffmessages/StandoffMessagesV2.scala index af319a7124..dc57230733 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/standoffmessages/StandoffMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/standoffmessages/StandoffMessagesV2.scala @@ -45,62 +45,66 @@ import scala.collection.immutable.SortedSet import scala.concurrent.{ExecutionContext, Future} /** - * An abstract trait representing a Knora v2 API request message that can be sent to `StandoffResponderV2`. - */ + * An abstract trait representing a Knora v2 API request message that can be sent to `StandoffResponderV2`. + */ sealed trait StandoffResponderRequestV2 extends KnoraRequestV2 /** - * Requests a page of standoff markup from a text value. A successful response will be a [[GetStandoffResponseV2]]. - * - * @param resourceIri the IRI of the resource containing the value. - * @param valueIri the IRI of the value. - * @param offset the start index of the first standoff tag to be returned. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class GetStandoffPageRequestV2(resourceIri: IRI, - valueIri: IRI, - offset: Int, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends StandoffResponderRequestV2 + * Requests a page of standoff markup from a text value. A successful response will be a [[GetStandoffResponseV2]]. + * + * @param resourceIri the IRI of the resource containing the value. + * @param valueIri the IRI of the value. + * @param offset the start index of the first standoff tag to be returned. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class GetStandoffPageRequestV2( + resourceIri: IRI, + valueIri: IRI, + offset: Int, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends StandoffResponderRequestV2 /** - * Requests all the standoff markup from a text value, except for the first page. A successful response will be a [[GetStandoffResponseV2]]. - * - * @param resourceIri the IRI of the resource containing the text value. - * @param valueIri the IRI of the text value. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ -case class GetRemainingStandoffFromTextValueRequestV2(resourceIri: IRI, - valueIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends StandoffResponderRequestV2 + * Requests all the standoff markup from a text value, except for the first page. A successful response will be a [[GetStandoffResponseV2]]. + * + * @param resourceIri the IRI of the resource containing the text value. + * @param valueIri the IRI of the text value. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ +case class GetRemainingStandoffFromTextValueRequestV2( + resourceIri: IRI, + valueIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends StandoffResponderRequestV2 /** - * A response to a [[GetStandoffPageRequestV2]] or a [[GetRemainingStandoffFromTextValueRequestV2]], representing standoff - * tags from a text value. - * - * @param valueIri the IRI of the value. - * @param standoff standoff tags from the value. - * @param nextOffset the next available offset, if any. - */ + * A response to a [[GetStandoffPageRequestV2]] or a [[GetRemainingStandoffFromTextValueRequestV2]], representing standoff + * tags from a text value. + * + * @param valueIri the IRI of the value. + * @param standoff standoff tags from the value. + * @param nextOffset the next available offset, if any. + */ case class GetStandoffResponseV2(valueIri: IRI, standoff: Seq[StandoffTagV2], nextOffset: Option[Int]) extends KnoraJsonLDResponseV2 { /** - * Converts the response to a data structure that can be used to generate JSON-LD. - * - * @param targetSchema the Knora API schema to be used in the JSON-LD document. - * @return a [[JsonLDDocument]] representing the response. - */ - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + * Converts the response to a data structure that can be used to generate JSON-LD. + * + * @param targetSchema the Knora API schema to be used in the JSON-LD document. + * @return a [[JsonLDDocument]] representing the response. + */ + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { if (targetSchema != ApiV2Complex) { throw AssertionException(s"Standoff is available only in the complex schema") } @@ -135,44 +139,45 @@ case class GetStandoffResponseV2(valueIri: IRI, standoff: Seq[StandoffTagV2], ne } /** - * Represents a request to create a mapping between XML elements and attributes and standoff classes and properties. - * A successful response will be a [[CreateMappingResponseV2]]. - * - * @param metadata the metadata describing the mapping. - * @param xml the mapping in XML syntax. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the user making the request. - * @param apiRequestID the ID of the API request. - */ -case class CreateMappingRequestV2(metadata: CreateMappingRequestMetadataV2, - xml: CreateMappingRequestXMLV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends StandoffResponderRequestV2 + * Represents a request to create a mapping between XML elements and attributes and standoff classes and properties. + * A successful response will be a [[CreateMappingResponseV2]]. + * + * @param metadata the metadata describing the mapping. + * @param xml the mapping in XML syntax. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the user making the request. + * @param apiRequestID the ID of the API request. + */ +case class CreateMappingRequestV2( + metadata: CreateMappingRequestMetadataV2, + xml: CreateMappingRequestXMLV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends StandoffResponderRequestV2 /** - * Represents the metadata describing the mapping that is to be created. - * - * @param label the label describing the mapping. - * @param projectIri the IRI of the project the mapping belongs to. - * @param mappingName the name of the mapping to be created. - */ + * Represents the metadata describing the mapping that is to be created. + * + * @param label the label describing the mapping. + * @param projectIri the IRI of the project the mapping belongs to. + * @param mappingName the name of the mapping to be created. + */ case class CreateMappingRequestMetadataV2(label: String, projectIri: SmartIri, mappingName: String) extends StandoffResponderRequestV2 object CreateMappingRequestMetadataV2 extends KnoraJsonLDRequestReaderV2[CreateMappingRequestMetadataV2] { - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[CreateMappingRequestMetadataV2] = { + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CreateMappingRequestMetadataV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -180,23 +185,27 @@ object CreateMappingRequestMetadataV2 extends KnoraJsonLDRequestReaderV2[CreateM requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM): CreateMappingRequestMetadataV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM + ): CreateMappingRequestMetadataV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val label: String = jsonLDDocument.requireStringWithValidation(OntologyConstants.Rdfs.Label, stringFormatter.toSparqlEncodedString) - val projectIri: SmartIri = jsonLDDocument.requireIriInObject(OntologyConstants.KnoraApiV2Complex.AttachedToProject, - stringFormatter.toSmartIriWithErr) + val projectIri: SmartIri = jsonLDDocument.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.AttachedToProject, + stringFormatter.toSmartIriWithErr + ) val mappingName: String = jsonLDDocument.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.MappingHasName, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) CreateMappingRequestMetadataV2( label = label, @@ -207,24 +216,26 @@ object CreateMappingRequestMetadataV2 extends KnoraJsonLDRequestReaderV2[CreateM } /** - * Represents the mapping as an XML document. - * - * @param xml the mapping to be created. - */ + * Represents the mapping as an XML document. + * + * @param xml the mapping to be created. + */ case class CreateMappingRequestXMLV2(xml: String) extends StandoffResponderRequestV2 /** - * Provides the IRI of the created mapping. - * - * @param mappingIri the IRI of the resource (knora-base:XMLToStandoffMapping) representing the mapping that has been created. - * @param label the label describing the mapping. - * @param projectIri the project the mapping belongs to. - */ + * Provides the IRI of the created mapping. + * + * @param mappingIri the IRI of the resource (knora-base:XMLToStandoffMapping) representing the mapping that has been created. + * @param label the label describing the mapping. + * @param projectIri the project the mapping belongs to. + */ case class CreateMappingResponseV2(mappingIri: IRI, label: String, projectIri: SmartIri) extends KnoraJsonLDResponseV2 { - def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -232,12 +243,14 @@ case class CreateMappingResponseV2(mappingIri: IRI, label: String, projectIri: S Map( JsonLDKeywords.ID -> JsonLDString(mappingIri), JsonLDKeywords.TYPE -> JsonLDString( - OntologyConstants.KnoraBase.XMLToStandoffMapping.toSmartIri.toOntologySchema(targetSchema).toString), + OntologyConstants.KnoraBase.XMLToStandoffMapping.toSmartIri.toOntologySchema(targetSchema).toString + ), OntologyConstants.Rdfs.Label -> JsonLDString(label), OntologyConstants.KnoraApiV2Complex.AttachedToProject.toSmartIri .toOntologySchema(targetSchema) .toString -> JsonLDUtil.iriToJsonLDObject(projectIri.toString) - )) + ) + ) val context = JsonLDObject( Map( @@ -245,104 +258,111 @@ case class CreateMappingResponseV2(mappingIri: IRI, label: String, projectIri: S "rdf" -> JsonLDString("http://www.w3.org/1999/02/22-rdf-syntax-ns#"), "owl" -> JsonLDString("http://www.w3.org/2002/07/owl#"), "xsd" -> JsonLDString("http://www.w3.org/2001/XMLSchema#") - )) + ) + ) JsonLDDocument(body, context) } } /** - * Represents a request to get a mapping from XML elements and attributes to standoff entities. - * - * @param mappingIri the IRI of the mapping. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the user making the request. - */ + * Represents a request to get a mapping from XML elements and attributes to standoff entities. + * + * @param mappingIri the IRI of the mapping. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the user making the request. + */ case class GetMappingRequestV2(mappingIri: IRI, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM) extends StandoffResponderRequestV2 /** - * Represents a response to a [[GetMappingRequestV2]]. - * - * @param mappingIri the IRI of the requested mapping. - * @param mapping the requested mapping. - * @param standoffEntities the standoff entities referred to in the mapping. - */ + * Represents a response to a [[GetMappingRequestV2]]. + * + * @param mappingIri the IRI of the requested mapping. + * @param mapping the requested mapping. + * @param standoffEntities the standoff entities referred to in the mapping. + */ case class GetMappingResponseV2( - mappingIri: IRI, - mapping: MappingXMLtoStandoff, - standoffEntities: StandoffEntityInfoGetResponseV2) // TODO: there should be a route to obtain a mapping + mappingIri: IRI, + mapping: MappingXMLtoStandoff, + standoffEntities: StandoffEntityInfoGetResponseV2 +) // TODO: there should be a route to obtain a mapping /** - * Represents a request that gets an XSL Transformation represented by a `knora-base:XSLTransformation`. - * - * @param xsltTextRepresentationIri the IRI of the `knora-base:XSLTransformation`. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the user making the request. - */ -case class GetXSLTransformationRequestV2(xsltTextRepresentationIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends StandoffResponderRequestV2 + * Represents a request that gets an XSL Transformation represented by a `knora-base:XSLTransformation`. + * + * @param xsltTextRepresentationIri the IRI of the `knora-base:XSLTransformation`. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the user making the request. + */ +case class GetXSLTransformationRequestV2( + xsltTextRepresentationIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM +) extends StandoffResponderRequestV2 /** - * Represents a response to a [[GetXSLTransformationRequestV2]]. - * - * @param xslt the XSLT to be applied to the XML created from standoff. - */ + * Represents a response to a [[GetXSLTransformationRequestV2]]. + * + * @param xslt the XSLT to be applied to the XML created from standoff. + */ case class GetXSLTransformationResponseV2(xslt: String) /** - * Represents a mapping between XML tags and standoff entities (classes and properties). - * - * Example: - * - * namespace = Map("myXMLNamespace" -> Map("myXMLTagName" -> Map("myXMLClassname" -> XMLTag(...)))) - * - * The class names allow for the reuse of the same tag name. This is important when using HTML since the tag set is very limited. - * - * @param namespace a Map of XML namespaces and a Map of tag names and [[XMLTag]]. - * @param defaultXSLTransformation the IRI of the default XSL transformation for the resulting XML, if any. - */ -case class MappingXMLtoStandoff(namespace: Map[String, Map[String, Map[String, XMLTag]]], - defaultXSLTransformation: Option[IRI]) + * Represents a mapping between XML tags and standoff entities (classes and properties). + * + * Example: + * + * namespace = Map("myXMLNamespace" -> Map("myXMLTagName" -> Map("myXMLClassname" -> XMLTag(...)))) + * + * The class names allow for the reuse of the same tag name. This is important when using HTML since the tag set is very limited. + * + * @param namespace a Map of XML namespaces and a Map of tag names and [[XMLTag]]. + * @param defaultXSLTransformation the IRI of the default XSL transformation for the resulting XML, if any. + */ +case class MappingXMLtoStandoff( + namespace: Map[String, Map[String, Map[String, XMLTag]]], + defaultXSLTransformation: Option[IRI] +) /** - * Represents a mapping between an XML tag and standoff entities (classes and properties). - * - * @param name the tag name. - * @param mapping the corresponding standoff entities. - * @param separatorRequired indicates if the element requires a separator in the text once the markup has been converted to standoff. - */ + * Represents a mapping between an XML tag and standoff entities (classes and properties). + * + * @param name the tag name. + * @param mapping the corresponding standoff entities. + * @param separatorRequired indicates if the element requires a separator in the text once the markup has been converted to standoff. + */ case class XMLTag(name: String, mapping: XMLTagToStandoffClass, separatorRequired: Boolean) /** - * Represents standoff entities referred to in the mapping. - * The attributes are represented as a Map of namespaces and a Map of attribute names and standoff properties. - * - * Example for attributesToProps: - * - * attributesToProps = Map("myXMLNamespace" -> Map("myXMLAttributeName" -> "standoffPropertyIri")) - * - * @param standoffClassIri the IRI of the standoff class. - * @param attributesToProps a mapping between XML namespaces and attribute names and standoff properties. - * @param dataType the data type of the standoff class (e.g., a date). - */ -case class XMLTagToStandoffClass(standoffClassIri: IRI, - attributesToProps: Map[String, Map[String, IRI]] = Map.empty[String, Map[String, IRI]], - dataType: Option[XMLStandoffDataTypeClass]) + * Represents standoff entities referred to in the mapping. + * The attributes are represented as a Map of namespaces and a Map of attribute names and standoff properties. + * + * Example for attributesToProps: + * + * attributesToProps = Map("myXMLNamespace" -> Map("myXMLAttributeName" -> "standoffPropertyIri")) + * + * @param standoffClassIri the IRI of the standoff class. + * @param attributesToProps a mapping between XML namespaces and attribute names and standoff properties. + * @param dataType the data type of the standoff class (e.g., a date). + */ +case class XMLTagToStandoffClass( + standoffClassIri: IRI, + attributesToProps: Map[String, Map[String, IRI]] = Map.empty[String, Map[String, IRI]], + dataType: Option[XMLStandoffDataTypeClass] +) /** - * Represents a data type standoff class in mapping for an XML element. - * - * @param standoffDataTypeClass the data type of the standoff class (e.g., a date). - * @param dataTypeXMLAttribute the XML attribute holding the information needed for the standoff class data type (e.g., a date string). - */ + * Represents a data type standoff class in mapping for an XML element. + * + * @param standoffDataTypeClass the data type of the standoff class (e.g., a date). + * @param dataTypeXMLAttribute the XML attribute holding the information needed for the standoff class data type (e.g., a date string). + */ case class XMLStandoffDataTypeClass(standoffDataTypeClass: StandoffDataTypeClasses.Value, dataTypeXMLAttribute: String) /** - * Represents the data types of standoff classes. - */ + * Represents the data types of standoff classes. + */ object StandoffDataTypeClasses extends Enumeration { val StandoffLinkTag: Value = Value(OntologyConstants.KnoraBase.StandoffLinkTag) @@ -368,27 +388,26 @@ object StandoffDataTypeClasses extends Enumeration { val valueMap: Map[IRI, Value] = values.toList.map(v => (v.toString, v)).toMap /** - * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an - * exception. - * - * @param name the name of the value. - * @param errorFun the function to be called in case of an error. - * @return the requested value. - */ - def lookup(name: String, errorFun: => Nothing): Value = { + * Given the name of a value in this enumeration, returns the value. If the value is not found, throws an + * exception. + * + * @param name the name of the value. + * @param errorFun the function to be called in case of an error. + * @return the requested value. + */ + def lookup(name: String, errorFun: => Nothing): Value = valueMap.get(name) match { case Some(value) => value case None => errorFun } - } def getStandoffClassIris: SortedSet[IRI] = StandoffDataTypeClasses.values.map(_.toString) } /** - * Represents collections of standoff properties. - */ + * Represents collections of standoff properties. + */ object StandoffProperties { // represents the standoff properties defined on the base standoff tag @@ -445,17 +464,17 @@ object StandoffProperties { // represents the standoff properties defined on the internal reference standoff tag val internalReferenceProperties: Set[IRI] = Set(OntologyConstants.KnoraBase.StandoffTagHasInternalReference) - val dataTypeProperties - : Set[IRI] = dateProperties ++ intervalProperties ++ timeProperties ++ booleanProperties ++ decimalProperties ++ - integerProperties ++ uriProperties ++ colorProperties ++ linkProperties ++ internalReferenceProperties + val dataTypeProperties: Set[IRI] = + dateProperties ++ intervalProperties ++ timeProperties ++ booleanProperties ++ decimalProperties ++ + integerProperties ++ uriProperties ++ colorProperties ++ linkProperties ++ internalReferenceProperties } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Standoff tags and their components. /** - * A trait representing an attribute attached to a standoff tag. - */ + * A trait representing an attribute attached to a standoff tag. + */ trait StandoffTagAttributeV2 extends KnoraContentV2[StandoffTagAttributeV2] { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -469,13 +488,13 @@ trait StandoffTagAttributeV2 extends KnoraContentV2[StandoffTagAttributeV2] { } /** - * Represents a standoff tag attribute of type IRI. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - * @param targetExists `true` if the specified IRI already exists in the triplestore, `false` if it is - * being created in the same transaction. - */ + * Represents a standoff tag attribute of type IRI. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + * @param targetExists `true` if the specified IRI already exists in the triplestore, `false` if it is + * being created in the same transaction. + */ case class StandoffTagIriAttributeV2(standoffPropertyIri: SmartIri, value: IRI, targetExists: Boolean = true) extends StandoffTagAttributeV2 { @@ -483,45 +502,41 @@ case class StandoffTagIriAttributeV2(standoffPropertyIri: SmartIri, value: IRI, def rdfValue: String = s"<$value>" - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDUtil.iriToJsonLDObject(value) - } } /** - * Represents a standoff tag attribute of type URI. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute of type URI. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagUriAttributeV2(standoffPropertyIri: SmartIri, value: String) extends StandoffTagAttributeV2 { def stringValue: String = value def rdfValue: String = s""""${stringValue.toString}"^^xsd:anyURI""" - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDUtil.datatypeValueToJsonLDObject( value = value, datatype = OntologyConstants.Xsd.Uri.toSmartIri ) - } } /** - * Represents a standoff tag attribute that refers to another standoff node. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute that refers to another standoff node. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagInternalReferenceAttributeV2(standoffPropertyIri: SmartIri, value: IRI) extends StandoffTagAttributeV2 { @@ -529,63 +544,57 @@ case class StandoffTagInternalReferenceAttributeV2(standoffPropertyIri: SmartIri def rdfValue: String = s"<$value>" - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDUtil.iriToJsonLDObject(value) - } } /** - * Represents a standoff tag attribute of type string. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute of type string. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagStringAttributeV2(standoffPropertyIri: SmartIri, value: String) extends StandoffTagAttributeV2 { def stringValue: String = value def rdfValue: String = s"""\"\"\"$value\"\"\"""" - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDString(value) - } } /** - * Represents a standoff tag attribute of type integer. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute of type integer. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagIntegerAttributeV2(standoffPropertyIri: SmartIri, value: Int) extends StandoffTagAttributeV2 { def stringValue: String = value.toString def rdfValue: String = value.toString - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDInt(value) - } } /** - * Represents a standoff tag attribute of type decimal. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute of type decimal. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagDecimalAttributeV2(standoffPropertyIri: SmartIri, value: BigDecimal) extends StandoffTagAttributeV2 { @@ -593,91 +602,86 @@ case class StandoffTagDecimalAttributeV2(standoffPropertyIri: SmartIri, value: B def rdfValue: String = s""""${value.toString}"^^xsd:decimal""" - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDUtil.datatypeValueToJsonLDObject( value = value.toString, datatype = OntologyConstants.Xsd.Decimal.toSmartIri ) - } } /** - * Represents a standoff tag attribute of type boolean. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute of type boolean. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagBooleanAttributeV2(standoffPropertyIri: SmartIri, value: Boolean) extends StandoffTagAttributeV2 { def stringValue: String = value.toString def rdfValue: String = value.toString - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDBoolean(value) - } } /** - * Represents a standoff tag attribute of type xsd:dateTimeStamp. - * - * @param standoffPropertyIri the IRI of the standoff property - * @param value the value of the standoff property. - */ + * Represents a standoff tag attribute of type xsd:dateTimeStamp. + * + * @param standoffPropertyIri the IRI of the standoff property + * @param value the value of the standoff property. + */ case class StandoffTagTimeAttributeV2(standoffPropertyIri: SmartIri, value: Instant) extends StandoffTagAttributeV2 { def stringValue: String = value.toString def rdfValue: String = s""""${value.toString}"^^xsd:dateTime""" - override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = { + override def toOntologySchema(targetSchema: OntologySchema): StandoffTagAttributeV2 = copy(standoffPropertyIri = standoffPropertyIri.toOntologySchema(targetSchema)) - } - override def toJsonLD: (IRI, JsonLDValue) = { + override def toJsonLD: (IRI, JsonLDValue) = standoffPropertyIri.toString -> JsonLDUtil.datatypeValueToJsonLDObject( value = value.toString, datatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri ) - } } /** - * Represents any subclass of a `knora-base:StandoffTag`. - * - * @param standoffTagClassIri the IRI of the standoff class to be created. - * @param dataType the data type of the standoff class, if any. - * @param uuid a [[UUID]] representing this tag and any other tags that - * point to semantically equivalent ranges in other versions of the same text. - * @param startPosition the start position of the range of characters marked up with this tag. - * @param endPosition the end position of the range of characters marked up with this tag. - * @param startIndex the index of this tag (start index in case of a virtual hierarchy tag that has two parents). Indexes are numbered from 0 within the context of a particular text, - * and make it possible to order tags that share the same position. - * @param endIndex the index of the end position (only in case of a virtual hierarchy tag). - * @param startParentIndex the index of the parent node (start index in case of a virtual hierarchy tag that has two parents), if any, that contains the start position. - * @param endParentIndex the index of the the parent node (only in case of a virtual hierarchy tag), if any, that contains the end position. - * @param attributes the attributes attached to this tag. - */ -case class StandoffTagV2(standoffTagClassIri: SmartIri, - dataType: Option[StandoffDataTypeClasses.Value] = None, - uuid: UUID, - originalXMLID: Option[String], - startPosition: Int, - endPosition: Int, - startIndex: Int, - endIndex: Option[Int] = None, - startParentIndex: Option[Int] = None, - endParentIndex: Option[Int] = None, - attributes: Seq[StandoffTagAttributeV2] = Seq.empty[StandoffTagAttributeV2]) - extends KnoraContentV2[StandoffTagV2] { + * Represents any subclass of a `knora-base:StandoffTag`. + * + * @param standoffTagClassIri the IRI of the standoff class to be created. + * @param dataType the data type of the standoff class, if any. + * @param uuid a [[UUID]] representing this tag and any other tags that + * point to semantically equivalent ranges in other versions of the same text. + * @param startPosition the start position of the range of characters marked up with this tag. + * @param endPosition the end position of the range of characters marked up with this tag. + * @param startIndex the index of this tag (start index in case of a virtual hierarchy tag that has two parents). Indexes are numbered from 0 within the context of a particular text, + * and make it possible to order tags that share the same position. + * @param endIndex the index of the end position (only in case of a virtual hierarchy tag). + * @param startParentIndex the index of the parent node (start index in case of a virtual hierarchy tag that has two parents), if any, that contains the start position. + * @param endParentIndex the index of the the parent node (only in case of a virtual hierarchy tag), if any, that contains the end position. + * @param attributes the attributes attached to this tag. + */ +case class StandoffTagV2( + standoffTagClassIri: SmartIri, + dataType: Option[StandoffDataTypeClasses.Value] = None, + uuid: UUID, + originalXMLID: Option[String], + startPosition: Int, + endPosition: Int, + startIndex: Int, + endIndex: Option[Int] = None, + startParentIndex: Option[Int] = None, + endParentIndex: Option[Int] = None, + attributes: Seq[StandoffTagAttributeV2] = Seq.empty[StandoffTagAttributeV2] +) extends KnoraContentV2[StandoffTagV2] { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override def toOntologySchema(targetSchema: OntologySchema): StandoffTagV2 = { @@ -732,7 +736,6 @@ case class StandoffTagV2(standoffTagClassIri: SmartIri, ) } - def getOntologyIrisUsed: Set[SmartIri] = { + def getOntologyIrisUsed: Set[SmartIri] = attributes.map(_.standoffPropertyIri.getOntologyFromEntity).toSet + standoffTagClassIri.getOntologyFromEntity - } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala index 6b1a2097d1..1b2f45c9f9 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala @@ -49,52 +49,54 @@ import org.knora.webapi.util._ import scala.concurrent.{ExecutionContext, Future} /** - * A tagging trait for requests handled by [[org.knora.webapi.responders.v2.ValuesResponderV2]]. - */ + * A tagging trait for requests handled by [[org.knora.webapi.responders.v2.ValuesResponderV2]]. + */ sealed trait ValuesResponderRequestV2 extends KnoraRequestV2 /** - * Requests the creation of a value. - * - * @param createValue a [[CreateValueV2]] representing the value to be created. A successful response will be - * a [[CreateValueResponseV2]]. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ -case class CreateValueRequestV2(createValue: CreateValueV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV2 + * Requests the creation of a value. + * + * @param createValue a [[CreateValueV2]] representing the value to be created. A successful response will be + * a [[CreateValueResponseV2]]. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ +case class CreateValueRequestV2( + createValue: CreateValueV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV2 /** - * Constructs [[CreateValueRequestV2]] instances based on JSON-LD input. - */ + * Constructs [[CreateValueRequestV2]] instances based on JSON-LD input. + */ object CreateValueRequestV2 extends KnoraJsonLDRequestReaderV2[CreateValueRequestV2] { /** - * Converts JSON-LD input to a [[CreateValueRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[CreateValueRequestV2] = { + * Converts JSON-LD input to a [[CreateValueRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[CreateValueRequestV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -142,56 +144,60 @@ object CreateValueRequestV2 extends KnoraJsonLDRequestReaderV2[CreateValueReques expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri, validationFun = stringFormatter.xsdDateTimeStampToInstant ) - .orElse(jsonLDObject.maybeDatatypeValueInObject( - key = OntologyConstants.KnoraApiV2Complex.CreationDate, - expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri, - validationFun = stringFormatter.xsdDateTimeStampToInstant - )) + .orElse( + jsonLDObject.maybeDatatypeValueInObject( + key = OntologyConstants.KnoraApiV2Complex.CreationDate, + expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri, + validationFun = stringFormatter.xsdDateTimeStampToInstant + ) + ) maybePermissions: Option[String] = jsonLDObject.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.HasPermissions, - stringFormatter.toSparqlEncodedString) - } yield - CreateValueV2( - resourceIri = resourceIri.toString, - resourceClassIri = resourceClassIri, - propertyIri = propertyIri, - valueContent = valueContent, - valueIri = maybeCustomValueIri, - valueUUID = maybeCustomUUID, - valueCreationDate = maybeCreationDate, - permissions = maybePermissions + stringFormatter.toSparqlEncodedString ) + } yield CreateValueV2( + resourceIri = resourceIri.toString, + resourceClassIri = resourceClassIri, + propertyIri = propertyIri, + valueContent = valueContent, + valueIri = maybeCustomValueIri, + valueUUID = maybeCustomUUID, + valueCreationDate = maybeCreationDate, + permissions = maybePermissions + ) } - } yield - CreateValueRequestV2( - createValue = createValue, - featureFactoryConfig = featureFactoryConfig, - apiRequestID = apiRequestID, - requestingUser = requestingUser - ) + } yield CreateValueRequestV2( + createValue = createValue, + featureFactoryConfig = featureFactoryConfig, + apiRequestID = apiRequestID, + requestingUser = requestingUser + ) } } /** - * Represents a successful response to a [[CreateValueRequestV2]]. - * - * @param valueIri the IRI of the value that was created. - * @param valueType the type of the value that was created. - * @param valueUUID the value's UUID. - * @param valueCreationDate the value's creationDate - * @param projectADM the project in which the value was created. - */ -case class CreateValueResponseV2(valueIri: IRI, - valueType: SmartIri, - valueUUID: UUID, - valueCreationDate: Instant, - projectADM: ProjectADM) - extends KnoraJsonLDResponseV2 + * Represents a successful response to a [[CreateValueRequestV2]]. + * + * @param valueIri the IRI of the value that was created. + * @param valueType the type of the value that was created. + * @param valueUUID the value's UUID. + * @param valueCreationDate the value's creationDate + * @param projectADM the project in which the value was created. + */ +case class CreateValueResponseV2( + valueIri: IRI, + valueType: SmartIri, + valueUUID: UUID, + valueCreationDate: Instant, + projectADM: ProjectADM +) extends KnoraJsonLDResponseV2 with UpdateResultInProject { - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance if (targetSchema != ApiV2Complex) { @@ -220,47 +226,49 @@ case class CreateValueResponseV2(valueIri: IRI, } /** - * Requests an update to a value, i.e. the creation of a new version of an existing value. - * - * @param updateValue an [[UpdateValueV2]] representing the new version of the value. A successful response will be - * an [[UpdateValueResponseV2]]. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ -case class UpdateValueRequestV2(updateValue: UpdateValueV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV2 + * Requests an update to a value, i.e. the creation of a new version of an existing value. + * + * @param updateValue an [[UpdateValueV2]] representing the new version of the value. A successful response will be + * an [[UpdateValueResponseV2]]. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ +case class UpdateValueRequestV2( + updateValue: UpdateValueV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV2 /** - * Constructs [[UpdateValueRequestV2]] instances based on JSON-LD input. - */ + * Constructs [[UpdateValueRequestV2]] instances based on JSON-LD input. + */ object UpdateValueRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateValueRequestV2] { /** - * Converts JSON-LD input to a [[CreateValueRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[UpdateValueRequestV2] = { + * Converts JSON-LD input to a [[CreateValueRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[UpdateValueRequestV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -296,7 +304,8 @@ object UpdateValueRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateValueReques .map { definedNewIri => if (definedNewIri == valueIri) { throw BadRequestException( - s"The IRI of a new value version cannot be the same as the IRI of the current version") + s"The IRI of a new value version cannot be the same as the IRI of the current version" + ) } stringFormatter.validateCustomValueIri( @@ -323,7 +332,8 @@ object UpdateValueRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateValueReques jsonLDObject.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.toSmartIriWithErr) val permissions = jsonLDObject.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.HasPermissions, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) FastFuture.successful( UpdateValuePermissionsV2( @@ -353,44 +363,45 @@ object UpdateValueRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateValueReques maybePermissions: Option[String] = jsonLDObject.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.HasPermissions, - stringFormatter.toSparqlEncodedString) - } yield - UpdateValueContentV2( - resourceIri = resourceIri.toString, - resourceClassIri = resourceClassIri, - propertyIri = propertyIri, - valueIri = valueIri.toString, - valueContent = valueContent, - permissions = maybePermissions, - valueCreationDate = maybeValueCreationDate, - newValueVersionIri = maybeNewIri + stringFormatter.toSparqlEncodedString ) + } yield UpdateValueContentV2( + resourceIri = resourceIri.toString, + resourceClassIri = resourceClassIri, + propertyIri = propertyIri, + valueIri = valueIri.toString, + valueContent = valueContent, + permissions = maybePermissions, + valueCreationDate = maybeValueCreationDate, + newValueVersionIri = maybeNewIri + ) } } - } yield - UpdateValueRequestV2( - updateValue = updateValue, - featureFactoryConfig = featureFactoryConfig, - apiRequestID = apiRequestID, - requestingUser = requestingUser - ) + } yield UpdateValueRequestV2( + updateValue = updateValue, + featureFactoryConfig = featureFactoryConfig, + apiRequestID = apiRequestID, + requestingUser = requestingUser + ) } } /** - * Represents a successful response to an [[UpdateValueRequestV2]]. - * - * @param valueIri the IRI of the value version that was created. - * @param valueType the type of the value that was updated. - * @param valueUUID the value's UUID. - * @param projectADM the project in which the value was updated. - */ + * Represents a successful response to an [[UpdateValueRequestV2]]. + * + * @param valueIri the IRI of the value version that was created. + * @param valueType the type of the value that was updated. + * @param valueUUID the value's UUID. + * @param projectADM the project in which the value was updated. + */ case class UpdateValueResponseV2(valueIri: IRI, valueType: SmartIri, valueUUID: UUID, projectADM: ProjectADM) extends KnoraJsonLDResponseV2 with UpdateResultInProject { - override def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = { + override def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance if (targetSchema != ApiV2Complex) { @@ -415,56 +426,58 @@ case class UpdateValueResponseV2(valueIri: IRI, valueType: SmartIri, valueUUID: } /** - * Requests that a value is marked as deleted. A successful response will be a [[SuccessResponseV2]]. - * - * @param resourceIri the IRI of the containing resource. - * @param resourceClassIri the IRI of the resource class. - * @param propertyIri the IRI of the property pointing to the value to be marked as deleted. - * @param valueIri the IRI of the value to be marked as deleted. - * @param valueTypeIri the IRI of the value class. - * @param deleteComment an optional comment explaining why the value is being marked as deleted. - * @param deleteDate an optional timestamp indicating when the value was deleted. If not supplied, - * the current time will be used. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ -case class DeleteValueRequestV2(resourceIri: IRI, - resourceClassIri: SmartIri, - propertyIri: SmartIri, - valueIri: IRI, - valueTypeIri: SmartIri, - deleteComment: Option[String] = None, - deleteDate: Option[Instant] = None, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID) - extends ValuesResponderRequestV2 + * Requests that a value is marked as deleted. A successful response will be a [[SuccessResponseV2]]. + * + * @param resourceIri the IRI of the containing resource. + * @param resourceClassIri the IRI of the resource class. + * @param propertyIri the IRI of the property pointing to the value to be marked as deleted. + * @param valueIri the IRI of the value to be marked as deleted. + * @param valueTypeIri the IRI of the value class. + * @param deleteComment an optional comment explaining why the value is being marked as deleted. + * @param deleteDate an optional timestamp indicating when the value was deleted. If not supplied, + * the current time will be used. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ +case class DeleteValueRequestV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + propertyIri: SmartIri, + valueIri: IRI, + valueTypeIri: SmartIri, + deleteComment: Option[String] = None, + deleteDate: Option[Instant] = None, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID +) extends ValuesResponderRequestV2 object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueRequestV2] { /** - * Converts JSON-LD input into a case class instance. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a case class instance representing the input. - */ - override def fromJsonLD(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[DeleteValueRequestV2] = { + * Converts JSON-LD input into a case class instance. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a case class instance representing the input. + */ + override def fromJsonLD( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[DeleteValueRequestV2] = Future { fromJsonLDSync( jsonLDDocument = jsonLDDocument, @@ -473,12 +486,13 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques requestingUser = requestingUser ) } - } - private def fromJsonLDSync(jsonLDDocument: JsonLDDocument, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): DeleteValueRequestV2 = { + private def fromJsonLDSync( + jsonLDDocument: JsonLDDocument, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): DeleteValueRequestV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance // Get the IRI of the resource that the value is to be created in. @@ -504,7 +518,8 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques val deleteComment: Option[String] = jsonLDObject.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.DeleteComment, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) val deleteDate: Option[Instant] = jsonLDObject.maybeDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.DeleteDate, @@ -529,72 +544,75 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques } /** - * Requests SPARQL for creating multiple values in a new, empty resource. The resource ''must'' be a new, empty - * resource, i.e. it must have no values. This message is used only internally by Knora, and is not part of the Knora - * v1 API. All pre-update checks must already have been performed before this message is sent. Specifically, the - * sender must ensure that: - * - * - The requesting user has permission to add values to the resource. - * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed - * to point to it. - * - The resource class has a suitable cardinality for each submitted value. - * - All required values are provided. - * - Redundant values are not submitted. - * - Any custom permissions in values have been validated and correctly formatted. - * - The target resources of link values and standoff links exist, if they are expected to exist. - * - The list nodes referred to by list values exist. - * - * A successful response will be a [[GenerateSparqlToCreateMultipleValuesResponseV2]]. - * - * @param resourceIri the IRI of the resource in which values are to be created. - * @param values a map of property IRIs to the values to be added for each property. - * @param creationDate an xsd:dateTimeStamp that will be attached to the values. - * @param requestingUser the user that is creating the values. - */ + * Requests SPARQL for creating multiple values in a new, empty resource. The resource ''must'' be a new, empty + * resource, i.e. it must have no values. This message is used only internally by Knora, and is not part of the Knora + * v1 API. All pre-update checks must already have been performed before this message is sent. Specifically, the + * sender must ensure that: + * + * - The requesting user has permission to add values to the resource. + * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed + * to point to it. + * - The resource class has a suitable cardinality for each submitted value. + * - All required values are provided. + * - Redundant values are not submitted. + * - Any custom permissions in values have been validated and correctly formatted. + * - The target resources of link values and standoff links exist, if they are expected to exist. + * - The list nodes referred to by list values exist. + * + * A successful response will be a [[GenerateSparqlToCreateMultipleValuesResponseV2]]. + * + * @param resourceIri the IRI of the resource in which values are to be created. + * @param values a map of property IRIs to the values to be added for each property. + * @param creationDate an xsd:dateTimeStamp that will be attached to the values. + * @param requestingUser the user that is creating the values. + */ case class GenerateSparqlToCreateMultipleValuesRequestV2( - resourceIri: IRI, - values: Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]], - creationDate: Instant, - requestingUser: UserADM) - extends ValuesResponderRequestV2 { + resourceIri: IRI, + values: Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]], + creationDate: Instant, + requestingUser: UserADM +) extends ValuesResponderRequestV2 { lazy val flatValues: Iterable[GenerateSparqlForValueInNewResourceV2] = values.values.flatten } -case class GenerateSparqlForValueInNewResourceV2(valueContent: ValueContentV2, - customValueIri: Option[SmartIri], - customValueUUID: Option[UUID], - customValueCreationDate: Option[Instant], - permissions: String) - extends IOValueV2 - -/** - * Represents a response to a [[GenerateSparqlToCreateMultipleValuesRequestV2]], providing a string that can be - * included in the `INSERT DATA` clause of a SPARQL update operation to create the requested values. - * - * @param insertSparql a string containing statements that must be inserted into the INSERT clause of the SPARQL - * update that will create the values. - * @param unverifiedValues a map of property IRIs to [[UnverifiedValueV2]] objects describing - * the values that should have been created. - * @param hasStandoffLink `true` if the property `knora-base:hasStandoffLinkToValue` was automatically added. - */ -case class GenerateSparqlToCreateMultipleValuesResponseV2(insertSparql: String, - unverifiedValues: Map[SmartIri, Seq[UnverifiedValueV2]], - hasStandoffLink: Boolean) - -/** - * The value of a Knora property in the context of some particular input or output operation. - * Any implementation of `IOValueV2` is an API operation-specific wrapper of a `ValueContentV2`. - */ +case class GenerateSparqlForValueInNewResourceV2( + valueContent: ValueContentV2, + customValueIri: Option[SmartIri], + customValueUUID: Option[UUID], + customValueCreationDate: Option[Instant], + permissions: String +) extends IOValueV2 + +/** + * Represents a response to a [[GenerateSparqlToCreateMultipleValuesRequestV2]], providing a string that can be + * included in the `INSERT DATA` clause of a SPARQL update operation to create the requested values. + * + * @param insertSparql a string containing statements that must be inserted into the INSERT clause of the SPARQL + * update that will create the values. + * @param unverifiedValues a map of property IRIs to [[UnverifiedValueV2]] objects describing + * the values that should have been created. + * @param hasStandoffLink `true` if the property `knora-base:hasStandoffLinkToValue` was automatically added. + */ +case class GenerateSparqlToCreateMultipleValuesResponseV2( + insertSparql: String, + unverifiedValues: Map[SmartIri, Seq[UnverifiedValueV2]], + hasStandoffLink: Boolean +) + +/** + * The value of a Knora property in the context of some particular input or output operation. + * Any implementation of `IOValueV2` is an API operation-specific wrapper of a `ValueContentV2`. + */ trait IOValueV2 { def valueContent: ValueContentV2 } /** - * Provides information about the deletion of a resource or value. - * - * @param deleteDate the date when the resource or value was deleted. - * @param maybeDeleteComment the reason why the resource or value was deleted. - */ + * Provides information about the deletion of a resource or value. + * + * @param deleteDate the date when the resource or value was deleted. + * @param maybeDeleteComment the reason why the resource or value was deleted. + */ case class DeletionInfo(deleteDate: Instant, maybeDeleteComment: Option[String]) { def toJsonLDFields(targetSchema: ApiV2Schema): Map[IRI, JsonLDValue] = { if (targetSchema != ApiV2Complex) { @@ -618,74 +636,76 @@ case class DeletionInfo(deleteDate: Instant, maybeDeleteComment: Option[String]) } /** - * Represents a Knora value as read from the triplestore. - */ + * Represents a Knora value as read from the triplestore. + */ sealed trait ReadValueV2 extends IOValueV2 { /** - * The IRI of the value. - */ + * The IRI of the value. + */ def valueIri: IRI /** - * The user that created the value. - */ + * The user that created the value. + */ def attachedToUser: IRI /** - * The value's permissions. - */ + * The value's permissions. + */ def permissions: String /** - * The permission that the requesting user has on the value. - */ + * The permission that the requesting user has on the value. + */ def userPermission: EntityPermission /** - * The date when the value was created. - */ + * The date when the value was created. + */ def valueCreationDate: Instant /** - * The UUID shared by all the versions of this value. - */ + * The UUID shared by all the versions of this value. + */ def valueHasUUID: UUID /** - * The content of the value. - */ + * The content of the value. + */ def valueContent: ValueContentV2 /** - * The IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - */ + * The IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + */ def previousValueIri: Option[IRI] /** - * If the value has been marked as deleted, information about its deletion. - */ + * If the value has been marked as deleted, information about its deletion. + */ def deletionInfo: Option[DeletionInfo] /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the schema that the value should be converted to. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the schema that the value should be converted to. + */ def toOntologySchema(targetSchema: ApiV2Schema): ReadValueV2 /** - * Converts this value to JSON-LD. - * - * @param targetSchema the target schema. - * @param settings the application settings. - * @return a JSON-LD representation of this value. - */ - def toJsonLD(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + * Converts this value to JSON-LD. + * + * @param targetSchema the target schema. + * @param settings the application settings. + * @return a JSON-LD representation of this value. + */ + def toJsonLD( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val valueContentAsJsonLD = valueContent.toJsonLDValue( @@ -716,14 +736,15 @@ sealed trait ReadValueV2 extends IOValueV2 { datatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri ), OntologyConstants.KnoraApiV2Complex.ValueHasUUID -> JsonLDString( - stringFormatter.base64EncodeUuid(valueHasUUID)), + stringFormatter.base64EncodeUuid(valueHasUUID) + ), OntologyConstants.KnoraApiV2Complex.ArkUrl -> JsonLDUtil.datatypeValueToJsonLDObject( value = valueSmartIri.fromValueIriToArkUrl(valueUUID = valueHasUUID), datatype = OntologyConstants.Xsd.Uri.toSmartIri ), OntologyConstants.KnoraApiV2Complex.VersionArkUrl -> JsonLDUtil.datatypeValueToJsonLDObject( - value = valueSmartIri.fromValueIriToArkUrl(valueUUID = valueHasUUID, - maybeTimestamp = Some(valueCreationDate)), + value = valueSmartIri + .fromValueIriToArkUrl(valueUUID = valueHasUUID, maybeTimestamp = Some(valueCreationDate)), datatype = OntologyConstants.Xsd.Uri.toSmartIri ) ) @@ -741,7 +762,8 @@ sealed trait ReadValueV2 extends IOValueV2 { case other => throw AssertionException( - s"Expected value $valueIri to be a represented as a JSON-LD object in the complex schema, but found $other") + s"Expected value $valueIri to be a represented as a JSON-LD object in the complex schema, but found $other" + ) } case ApiV2Simple => valueContentAsJsonLD @@ -750,48 +772,50 @@ sealed trait ReadValueV2 extends IOValueV2 { } /** - * A text value, or a page of standoff markup attached to a text value, as read from the triplestore. - * - * @param valueIri the IRI of the value. - * @param attachedToUser the user that created the value. - * @param permissions the permissions that the value grants to user groups. - * @param userPermission the permission that the requesting user has on the value. - * @param valueHasUUID the UUID shared by all the versions of this value. - * @param valueContent the content of the value. - * @param valueHasMaxStandoffStartIndex if this text value has standoff markup, the highest - * `knora-base:standoffTagHasEndIndex` - * used in its standoff tags. - * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - * @param deletionInfo if this value has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ -case class ReadTextValueV2(valueIri: IRI, - attachedToUser: IRI, - permissions: String, - userPermission: EntityPermission, - valueCreationDate: Instant, - valueHasUUID: UUID, - valueContent: TextValueContentV2, - valueHasMaxStandoffStartIndex: Option[Int], - previousValueIri: Option[IRI], - deletionInfo: Option[DeletionInfo]) - extends ReadValueV2 + * A text value, or a page of standoff markup attached to a text value, as read from the triplestore. + * + * @param valueIri the IRI of the value. + * @param attachedToUser the user that created the value. + * @param permissions the permissions that the value grants to user groups. + * @param userPermission the permission that the requesting user has on the value. + * @param valueHasUUID the UUID shared by all the versions of this value. + * @param valueContent the content of the value. + * @param valueHasMaxStandoffStartIndex if this text value has standoff markup, the highest + * `knora-base:standoffTagHasEndIndex` + * used in its standoff tags. + * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + * @param deletionInfo if this value has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ +case class ReadTextValueV2( + valueIri: IRI, + attachedToUser: IRI, + permissions: String, + userPermission: EntityPermission, + valueCreationDate: Instant, + valueHasUUID: UUID, + valueContent: TextValueContentV2, + valueHasMaxStandoffStartIndex: Option[Int], + previousValueIri: Option[IRI], + deletionInfo: Option[DeletionInfo] +) extends ReadValueV2 with KnoraReadV2[ReadTextValueV2] { /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ - override def toOntologySchema(targetSchema: ApiV2Schema): ReadTextValueV2 = { + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ + override def toOntologySchema(targetSchema: ApiV2Schema): ReadTextValueV2 = copy(valueContent = valueContent.toOntologySchema(targetSchema)) - } - override def toJsonLD(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLD( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val valueAsJsonLDValue: JsonLDValue = super.toJsonLD( @@ -812,7 +836,8 @@ case class ReadTextValueV2(valueIri: IRI, case jsonLDObject: JsonLDObject => jsonLDObject case other => throw AssertionException( - s"Expected value $valueIri to be a represented as a JSON-LD object in the complex schema, but found $other") + s"Expected value $valueIri to be a represented as a JSON-LD object in the complex schema, but found $other" + ) } JsonLDObject( @@ -834,331 +859,343 @@ case class ReadTextValueV2(valueIri: IRI, } /** - * A link value as read from the triplestore. - * - * @param valueIri the IRI of the value. - * @param attachedToUser the user that created the value. - * @param permissions the permissions that the value grants to user groups. - * @param userPermission the permission that the requesting user has on the value. - * @param valueHasUUID the UUID shared by all the versions of this value. - * @param valueContent the content of the value. - * @param valueHasRefCount if this is a link value, its reference count. Not returned in API responses, but needed - * here for testing. - * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - * @param deletionInfo if this value has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ -case class ReadLinkValueV2(valueIri: IRI, - attachedToUser: IRI, - permissions: String, - userPermission: EntityPermission, - valueCreationDate: Instant, - valueHasUUID: UUID, - valueContent: LinkValueContentV2, - valueHasRefCount: Int, - previousValueIri: Option[IRI] = None, - deletionInfo: Option[DeletionInfo]) - extends ReadValueV2 + * A link value as read from the triplestore. + * + * @param valueIri the IRI of the value. + * @param attachedToUser the user that created the value. + * @param permissions the permissions that the value grants to user groups. + * @param userPermission the permission that the requesting user has on the value. + * @param valueHasUUID the UUID shared by all the versions of this value. + * @param valueContent the content of the value. + * @param valueHasRefCount if this is a link value, its reference count. Not returned in API responses, but needed + * here for testing. + * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + * @param deletionInfo if this value has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ +case class ReadLinkValueV2( + valueIri: IRI, + attachedToUser: IRI, + permissions: String, + userPermission: EntityPermission, + valueCreationDate: Instant, + valueHasUUID: UUID, + valueContent: LinkValueContentV2, + valueHasRefCount: Int, + previousValueIri: Option[IRI] = None, + deletionInfo: Option[DeletionInfo] +) extends ReadValueV2 with KnoraReadV2[ReadLinkValueV2] { /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ - override def toOntologySchema(targetSchema: ApiV2Schema): ReadLinkValueV2 = { + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ + override def toOntologySchema(targetSchema: ApiV2Schema): ReadLinkValueV2 = copy(valueContent = valueContent.toOntologySchema(targetSchema)) - } } /** - * A non-text, non-link value as read from the triplestore. - * - * @param valueIri the IRI of the value. - * @param attachedToUser the user that created the value. - * @param permissions the permissions that the value grants to user groups. - * @param userPermission the permission that the requesting user has on the value. - * @param valueHasUUID the UUID shared by all the versions of this value. - * @param valueContent the content of the value. - * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - * @param deletionInfo if this value has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ -case class ReadOtherValueV2(valueIri: IRI, - attachedToUser: IRI, - permissions: String, - userPermission: EntityPermission, - valueCreationDate: Instant, - valueHasUUID: UUID, - valueContent: ValueContentV2, - previousValueIri: Option[IRI], - deletionInfo: Option[DeletionInfo]) - extends ReadValueV2 + * A non-text, non-link value as read from the triplestore. + * + * @param valueIri the IRI of the value. + * @param attachedToUser the user that created the value. + * @param permissions the permissions that the value grants to user groups. + * @param userPermission the permission that the requesting user has on the value. + * @param valueHasUUID the UUID shared by all the versions of this value. + * @param valueContent the content of the value. + * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + * @param deletionInfo if this value has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ +case class ReadOtherValueV2( + valueIri: IRI, + attachedToUser: IRI, + permissions: String, + userPermission: EntityPermission, + valueCreationDate: Instant, + valueHasUUID: UUID, + valueContent: ValueContentV2, + previousValueIri: Option[IRI], + deletionInfo: Option[DeletionInfo] +) extends ReadValueV2 with KnoraReadV2[ReadOtherValueV2] { /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ - override def toOntologySchema(targetSchema: ApiV2Schema): ReadOtherValueV2 = { + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ + override def toOntologySchema(targetSchema: ApiV2Schema): ReadOtherValueV2 = copy(valueContent = valueContent.toOntologySchema(targetSchema)) - } } /** - * Represents a Knora value to be created in an existing resource. - * - * @param resourceIri the resource the new value should be attached to. - * @param resourceClassIri the resource class that the client believes the resource belongs to. - * @param propertyIri the property of the new value. If the client wants to create a link, this must be a link value property. - * @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]]. - * @param valueIri the optional custom IRI supplied for the value. - * @param valueUUID the optional custom UUID supplied for the value. - * @param valueCreationDate the optional custom creation date supplied for the value. If not supplied, - * the current time will be used. - * @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults. - */ -case class CreateValueV2(resourceIri: IRI, - resourceClassIri: SmartIri, - propertyIri: SmartIri, - valueContent: ValueContentV2, - valueIri: Option[SmartIri] = None, - valueUUID: Option[UUID] = None, - valueCreationDate: Option[Instant] = None, - permissions: Option[String] = None) - extends IOValueV2 - -/** - * A trait for classes representing information to be updated in a value. - */ + * Represents a Knora value to be created in an existing resource. + * + * @param resourceIri the resource the new value should be attached to. + * @param resourceClassIri the resource class that the client believes the resource belongs to. + * @param propertyIri the property of the new value. If the client wants to create a link, this must be a link value property. + * @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]]. + * @param valueIri the optional custom IRI supplied for the value. + * @param valueUUID the optional custom UUID supplied for the value. + * @param valueCreationDate the optional custom creation date supplied for the value. If not supplied, + * the current time will be used. + * @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults. + */ +case class CreateValueV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + propertyIri: SmartIri, + valueContent: ValueContentV2, + valueIri: Option[SmartIri] = None, + valueUUID: Option[UUID] = None, + valueCreationDate: Option[Instant] = None, + permissions: Option[String] = None +) extends IOValueV2 + +/** + * A trait for classes representing information to be updated in a value. + */ trait UpdateValueV2 { /** - * The IRI of the resource containing the value. - */ + * The IRI of the resource containing the value. + */ val resourceIri: IRI /** - * The external IRI of the resource class. - */ + * The external IRI of the resource class. + */ val resourceClassIri: SmartIri /** - * The external IRI of the property pointing to the value. - */ + * The external IRI of the property pointing to the value. + */ val propertyIri: SmartIri /** - * The value IRI. - */ + * The value IRI. + */ val valueIri: IRI /** - * A custom value creation date. - */ + * A custom value creation date. + */ val valueCreationDate: Option[Instant] } /** - * A new version of a value of a Knora property to be created. - * - * @param resourceIri the resource that the current value version is attached to. - * @param resourceClassIri the resource class that the client believes the resource belongs to. - * @param propertyIri the property that the client believes points to the value. If the value is a link value, - * this must be a link value property. - * @param valueIri the IRI of the value to be updated. - * @param valueContent the content of the new version of the value. - * @param permissions the permissions to be attached to the new value version. - * @param valueCreationDate an optional custom creation date to be attached to the new value version. If not provided, - * the current time will be used. - * @param newValueVersionIri an optional IRI to be used for the new value version. If not provided, a random IRI - * will be generated. - */ -case class UpdateValueContentV2(resourceIri: IRI, - resourceClassIri: SmartIri, - propertyIri: SmartIri, - valueIri: IRI, - valueContent: ValueContentV2, - permissions: Option[String] = None, - valueCreationDate: Option[Instant] = None, - newValueVersionIri: Option[SmartIri] = None) - extends IOValueV2 + * A new version of a value of a Knora property to be created. + * + * @param resourceIri the resource that the current value version is attached to. + * @param resourceClassIri the resource class that the client believes the resource belongs to. + * @param propertyIri the property that the client believes points to the value. If the value is a link value, + * this must be a link value property. + * @param valueIri the IRI of the value to be updated. + * @param valueContent the content of the new version of the value. + * @param permissions the permissions to be attached to the new value version. + * @param valueCreationDate an optional custom creation date to be attached to the new value version. If not provided, + * the current time will be used. + * @param newValueVersionIri an optional IRI to be used for the new value version. If not provided, a random IRI + * will be generated. + */ +case class UpdateValueContentV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + propertyIri: SmartIri, + valueIri: IRI, + valueContent: ValueContentV2, + permissions: Option[String] = None, + valueCreationDate: Option[Instant] = None, + newValueVersionIri: Option[SmartIri] = None +) extends IOValueV2 with UpdateValueV2 /** - * New permissions for a value. - * - * @param resourceIri the resource that the current value version is attached to. - * @param resourceClassIri the resource class that the client believes the resource belongs to. - * @param propertyIri the property that the client believes points to the value. If the value is a link value, - * this must be a link value property. - * @param valueIri the IRI of the value to be updated. - * @param valueType the IRI of the value type. - * @param permissions the permissions to be attached to the new value version. - * @param valueCreationDate an optional custom creation date to be attached to the new value version. If not provided, - * the current time will be used. - * @param newValueVersionIri an optional IRI to be used for the new value version. If not provided, a random IRI - * will be generated. - */ -case class UpdateValuePermissionsV2(resourceIri: IRI, - resourceClassIri: SmartIri, - propertyIri: SmartIri, - valueIri: IRI, - valueType: SmartIri, - permissions: String, - valueCreationDate: Option[Instant] = None, - newValueVersionIri: Option[SmartIri] = None) - extends UpdateValueV2 - -/** - * The IRI and content of a new value or value version whose existence in the triplestore needs to be verified. - * - * @param newValueIri the IRI that was assigned to the new value. - * @param newValueUUID the UUID attached to the new value. - * @param valueContent the content of the new value (unescaped, as it would be read from the triplestore). - * @param permissions the permissions of the new value. - * @param creationDate the new value's creation date. - */ -case class UnverifiedValueV2(newValueIri: IRI, - newValueUUID: UUID, - valueContent: ValueContentV2, - permissions: String, - creationDate: Instant) - -/** - * The content of the value of a Knora property. - */ + * New permissions for a value. + * + * @param resourceIri the resource that the current value version is attached to. + * @param resourceClassIri the resource class that the client believes the resource belongs to. + * @param propertyIri the property that the client believes points to the value. If the value is a link value, + * this must be a link value property. + * @param valueIri the IRI of the value to be updated. + * @param valueType the IRI of the value type. + * @param permissions the permissions to be attached to the new value version. + * @param valueCreationDate an optional custom creation date to be attached to the new value version. If not provided, + * the current time will be used. + * @param newValueVersionIri an optional IRI to be used for the new value version. If not provided, a random IRI + * will be generated. + */ +case class UpdateValuePermissionsV2( + resourceIri: IRI, + resourceClassIri: SmartIri, + propertyIri: SmartIri, + valueIri: IRI, + valueType: SmartIri, + permissions: String, + valueCreationDate: Option[Instant] = None, + newValueVersionIri: Option[SmartIri] = None +) extends UpdateValueV2 + +/** + * The IRI and content of a new value or value version whose existence in the triplestore needs to be verified. + * + * @param newValueIri the IRI that was assigned to the new value. + * @param newValueUUID the UUID attached to the new value. + * @param valueContent the content of the new value (unescaped, as it would be read from the triplestore). + * @param permissions the permissions of the new value. + * @param creationDate the new value's creation date. + */ +case class UnverifiedValueV2( + newValueIri: IRI, + newValueUUID: UUID, + valueContent: ValueContentV2, + permissions: String, + creationDate: Instant +) + +/** + * The content of the value of a Knora property. + */ sealed trait ValueContentV2 extends KnoraContentV2[ValueContentV2] { protected implicit def stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * The IRI of the value type. - */ + * The IRI of the value type. + */ def valueType: SmartIri /** - * The string representation of this `ValueContentV2`. - */ + * The string representation of this `ValueContentV2`. + */ def valueHasString: String /** - * a comment on this [[ValueContentV2]], if any. - */ + * a comment on this [[ValueContentV2]], if any. + */ def comment: Option[String] /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ def toOntologySchema(targetSchema: OntologySchema): ValueContentV2 /** - * A representation of the `ValueContentV2` as a [[JsonLDValue]]. - * - * @param targetSchema the API schema to be used. - * @param settings the configuration options. - * @return a [[JsonLDValue]] that can be used to generate JSON-LD representing this value. - */ - def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue + * A representation of the `ValueContentV2` as a [[JsonLDValue]]. + * + * @param targetSchema the API schema to be used. + * @param settings the configuration options. + * @return a [[JsonLDValue]] that can be used to generate JSON-LD representing this value. + */ + def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue /** - * Undoes the SPARQL-escaping of strings in this [[ValueContentV2]]. - * - * @return the same [[ValueContentV2]] with its strings unescaped. - */ + * Undoes the SPARQL-escaping of strings in this [[ValueContentV2]]. + * + * @return the same [[ValueContentV2]] with its strings unescaped. + */ def unescape: ValueContentV2 /** - * Returns `true` if creating this [[ValueContentV2]] as a new value would duplicate the specified other value. - * This means that if resource `R` has property `P` with value `V1`, and `V1` would duplicate `V2`, the API server - * should not add another instance of property `P` with value `V2`. It does not necessarily mean that `V1 == V2`. - * - * @param that a [[ValueContentV2]] in the same resource, as read from the triplestore. - * @return `true` if `other` would duplicate `this`. - */ + * Returns `true` if creating this [[ValueContentV2]] as a new value would duplicate the specified other value. + * This means that if resource `R` has property `P` with value `V1`, and `V1` would duplicate `V2`, the API server + * should not add another instance of property `P` with value `V2`. It does not necessarily mean that `V1 == V2`. + * + * @param that a [[ValueContentV2]] in the same resource, as read from the triplestore. + * @return `true` if `other` would duplicate `this`. + */ def wouldDuplicateOtherValue(that: ValueContentV2): Boolean /** - * Returns `true` if this [[ValueContentV2]] would be redundant as a new version of an existing value. This means - * that if resource `R` has property `P` with value `V1`, and `V2` would duplicate `V1`, we should not add `V2` - * as a new version of `V1`. It does not necessarily mean that `V1 == V2`. - * - * @param currentVersion the current version of the value, as read from the triplestore. - * @return `true` if this [[ValueContentV2]] would duplicate `currentVersion`. - */ + * Returns `true` if this [[ValueContentV2]] would be redundant as a new version of an existing value. This means + * that if resource `R` has property `P` with value `V1`, and `V2` would duplicate `V1`, we should not add `V2` + * as a new version of `V1`. It does not necessarily mean that `V1 == V2`. + * + * @param currentVersion the current version of the value, as read from the triplestore. + * @return `true` if this [[ValueContentV2]] would duplicate `currentVersion`. + */ def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean } /** - * A trait for objects that can convert JSON-LD objects into value content objects (subclasses of [[ValueContentV2]]). - * - * @tparam C a subclass of [[ValueContentV2]]. - */ + * A trait for objects that can convert JSON-LD objects into value content objects (subclasses of [[ValueContentV2]]). + * + * @tparam C a subclass of [[ValueContentV2]]. + */ trait ValueContentReaderV2[C <: ValueContentV2] { /** - * Converts a JSON-LD object to a subclass of [[ValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a subclass of [[ValueContentV2]]. - */ - def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[C] - - protected def getComment(jsonLDObject: JsonLDObject)(implicit stringFormatter: StringFormatter): Option[String] = { - jsonLDObject.maybeStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasComment, - stringFormatter.toSparqlEncodedString) - } + * Converts a JSON-LD object to a subclass of [[ValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a subclass of [[ValueContentV2]]. + */ + def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[C] + + protected def getComment(jsonLDObject: JsonLDObject)(implicit stringFormatter: StringFormatter): Option[String] = + jsonLDObject.maybeStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasComment, + stringFormatter.toSparqlEncodedString + ) } /** - * Generates instances of value content classes (subclasses of [[ValueContentV2]]) from JSON-LD input. - */ + * Generates instances of value content classes (subclasses of [[ValueContentV2]]) from JSON-LD input. + */ object ValueContentV2 extends ValueContentReaderV2[ValueContentV2] { /** - * Converts a JSON-LD object to a [[ValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[ValueContentV2]]. - */ + * Converts a JSON-LD object to a [[ValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[ValueContentV2]]. + */ override def fromJsonLDObject( - jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ValueContentV2] = { + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { valueType: SmartIri <- Future( - jsonLDObject.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.toSmartIriWithErr)) + jsonLDObject.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.toSmartIriWithErr) + ) valueContent: ValueContentV2 <- valueType.toString match { case OntologyConstants.KnoraApiV2Complex.TextValue => @@ -1367,23 +1404,24 @@ object ValueContentV2 extends ValueContentReaderV2[ValueContentV2] { } /** - * Represents a Knora date value. - * - * @param valueHasStartJDN the start of the date as JDN. - * @param valueHasEndJDN the end of the date as JDN. - * @param valueHasStartPrecision the precision of the start date. - * @param valueHasEndPrecision the precision of the end date. - * @param valueHasCalendar the calendar of the date. - * @param comment a comment on this [[DateValueContentV2]], if any. - */ -case class DateValueContentV2(ontologySchema: OntologySchema, - valueHasStartJDN: Int, - valueHasEndJDN: Int, - valueHasStartPrecision: DatePrecisionV2, - valueHasEndPrecision: DatePrecisionV2, - valueHasCalendar: CalendarNameV2, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Knora date value. + * + * @param valueHasStartJDN the start of the date as JDN. + * @param valueHasEndJDN the end of the date as JDN. + * @param valueHasStartPrecision the precision of the start date. + * @param valueHasEndPrecision the precision of the end date. + * @param valueHasCalendar the calendar of the date. + * @param comment a comment on this [[DateValueContentV2]], if any. + */ +case class DateValueContentV2( + ontologySchema: OntologySchema, + valueHasStartJDN: Int, + valueHasEndJDN: Int, + valueHasStartPrecision: DatePrecisionV2, + valueHasEndPrecision: DatePrecisionV2, + valueHasCalendar: CalendarNameV2, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.DateValue.toSmartIri.toOntologySchema(ontologySchema) @@ -1414,10 +1452,12 @@ case class DateValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): DateValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.datatypeValueToJsonLDObject( @@ -1429,36 +1469,42 @@ case class DateValueContentV2(ontologySchema: OntologySchema, val startCalendarDate: CalendarDateV2 = asCalendarDateRange.startCalendarDate val endCalendarDate: CalendarDateV2 = asCalendarDateRange.endCalendarDate - val startDateAssertions = Map( - OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear -> JsonLDInt(startCalendarDate.year)) ++ - startCalendarDate.maybeMonth.map(month => - OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth -> JsonLDInt(month)) ++ - startCalendarDate.maybeDay.map(day => - OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay -> JsonLDInt(day)) ++ - startCalendarDate.maybeEra.map(era => - OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra -> JsonLDString(era.toString)) - - val endDateAssertions = Map( - OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear -> JsonLDInt(endCalendarDate.year)) ++ - endCalendarDate.maybeMonth.map(month => - OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth -> JsonLDInt(month)) ++ - endCalendarDate.maybeDay.map(day => OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay -> JsonLDInt(day)) ++ - endCalendarDate.maybeEra.map(era => - OntologyConstants.KnoraApiV2Complex.DateValueHasEndEra -> JsonLDString(era.toString)) + val startDateAssertions = + Map(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear -> JsonLDInt(startCalendarDate.year)) ++ + startCalendarDate.maybeMonth.map(month => + OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth -> JsonLDInt(month) + ) ++ + startCalendarDate.maybeDay.map(day => + OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay -> JsonLDInt(day) + ) ++ + startCalendarDate.maybeEra.map(era => + OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra -> JsonLDString(era.toString) + ) + + val endDateAssertions = + Map(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear -> JsonLDInt(endCalendarDate.year)) ++ + endCalendarDate.maybeMonth.map(month => + OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth -> JsonLDInt(month) + ) ++ + endCalendarDate.maybeDay.map(day => + OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay -> JsonLDInt(day) + ) ++ + endCalendarDate.maybeEra.map(era => + OntologyConstants.KnoraApiV2Complex.DateValueHasEndEra -> JsonLDString(era.toString) + ) JsonLDObject( Map( OntologyConstants.KnoraApiV2Complex.ValueAsString -> JsonLDString(valueHasString), OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar -> JsonLDString(valueHasCalendar.toString) - ) ++ startDateAssertions ++ endDateAssertions) + ) ++ startDateAssertions ++ endDateAssertions + ) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatDateValue: DateValueContentV2 => valueHasStartJDN == thatDateValue.valueHasStartJDN && @@ -1469,9 +1515,8 @@ case class DateValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatDateValue: DateValueContentV2 => valueHasStartJDN == thatDateValue.valueHasStartJDN && @@ -1483,20 +1528,19 @@ case class DateValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[DateValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[DateValueContentV2]] objects based on JSON-LD input. + */ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { /** - * Parses a string representing a date range in API v2 simple format. - * - * @param dateStr the string to be parsed. - * @return a [[DateValueContentV2]] representing the date range. - */ + * Parses a string representing a date range in API v2 simple format. + * + * @param dateStr the string to be parsed. + * @return a [[DateValueContentV2]] representing the date range. + */ def parse(dateStr: String): DateValueContentV2 = { val dateRange: CalendarDateRangeV2 = CalendarDateRangeV2.parse(dateStr) val (startJDN: Int, endJDN: Int) = dateRange.toJulianDayRange @@ -1512,27 +1556,27 @@ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { } /** - * Converts a JSON-LD object to a [[DateValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[DateValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[DateValueContentV2] = { + * Converts a JSON-LD object to a [[DateValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[DateValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[DateValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): DateValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -1541,7 +1585,8 @@ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { val calendarName: CalendarNameV2 = jsonLDObject.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar, - CalendarNameV2.parse) + CalendarNameV2.parse + ) val dateValueHasStartYear: Int = jsonLDObject.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) val maybeDateValueHasStartMonth: Option[Int] = @@ -1620,63 +1665,65 @@ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { } /** - * Represents a [[StandoffTagV2]] for a standoff tag of a certain type (standoff tag class) that is about to be created in the triplestore. - * - * @param standoffNode the standoff node to be created. - * @param standoffTagInstanceIri the standoff node's IRI. - * @param startParentIri the IRI of the parent of the start tag. - * @param endParentIri the IRI of the parent of the end tag, if any. - */ -case class CreateStandoffTagV2InTriplestore(standoffNode: StandoffTagV2, - standoffTagInstanceIri: IRI, - startParentIri: Option[IRI] = None, - endParentIri: Option[IRI] = None) - -/** - * Represents a Knora text value, or a page of standoff markup that will be included in a text value. - * - * @param maybeValueHasString the string representation of this text value, if available. - * @param standoff the standoff markup attached to the text value, if any. - * @param mappingIri the IRI of the [[MappingXMLtoStandoff]] used by default with the text value, if any. - * @param mapping the [[MappingXMLtoStandoff]] used by default with the text value, if any. - * @param comment a comment on this [[TextValueContentV2]], if any. - */ -case class TextValueContentV2(ontologySchema: OntologySchema, - maybeValueHasString: Option[String], - valueHasLanguage: Option[String] = None, - standoff: Seq[StandoffTagV2] = Vector.empty, - mappingIri: Option[IRI] = None, - mapping: Option[MappingXMLtoStandoff] = None, - xslt: Option[String] = None, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a [[StandoffTagV2]] for a standoff tag of a certain type (standoff tag class) that is about to be created in the triplestore. + * + * @param standoffNode the standoff node to be created. + * @param standoffTagInstanceIri the standoff node's IRI. + * @param startParentIri the IRI of the parent of the start tag. + * @param endParentIri the IRI of the parent of the end tag, if any. + */ +case class CreateStandoffTagV2InTriplestore( + standoffNode: StandoffTagV2, + standoffTagInstanceIri: IRI, + startParentIri: Option[IRI] = None, + endParentIri: Option[IRI] = None +) + +/** + * Represents a Knora text value, or a page of standoff markup that will be included in a text value. + * + * @param maybeValueHasString the string representation of this text value, if available. + * @param standoff the standoff markup attached to the text value, if any. + * @param mappingIri the IRI of the [[MappingXMLtoStandoff]] used by default with the text value, if any. + * @param mapping the [[MappingXMLtoStandoff]] used by default with the text value, if any. + * @param comment a comment on this [[TextValueContentV2]], if any. + */ +case class TextValueContentV2( + ontologySchema: OntologySchema, + maybeValueHasString: Option[String], + valueHasLanguage: Option[String] = None, + standoff: Seq[StandoffTagV2] = Vector.empty, + mappingIri: Option[IRI] = None, + mapping: Option[MappingXMLtoStandoff] = None, + xslt: Option[String] = None, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.TextValue.toSmartIri.toOntologySchema(ontologySchema) } /** - * Returns the IRIs of any resources that are target of standoff link tags in this text value. - */ + * Returns the IRIs of any resources that are target of standoff link tags in this text value. + */ lazy val standoffLinkTagTargetResourceIris: Set[IRI] = { standoffLinkTagIriAttributes.map(_.value) } /** - * Returns the IRI attributes representing the target IRIs of any standoff links in this text value. - */ + * Returns the IRI attributes representing the target IRIs of any standoff links in this text value. + */ lazy val standoffLinkTagIriAttributes: Set[StandoffTagIriAttributeV2] = { - standoff.foldLeft(Set.empty[StandoffTagIriAttributeV2]) { - case (acc, standoffTag: StandoffTagV2) => - if (standoffTag.dataType.contains(StandoffDataTypeClasses.StandoffLinkTag)) { - val iriAttributes: Set[StandoffTagIriAttributeV2] = standoffTag.attributes.collect { - case iriAttribute: StandoffTagIriAttributeV2 => iriAttribute - }.toSet - - acc ++ iriAttributes - } else { - acc - } + standoff.foldLeft(Set.empty[StandoffTagIriAttributeV2]) { case (acc, standoffTag: StandoffTagV2) => + if (standoffTag.dataType.contains(StandoffDataTypeClasses.StandoffLinkTag)) { + val iriAttributes: Set[StandoffTagIriAttributeV2] = standoffTag.attributes.collect { + case iriAttribute: StandoffTagIriAttributeV2 => iriAttribute + }.toSet + + acc ++ iriAttributes + } else { + acc + } } } @@ -1685,16 +1732,16 @@ case class TextValueContentV2(ontologySchema: OntologySchema, maybeValueHasString.getOrElse(throw AssertionException("Text value has no valueHasString")) /** - * The content of the text value without standoff, suitable for returning in API responses. This removes - * INFORMATION SEPARATOR TWO, which is used only internally. - */ + * The content of the text value without standoff, suitable for returning in API responses. This removes + * INFORMATION SEPARATOR TWO, which is used only internally. + */ lazy val valueHasStringWithoutStandoff: String = valueHasString.replace(StringFormatter.INFORMATION_SEPARATOR_TWO.toString, "") /** - * The maximum start index in the standoff attached to this [[TextValueContentV2]]. This is used - * only when writing a text value to the triplestore. - */ + * The maximum start index in the standoff attached to this [[TextValueContentV2]]. This is used + * only when writing a text value to the triplestore. + */ lazy val computedMaxStandoffStartIndex: Option[Int] = if (standoff.nonEmpty) { Some(standoff.map(_.startIndex).max) } else { @@ -1703,10 +1750,12 @@ case class TextValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): TextValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => valueHasLanguage match { @@ -1722,10 +1771,10 @@ case class TextValueContentV2(ontologySchema: OntologySchema, } case ApiV2Complex => - val renderStandoffAsXml: Boolean = standoff.nonEmpty && SchemaOptions.renderMarkupAsXml(targetSchema = - targetSchema, - schemaOptions = - schemaOptions) + val renderStandoffAsXml: Boolean = standoff.nonEmpty && SchemaOptions.renderMarkupAsXml( + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) // Should we render standoff as XML? val objectMap: Map[IRI, JsonLDValue] = if (renderStandoffAsXml) { @@ -1758,7 +1807,8 @@ case class TextValueContentV2(ontologySchema: OntologySchema, Map( OntologyConstants.KnoraApiV2Complex.TextValueAsXml -> JsonLDString(xmlFromStandoff), OntologyConstants.KnoraApiV2Complex.TextValueHasMapping -> JsonLDUtil.iriToJsonLDObject( - definedMappingIri) + definedMappingIri + ) ) } } else { @@ -1777,16 +1827,14 @@ case class TextValueContentV2(ontologySchema: OntologySchema, JsonLDObject(objectMapWithLanguage) } - } /** - * A convenience method that creates an IRI for each [[StandoffTagV2]] and resolves internal references to standoff node Iris. - * - * @return a list of [[CreateStandoffTagV2InTriplestore]] each representing a [[StandoffTagV2]] object - * along with is standoff tag class and IRI that is going to identify it in the triplestore. - */ - def prepareForSparqlInsert(valueIri: IRI): Seq[CreateStandoffTagV2InTriplestore] = { - + * A convenience method that creates an IRI for each [[StandoffTagV2]] and resolves internal references to standoff node Iris. + * + * @return a list of [[CreateStandoffTagV2InTriplestore]] each representing a [[StandoffTagV2]] object + * along with is standoff tag class and IRI that is going to identify it in the triplestore. + */ + def prepareForSparqlInsert(valueIri: IRI): Seq[CreateStandoffTagV2InTriplestore] = if (standoff.nonEmpty) { // create an IRI for each standoff tag // internal references to XML ids are not resolved yet @@ -1796,22 +1844,21 @@ case class TextValueContentV2(ontologySchema: OntologySchema, standoffNode = standoffNode, standoffTagInstanceIri = stringFormatter.makeRandomStandoffTagIri( valueIri = valueIri, - startIndex = standoffNode.startIndex) // generate IRI for new standoff node + startIndex = standoffNode.startIndex + ) // generate IRI for new standoff node ) } // collect all the standoff tags that contain XML ids and // map the XML ids to standoff node Iris - val iDsToStandoffNodeIris: Map[IRI, IRI] = standoffTagsWithOriginalXMLIDs - .filter { standoffTag: CreateStandoffTagV2InTriplestore => + val iDsToStandoffNodeIris: Map[IRI, IRI] = standoffTagsWithOriginalXMLIDs.filter { + standoffTag: CreateStandoffTagV2InTriplestore => // filter those tags out that have an XML id standoffTag.standoffNode.originalXMLID.isDefined - } - .map { standoffTagWithID: CreateStandoffTagV2InTriplestore => - // return the XML id as a key and the standoff IRI as the value - standoffTagWithID.standoffNode.originalXMLID.get -> standoffTagWithID.standoffTagInstanceIri - } - .toMap + }.map { standoffTagWithID: CreateStandoffTagV2InTriplestore => + // return the XML id as a key and the standoff IRI as the value + standoffTagWithID.standoffNode.originalXMLID.get -> standoffTagWithID.standoffTagInstanceIri + }.toMap // Map the start index of each tag to its IRI, so we can resolve references to parent tags as references to // tag IRIs. We only care about start indexes here, because only hierarchical tags can be parents, and @@ -1840,8 +1887,12 @@ case class TextValueContentV2(ontologySchema: OntologySchema, // return standoff tag with updated attributes standoffTag.copy( standoffNode = standoffTag.standoffNode.copy(attributes = attributesWithStandoffNodeIriReferences), - startParentIri = startParentIndex.map(parentIndex => startIndexesToStandoffNodeIris(parentIndex)), // If there's a start parent index, get its IRI, otherwise None - endParentIri = endParentIndex.map(parentIndex => startIndexesToStandoffNodeIris(parentIndex)) // If there's an end parent index, get its IRI, otherwise None + startParentIri = startParentIndex.map(parentIndex => + startIndexesToStandoffNodeIris(parentIndex) + ), // If there's a start parent index, get its IRI, otherwise None + endParentIri = endParentIndex.map(parentIndex => + startIndexesToStandoffNodeIris(parentIndex) + ) // If there's an end parent index, get its IRI, otherwise None ) } @@ -1850,8 +1901,6 @@ case class TextValueContentV2(ontologySchema: OntologySchema, Seq.empty[CreateStandoffTagV2InTriplestore] } - } - override def unescape: ValueContentV2 = { // Unescape the text in standoff string attributes. val unescapedStandoff = standoff.map { standoffTag => @@ -1872,7 +1921,7 @@ case class TextValueContentV2(ontologySchema: OntologySchema, ) } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = // It doesn't make sense for a resource to have two different text values associated with the same property, // containing the same text but different markup. that match { @@ -1880,9 +1929,8 @@ case class TextValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = // It's OK to add a new version of a text value as long as something has been changed in it, even if it's only the markup // or the comment. currentVersion match { @@ -1899,47 +1947,52 @@ case class TextValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[TextValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[TextValueContentV2]] objects based on JSON-LD input. + */ object TextValueContentV2 extends ValueContentReaderV2[TextValueContentV2] { /** - * Converts a JSON-LD object to a [[TextValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application settings. - * @param log a logging adapter. - * @return a [[TextValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TextValueContentV2] = { + * Converts a JSON-LD object to a [[TextValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application settings. + * @param log a logging adapter. + * @return a [[TextValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TextValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { maybeValueAsString: Option[String] <- Future( - jsonLDObject.maybeStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueAsString, - stringFormatter.toSparqlEncodedString)) + jsonLDObject.maybeStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueAsString, + stringFormatter.toSparqlEncodedString + ) + ) maybeValueHasLanguage: Option[String] = jsonLDObject.maybeStringWithValidation( OntologyConstants.KnoraApiV2Complex.TextValueHasLanguage, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) maybeTextValueAsXml: Option[String] = jsonLDObject.maybeString(OntologyConstants.KnoraApiV2Complex.TextValueAsXml) maybeMappingIri: Option[IRI] = jsonLDObject.maybeIriInObject( OntologyConstants.KnoraApiV2Complex.TextValueHasMapping, - stringFormatter.validateAndEscapeIri) + stringFormatter.validateAndEscapeIri + ) // If the client supplied the IRI of a standoff-to-XML mapping, get the mapping. @@ -1980,7 +2033,9 @@ object TextValueContentV2 extends ValueContentReaderV2[TextValueContentV2] { maybeValueHasString = Some( stringFormatter.toSparqlEncodedString( textWithStandoffTags.text, - throw BadRequestException("Text value contains invalid characters"))), + throw BadRequestException("Text value contains invalid characters") + ) + ), valueHasLanguage = maybeValueHasLanguage, standoff = textWithStandoffTags.standoffTagV2, mappingIri = Some(mappingResponse.mappingIri), @@ -1990,7 +2045,8 @@ object TextValueContentV2 extends ValueContentReaderV2[TextValueContentV2] { case _ => throw BadRequestException( - s"Invalid combination of knora-api:valueHasString, knora-api:textValueAsXml, and/or knora-api:textValueHasMapping") + s"Invalid combination of knora-api:valueHasString, knora-api:textValueAsXml, and/or knora-api:textValueHasMapping" + ) } } yield textValue @@ -1998,11 +2054,11 @@ object TextValueContentV2 extends ValueContentReaderV2[TextValueContentV2] { } /** - * Represents a Knora integer value. - * - * @param valueHasInteger the integer value. - * @param comment a comment on this [[IntegerValueContentV2]], if any. - */ + * Represents a Knora integer value. + * + * @param valueHasInteger the integer value. + * @param comment a comment on this [[IntegerValueContentV2]], if any. + */ case class IntegerValueContentV2(ontologySchema: OntologySchema, valueHasInteger: Int, comment: Option[String] = None) extends ValueContentV2 { override def valueType: SmartIri = { @@ -2015,10 +2071,12 @@ case class IntegerValueContentV2(ontologySchema: OntologySchema, valueHasInteger override def toOntologySchema(targetSchema: OntologySchema): IntegerValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDInt(valueHasInteger) @@ -2026,20 +2084,17 @@ case class IntegerValueContentV2(ontologySchema: OntologySchema, valueHasInteger JsonLDObject(Map(OntologyConstants.KnoraApiV2Complex.IntValueAsInt -> JsonLDInt(valueHasInteger))) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatIntegerValue: IntegerValueContentV2 => valueHasInteger == thatIntegerValue.valueHasInteger case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatIntegerValue: IntegerValueContentV2 => valueHasInteger == thatIntegerValue.valueHasInteger && @@ -2047,37 +2102,35 @@ case class IntegerValueContentV2(ontologySchema: OntologySchema, valueHasInteger case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[IntegerValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[IntegerValueContentV2]] objects based on JSON-LD input. + */ object IntegerValueContentV2 extends ValueContentReaderV2[IntegerValueContentV2] { /** - * Converts a JSON-LD object to an [[IntegerValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[IntegerValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[IntegerValueContentV2] = { + * Converts a JSON-LD object to an [[IntegerValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[IntegerValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[IntegerValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): IntegerValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2093,15 +2146,16 @@ object IntegerValueContentV2 extends ValueContentReaderV2[IntegerValueContentV2] } /** - * Represents a Knora decimal value. - * - * @param valueHasDecimal the decimal value. - * @param comment a comment on this [[DecimalValueContentV2]], if any. - */ -case class DecimalValueContentV2(ontologySchema: OntologySchema, - valueHasDecimal: BigDecimal, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Knora decimal value. + * + * @param valueHasDecimal the decimal value. + * @param comment a comment on this [[DecimalValueContentV2]], if any. + */ +case class DecimalValueContentV2( + ontologySchema: OntologySchema, + valueHasDecimal: BigDecimal, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.DecimalValue.toSmartIri.toOntologySchema(ontologySchema) @@ -2112,10 +2166,12 @@ case class DecimalValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): DecimalValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val decimalValueAsJsonLDObject = JsonLDUtil.datatypeValueToJsonLDObject( value = valueHasDecimal.toString, datatype = OntologyConstants.Xsd.Decimal.toSmartIri @@ -2129,18 +2185,16 @@ case class DecimalValueContentV2(ontologySchema: OntologySchema, } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatDecimalValue: DecimalValueContentV2 => valueHasDecimal == thatDecimalValue.valueHasDecimal case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatDecimalValue: DecimalValueContentV2 => valueHasDecimal == thatDecimalValue.valueHasDecimal && @@ -2148,37 +2202,35 @@ case class DecimalValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[DecimalValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[DecimalValueContentV2]] objects based on JSON-LD input. + */ object DecimalValueContentV2 extends ValueContentReaderV2[DecimalValueContentV2] { /** - * Converts a JSON-LD object to a [[DecimalValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[DecimalValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[DecimalValueContentV2] = { + * Converts a JSON-LD object to a [[DecimalValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[DecimalValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[DecimalValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): DecimalValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2198,15 +2250,16 @@ object DecimalValueContentV2 extends ValueContentReaderV2[DecimalValueContentV2] } /** - * Represents a Boolean value. - * - * @param valueHasBoolean the Boolean value. - * @param comment a comment on this [[BooleanValueContentV2]], if any. - */ -case class BooleanValueContentV2(ontologySchema: OntologySchema, - valueHasBoolean: Boolean, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Boolean value. + * + * @param valueHasBoolean the Boolean value. + * @param comment a comment on this [[BooleanValueContentV2]], if any. + */ +case class BooleanValueContentV2( + ontologySchema: OntologySchema, + valueHasBoolean: Boolean, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.BooleanValue.toSmartIri.toOntologySchema(ontologySchema) @@ -2217,28 +2270,27 @@ case class BooleanValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): BooleanValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDBoolean(valueHasBoolean) case ApiV2Complex => JsonLDObject(Map(OntologyConstants.KnoraApiV2Complex.BooleanValueAsBoolean -> JsonLDBoolean(valueHasBoolean))) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = // Always returns true, because it doesn't make sense to have two instances of the same boolean property. true - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatBooleanValue: BooleanValueContentV2 => valueHasBoolean == thatBooleanValue.valueHasBoolean && @@ -2246,37 +2298,35 @@ case class BooleanValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[BooleanValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[BooleanValueContentV2]] objects based on JSON-LD input. + */ object BooleanValueContentV2 extends ValueContentReaderV2[BooleanValueContentV2] { /** - * Converts a JSON-LD object to a [[BooleanValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[BooleanValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[BooleanValueContentV2] = { + * Converts a JSON-LD object to a [[BooleanValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[BooleanValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[BooleanValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): BooleanValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2293,11 +2343,11 @@ object BooleanValueContentV2 extends ValueContentReaderV2[BooleanValueContentV2] } /** - * Represents a Knora geometry value (a 2D-shape). - * - * @param valueHasGeometry JSON representing a 2D geometrical shape. - * @param comment a comment on this [[GeomValueContentV2]], if any. - */ + * Represents a Knora geometry value (a 2D-shape). + * + * @param valueHasGeometry JSON representing a 2D geometrical shape. + * @param comment a comment on this [[GeomValueContentV2]], if any. + */ case class GeomValueContentV2(ontologySchema: OntologySchema, valueHasGeometry: String, comment: Option[String] = None) extends ValueContentV2 { override def valueType: SmartIri = { @@ -2309,10 +2359,12 @@ case class GeomValueContentV2(ontologySchema: OntologySchema, valueHasGeometry: override def toOntologySchema(targetSchema: OntologySchema): GeomValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.datatypeValueToJsonLDObject( @@ -2323,23 +2375,20 @@ case class GeomValueContentV2(ontologySchema: OntologySchema, valueHasGeometry: case ApiV2Complex => JsonLDObject(Map(OntologyConstants.KnoraApiV2Complex.GeometryValueAsGeometry -> JsonLDString(valueHasGeometry))) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy( valueHasGeometry = stringFormatter.fromSparqlEncodedString(valueHasGeometry), comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr)) ) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatGeomValue: GeomValueContentV2 => valueHasGeometry == thatGeomValue.valueHasGeometry case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatGeomValue: GeomValueContentV2 => valueHasGeometry == thatGeomValue.valueHasGeometry && @@ -2347,43 +2396,43 @@ case class GeomValueContentV2(ontologySchema: OntologySchema, valueHasGeometry: case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[GeomValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[GeomValueContentV2]] objects based on JSON-LD input. + */ object GeomValueContentV2 extends ValueContentReaderV2[GeomValueContentV2] { /** - * Converts a JSON-LD object to a [[GeomValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[GeomValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[GeomValueContentV2] = { + * Converts a JSON-LD object to a [[GeomValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[GeomValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[GeomValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): GeomValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val geometryValueAsGeometry: String = jsonLDObject.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.GeometryValueAsGeometry, - stringFormatter.validateGeometryString) + stringFormatter.validateGeometryString + ) GeomValueContentV2( ontologySchema = ApiV2Complex, @@ -2394,17 +2443,18 @@ object GeomValueContentV2 extends ValueContentReaderV2[GeomValueContentV2] { } /** - * Represents a Knora time interval value. - * - * @param valueHasIntervalStart the start of the time interval. - * @param valueHasIntervalEnd the end of the time interval. - * @param comment a comment on this [[IntervalValueContentV2]], if any. - */ -case class IntervalValueContentV2(ontologySchema: OntologySchema, - valueHasIntervalStart: BigDecimal, - valueHasIntervalEnd: BigDecimal, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Knora time interval value. + * + * @param valueHasIntervalStart the start of the time interval. + * @param valueHasIntervalEnd the end of the time interval. + * @param comment a comment on this [[IntervalValueContentV2]], if any. + */ +case class IntervalValueContentV2( + ontologySchema: OntologySchema, + valueHasIntervalStart: BigDecimal, + valueHasIntervalEnd: BigDecimal, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.IntervalValue.toSmartIri.toOntologySchema(ontologySchema) @@ -2415,10 +2465,12 @@ case class IntervalValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): IntervalValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.datatypeValueToJsonLDObject( @@ -2439,15 +2491,14 @@ case class IntervalValueContentV2(ontologySchema: OntologySchema, value = valueHasIntervalEnd.toString, datatype = OntologyConstants.Xsd.Decimal.toSmartIri ) - )) + ) + ) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatIntervalValueContent: IntervalValueContentV2 => valueHasIntervalStart == thatIntervalValueContent.valueHasIntervalStart && @@ -2455,9 +2506,8 @@ case class IntervalValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatIntervalValueContent: IntervalValueContentV2 => valueHasIntervalStart == thatIntervalValueContent.valueHasIntervalStart && @@ -2466,37 +2516,35 @@ case class IntervalValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[IntervalValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[IntervalValueContentV2]] objects based on JSON-LD input. + */ object IntervalValueContentV2 extends ValueContentReaderV2[IntervalValueContentV2] { /** - * Converts a JSON-LD object to an [[IntervalValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[IntervalValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[IntervalValueContentV2] = { + * Converts a JSON-LD object to an [[IntervalValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[IntervalValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[IntervalValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): IntervalValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2523,15 +2571,16 @@ object IntervalValueContentV2 extends ValueContentReaderV2[IntervalValueContentV } /** - * Represents a Knora timestamp value. - * - * @param valueHasTimeStamp the timestamp. - * @param comment a comment on this [[TimeValueContentV2]], if any. - */ -case class TimeValueContentV2(ontologySchema: OntologySchema, - valueHasTimeStamp: Instant, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Knora timestamp value. + * + * @param valueHasTimeStamp the timestamp. + * @param comment a comment on this [[TimeValueContentV2]], if any. + */ +case class TimeValueContentV2( + ontologySchema: OntologySchema, + valueHasTimeStamp: Instant, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.TimeValue.toSmartIri.toOntologySchema(ontologySchema) @@ -2541,10 +2590,12 @@ case class TimeValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): TimeValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.datatypeValueToJsonLDObject( @@ -2560,24 +2611,22 @@ case class TimeValueContentV2(ontologySchema: OntologySchema, value = valueHasTimeStamp.toString, datatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri ) - )) + ) + ) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatTimeValueContent: TimeValueContentV2 => valueHasTimeStamp == thatTimeValueContent.valueHasTimeStamp case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatTimeValueContent: TimeValueContentV2 => valueHasTimeStamp == thatTimeValueContent.valueHasTimeStamp && @@ -2585,36 +2634,35 @@ case class TimeValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[TimeValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[TimeValueContentV2]] objects based on JSON-LD input. + */ object TimeValueContentV2 extends ValueContentReaderV2[TimeValueContentV2] { /** - * Converts a JSON-LD object to a [[TimeValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[IntervalValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TimeValueContentV2] = { + * Converts a JSON-LD object to a [[TimeValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[IntervalValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TimeValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): TimeValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2634,17 +2682,18 @@ object TimeValueContentV2 extends ValueContentReaderV2[TimeValueContentV2] { } /** - * Represents a value pointing to a Knora hierarchical list node. - * - * @param valueHasListNode the IRI of the hierarchical list node pointed to. - * @param listNodeLabel the label of the hierarchical list node pointed to. - * @param comment a comment on this [[HierarchicalListValueContentV2]], if any. - */ -case class HierarchicalListValueContentV2(ontologySchema: OntologySchema, - valueHasListNode: IRI, - listNodeLabel: Option[String] = None, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a value pointing to a Knora hierarchical list node. + * + * @param valueHasListNode the IRI of the hierarchical list node pointed to. + * @param listNodeLabel the label of the hierarchical list node pointed to. + * @param comment a comment on this [[HierarchicalListValueContentV2]], if any. + */ +case class HierarchicalListValueContentV2( + ontologySchema: OntologySchema, + valueHasListNode: IRI, + listNodeLabel: Option[String] = None, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.ListValue.toSmartIri.toOntologySchema(ontologySchema) @@ -2655,10 +2704,12 @@ case class HierarchicalListValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): HierarchicalListValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => listNodeLabel match { @@ -2679,23 +2730,20 @@ case class HierarchicalListValueContentV2(ontologySchema: OntologySchema, ) ) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy( listNodeLabel = listNodeLabel.map(labelStr => stringFormatter.fromSparqlEncodedString(labelStr)), comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr)) ) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatListContent: HierarchicalListValueContentV2 => valueHasListNode == thatListContent.valueHasListNode case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatListContent: HierarchicalListValueContentV2 => valueHasListNode == thatListContent.valueHasListNode && @@ -2703,44 +2751,43 @@ case class HierarchicalListValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[HierarchicalListValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[HierarchicalListValueContentV2]] objects based on JSON-LD input. + */ object HierarchicalListValueContentV2 extends ValueContentReaderV2[HierarchicalListValueContentV2] { /** - * Converts a JSON-LD object to a [[HierarchicalListValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[HierarchicalListValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[HierarchicalListValueContentV2] = { + * Converts a JSON-LD object to a [[HierarchicalListValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[HierarchicalListValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[HierarchicalListValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): HierarchicalListValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val listValueAsListNode: SmartIri = jsonLDObject.requireIriInObject( OntologyConstants.KnoraApiV2Complex.ListValueAsListNode, - stringFormatter.toSmartIriWithErr) + stringFormatter.toSmartIriWithErr + ) if (!listValueAsListNode.isKnoraDataIri) { throw BadRequestException(s"List node IRI <$listValueAsListNode> is not a Knora data IRI") @@ -2755,11 +2802,11 @@ object HierarchicalListValueContentV2 extends ValueContentReaderV2[HierarchicalL } /** - * Represents a Knora color value. - * - * @param valueHasColor a hexadecimal string containing the RGB color value - * @param comment a comment on this [[ColorValueContentV2]], if any. - */ + * Represents a Knora color value. + * + * @param valueHasColor a hexadecimal string containing the RGB color value + * @param comment a comment on this [[ColorValueContentV2]], if any. + */ case class ColorValueContentV2(ontologySchema: OntologySchema, valueHasColor: String, comment: Option[String] = None) extends ValueContentV2 { override def valueType: SmartIri = { @@ -2771,10 +2818,12 @@ case class ColorValueContentV2(ontologySchema: OntologySchema, valueHasColor: St override def toOntologySchema(targetSchema: OntologySchema): ColorValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.datatypeValueToJsonLDObject( @@ -2785,23 +2834,20 @@ case class ColorValueContentV2(ontologySchema: OntologySchema, valueHasColor: St case ApiV2Complex => JsonLDObject(Map(OntologyConstants.KnoraApiV2Complex.ColorValueAsColor -> JsonLDString(valueHasColor))) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy( valueHasColor = stringFormatter.fromSparqlEncodedString(valueHasColor), comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr)) ) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatColorContent: ColorValueContentV2 => valueHasColor == thatColorContent.valueHasColor case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatColorContent: ColorValueContentV2 => valueHasColor == thatColorContent.valueHasColor && @@ -2809,44 +2855,43 @@ case class ColorValueContentV2(ontologySchema: OntologySchema, valueHasColor: St case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[ColorValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[ColorValueContentV2]] objects based on JSON-LD input. + */ object ColorValueContentV2 extends ValueContentReaderV2[ColorValueContentV2] { /** - * Converts a JSON-LD object to a [[ColorValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[ColorValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[ColorValueContentV2] = { + * Converts a JSON-LD object to a [[ColorValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[ColorValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[ColorValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): ColorValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val colorValueAsColor: String = jsonLDObject.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.ColorValueAsColor, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) ColorValueContentV2( ontologySchema = ApiV2Complex, @@ -2857,11 +2902,11 @@ object ColorValueContentV2 extends ValueContentReaderV2[ColorValueContentV2] { } /** - * Represents a Knora URI value. - * - * @param valueHasUri the URI value. - * @param comment a comment on this [[UriValueContentV2]], if any. - */ + * Represents a Knora URI value. + * + * @param valueHasUri the URI value. + * @param comment a comment on this [[UriValueContentV2]], if any. + */ case class UriValueContentV2(ontologySchema: OntologySchema, valueHasUri: String, comment: Option[String] = None) extends ValueContentV2 { override def valueType: SmartIri = { @@ -2873,10 +2918,12 @@ case class UriValueContentV2(ontologySchema: OntologySchema, valueHasUri: String override def toOntologySchema(targetSchema: OntologySchema): UriValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val uriAsJsonLDObject = JsonLDUtil.datatypeValueToJsonLDObject( value = valueHasUri, datatype = OntologyConstants.Xsd.Uri.toSmartIri @@ -2890,21 +2937,19 @@ case class UriValueContentV2(ontologySchema: OntologySchema, valueHasUri: String } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy( valueHasUri = stringFormatter.fromSparqlEncodedString(valueHasUri), comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr)) ) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatUriContent: UriValueContentV2 => valueHasUri == thatUriContent.valueHasUri case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatUriContent: UriValueContentV2 => valueHasUri == thatUriContent.valueHasUri && @@ -2912,36 +2957,35 @@ case class UriValueContentV2(ontologySchema: OntologySchema, valueHasUri: String case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[UriValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[UriValueContentV2]] objects based on JSON-LD input. + */ object UriValueContentV2 extends ValueContentReaderV2[UriValueContentV2] { /** - * Converts a JSON-LD object to a [[UriValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[UriValueContentV2]]. - */ + * Converts a JSON-LD object to a [[UriValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[UriValueContentV2]]. + */ override def fromJsonLDObject( - jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[UriValueContentV2] = { + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[UriValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): UriValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -2961,16 +3005,16 @@ object UriValueContentV2 extends ValueContentReaderV2[UriValueContentV2] { } /** - * - * Represents a Knora geoname value. - * - * @param valueHasGeonameCode the geoname code. - * @param comment a comment on this [[GeonameValueContentV2]], if any. - */ -case class GeonameValueContentV2(ontologySchema: OntologySchema, - valueHasGeonameCode: String, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Knora geoname value. + * + * @param valueHasGeonameCode the geoname code. + * @param comment a comment on this [[GeonameValueContentV2]], if any. + */ +case class GeonameValueContentV2( + ontologySchema: OntologySchema, + valueHasGeonameCode: String, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.GeonameValue.toSmartIri.toOntologySchema(ontologySchema) @@ -2981,10 +3025,12 @@ case class GeonameValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): GeonameValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.datatypeValueToJsonLDObject( @@ -2994,25 +3040,23 @@ case class GeonameValueContentV2(ontologySchema: OntologySchema, case ApiV2Complex => JsonLDObject( - Map(OntologyConstants.KnoraApiV2Complex.GeonameValueAsGeonameCode -> JsonLDString(valueHasGeonameCode))) + Map(OntologyConstants.KnoraApiV2Complex.GeonameValueAsGeonameCode -> JsonLDString(valueHasGeonameCode)) + ) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy( valueHasGeonameCode = stringFormatter.fromSparqlEncodedString(valueHasGeonameCode), comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr)) ) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatGeonameContent: GeonameValueContentV2 => valueHasGeonameCode == thatGeonameContent.valueHasGeonameCode case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatGeonameContent: GeonameValueContentV2 => valueHasGeonameCode == thatGeonameContent.valueHasGeonameCode && @@ -3020,44 +3064,43 @@ case class GeonameValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[GeonameValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[GeonameValueContentV2]] objects based on JSON-LD input. + */ object GeonameValueContentV2 extends ValueContentReaderV2[GeonameValueContentV2] { /** - * Converts a JSON-LD object to a [[GeonameValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[GeonameValueContentV2]]. - */ - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[GeonameValueContentV2] = { + * Converts a JSON-LD object to a [[GeonameValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[GeonameValueContentV2]]. + */ + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[GeonameValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): GeonameValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val geonameValueAsGeonameCode: String = jsonLDObject.requireStringWithValidation( OntologyConstants.KnoraApiV2Complex.GeonameValueAsGeonameCode, - stringFormatter.toSparqlEncodedString) + stringFormatter.toSparqlEncodedString + ) GeonameValueContentV2( ontologySchema = ApiV2Complex, @@ -3068,45 +3111,52 @@ object GeonameValueContentV2 extends ValueContentReaderV2[GeonameValueContentV2] } /** - * Represents the basic metadata stored about any file value. - */ -case class FileValueV2(internalFilename: String, - internalMimeType: String, - originalFilename: Option[String], - originalMimeType: Option[String]) + * Represents the basic metadata stored about any file value. + */ +case class FileValueV2( + internalFilename: String, + internalMimeType: String, + originalFilename: Option[String], + originalMimeType: Option[String] +) /** - * Holds a [[FileValueV2]] and the metadata that Sipi returned about the file. - * - * @param fileValue a [[FileValueV2]]. - * @param sipiFileMetadata the metadata that Sipi returned about the file. - */ + * Holds a [[FileValueV2]] and the metadata that Sipi returned about the file. + * + * @param fileValue a [[FileValueV2]]. + * @param sipiFileMetadata the metadata that Sipi returned about the file. + */ case class FileValueWithSipiMetadata(fileValue: FileValueV2, sipiFileMetadata: GetFileMetadataResponse) /** - * Constructs [[FileValueWithSipiMetadata]] objects based on JSON-LD input. - */ + * Constructs [[FileValueWithSipiMetadata]] objects based on JSON-LD input. + */ object FileValueWithSipiMetadata { - def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[FileValueWithSipiMetadata] = { + def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[FileValueWithSipiMetadata] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { // The submitted value provides only Sipi's internal filename for the file. internalFilename <- Future( - jsonLDObject.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.FileValueHasFilename, - stringFormatter.toSparqlEncodedString)) + jsonLDObject.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.FileValueHasFilename, + stringFormatter.toSparqlEncodedString + ) + ) // Ask Sipi about the rest of the file's metadata. tempFileUrl = stringFormatter.makeSipiTempFileUrl(settings, internalFilename) fileMetadataResponse: GetFileMetadataResponse <- (storeManager ? GetFileMetadataRequest( fileUrl = tempFileUrl, - requestingUser = requestingUser)).mapTo[GetFileMetadataResponse] + requestingUser = requestingUser + )).mapTo[GetFileMetadataResponse] fileValue = FileValueV2( internalFilename = internalFilename, @@ -3119,13 +3169,13 @@ object FileValueWithSipiMetadata { } /** - * A trait for case classes representing different types of file values. - */ + * A trait for case classes representing different types of file values. + */ sealed trait FileValueContentV2 extends ValueContentV2 { /** - * The basic metadata about the file value. - */ + * The basic metadata about the file value. + */ def fileValue: FileValueV2 def toJsonLDValueInSimpleSchema(fileUrl: String): JsonLDObject = { @@ -3147,19 +3197,20 @@ sealed trait FileValueContentV2 extends ValueContentV2 { } /** - * Represents image file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param dimX the with of the the image in pixels. - * @param dimY the height of the the image in pixels. - * @param comment a comment on this `StillImageFileValueContentV2`, if any. - */ -case class StillImageFileValueContentV2(ontologySchema: OntologySchema, - fileValue: FileValueV2, - dimX: Int, - dimY: Int, - comment: Option[String] = None) - extends FileValueContentV2 { + * Represents image file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param dimX the with of the the image in pixels. + * @param dimY the height of the the image in pixels. + * @param comment a comment on this `StillImageFileValueContentV2`, if any. + */ +case class StillImageFileValueContentV2( + ontologySchema: OntologySchema, + fileValue: FileValueV2, + dimX: Int, + dimY: Int, + comment: Option[String] = None +) extends FileValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.StillImageFileValue.toSmartIri.toOntologySchema(ontologySchema) @@ -3170,14 +3221,15 @@ case class StillImageFileValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): StillImageFileValueContentV2 = copy(ontologySchema = targetSchema) - def makeFileUrl(projectADM: ProjectADM, settings: KnoraSettingsImpl): String = { + def makeFileUrl(projectADM: ProjectADM, settings: KnoraSettingsImpl): String = s"${settings.externalSipiIIIFGetUrl}/${projectADM.shortcode}/${fileValue.internalFilename}/full/$dimX,$dimY/0/default.jpg" - } - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val fileUrl: String = makeFileUrl(projectADM, settings) targetSchema match { @@ -3193,15 +3245,15 @@ case class StillImageFileValueContentV2(ontologySchema: OntologySchema, value = s"${settings.externalSipiIIIFGetUrl}/${projectADM.shortcode}", datatype = OntologyConstants.Xsd.Uri.toSmartIri ) - )) + ) + ) } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatStillImage: StillImageFileValueContentV2 => fileValue == thatStillImage.fileValue && @@ -3210,31 +3262,29 @@ case class StillImageFileValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatStillImage: StillImageFileValueContentV2 => wouldDuplicateOtherValue(thatStillImage) && comment == thatStillImage.comment case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[StillImageFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[StillImageFileValueContentV2]] objects based on JSON-LD input. + */ object StillImageFileValueContentV2 extends ValueContentReaderV2[StillImageFileValueContentV2] { - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[StillImageFileValueContentV2] = { + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[StillImageFileValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -3249,37 +3299,38 @@ object StillImageFileValueContentV2 extends ValueContentReaderV2[StillImageFileV _ = if (!settings.imageMimeTypes.contains(fileValueWithSipiMetadata.fileValue.internalMimeType)) { throw BadRequestException( - s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for still image files") + s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for still image files" + ) } - } yield - StillImageFileValueContentV2( - ontologySchema = ApiV2Complex, - fileValue = fileValueWithSipiMetadata.fileValue, - dimX = fileValueWithSipiMetadata.sipiFileMetadata.width - .getOrElse(throw SipiException(s"Sipi did not return the image width")), - dimY = fileValueWithSipiMetadata.sipiFileMetadata.height - .getOrElse(throw SipiException(s"Sipi did not return the image height")), - comment = getComment(jsonLDObject) - ) + } yield StillImageFileValueContentV2( + ontologySchema = ApiV2Complex, + fileValue = fileValueWithSipiMetadata.fileValue, + dimX = fileValueWithSipiMetadata.sipiFileMetadata.width + .getOrElse(throw SipiException(s"Sipi did not return the image width")), + dimY = fileValueWithSipiMetadata.sipiFileMetadata.height + .getOrElse(throw SipiException(s"Sipi did not return the image height")), + comment = getComment(jsonLDObject) + ) } } /** - * Represents document file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param pageCount the number of pages in the document. - * @param dimX the with of the the document in pixels. - * @param dimY the height of the the document in pixels. - * @param comment a comment on this `DocumentFileValueContentV2`, if any. - */ -case class DocumentFileValueContentV2(ontologySchema: OntologySchema, - fileValue: FileValueV2, - pageCount: Option[Int], - dimX: Option[Int], - dimY: Option[Int], - comment: Option[String] = None) - extends FileValueContentV2 { + * Represents document file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param pageCount the number of pages in the document. + * @param dimX the with of the the document in pixels. + * @param dimY the height of the the document in pixels. + * @param comment a comment on this `DocumentFileValueContentV2`, if any. + */ +case class DocumentFileValueContentV2( + ontologySchema: OntologySchema, + fileValue: FileValueV2, + pageCount: Option[Int], + dimX: Option[Int], + dimY: Option[Int], + comment: Option[String] = None +) extends FileValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.DocumentFileValue.toSmartIri.toOntologySchema(ontologySchema) @@ -3290,10 +3341,12 @@ case class DocumentFileValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): DocumentFileValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val fileUrl: String = s"${settings.externalSipiBaseUrl}/${projectADM.shortcode}/${fileValue.internalFilename}/file" targetSchema match { @@ -3313,47 +3366,46 @@ case class DocumentFileValueContentV2(ontologySchema: OntologySchema, } JsonLDObject( - toJsonLDObjectMapInComplexSchema(fileUrl) ++ maybeDimXStatement ++ maybeDimYStatement ++ maybePageCountStatement + toJsonLDObjectMapInComplexSchema( + fileUrl + ) ++ maybeDimXStatement ++ maybeDimYStatement ++ maybePageCountStatement ) } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatDocumentFile: DocumentFileValueContentV2 => fileValue == thatDocumentFile.fileValue case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatDocumentFile: DocumentFileValueContentV2 => wouldDuplicateOtherValue(thatDocumentFile) && comment == thatDocumentFile.comment case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[DocumentFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[DocumentFileValueContentV2]] objects based on JSON-LD input. + */ object DocumentFileValueContentV2 extends ValueContentReaderV2[DocumentFileValueContentV2] { - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[DocumentFileValueContentV2] = { + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[DocumentFileValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -3368,30 +3420,31 @@ object DocumentFileValueContentV2 extends ValueContentReaderV2[DocumentFileValue _ = if (!settings.documentMimeTypes.contains(fileValueWithSipiMetadata.fileValue.internalMimeType)) { throw BadRequestException( - s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for document files") + s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for document files" + ) } - } yield - DocumentFileValueContentV2( - ontologySchema = ApiV2Complex, - fileValue = fileValueWithSipiMetadata.fileValue, - pageCount = fileValueWithSipiMetadata.sipiFileMetadata.pageCount, - dimX = fileValueWithSipiMetadata.sipiFileMetadata.width, - dimY = fileValueWithSipiMetadata.sipiFileMetadata.height, - comment = getComment(jsonLDObject) - ) + } yield DocumentFileValueContentV2( + ontologySchema = ApiV2Complex, + fileValue = fileValueWithSipiMetadata.fileValue, + pageCount = fileValueWithSipiMetadata.sipiFileMetadata.pageCount, + dimX = fileValueWithSipiMetadata.sipiFileMetadata.width, + dimY = fileValueWithSipiMetadata.sipiFileMetadata.height, + comment = getComment(jsonLDObject) + ) } } /** - * Represents text file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param comment a comment on this [[TextFileValueContentV2]], if any. - */ -case class TextFileValueContentV2(ontologySchema: OntologySchema, - fileValue: FileValueV2, - comment: Option[String] = None) - extends FileValueContentV2 { + * Represents text file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param comment a comment on this [[TextFileValueContentV2]], if any. + */ +case class TextFileValueContentV2( + ontologySchema: OntologySchema, + fileValue: FileValueV2, + comment: Option[String] = None +) extends FileValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.TextFileValue.toSmartIri.toOntologySchema(ontologySchema) @@ -3402,10 +3455,12 @@ case class TextFileValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): TextFileValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val fileUrl: String = s"${settings.externalSipiBaseUrl}/${projectADM.shortcode}/${fileValue.internalFilename}/file" targetSchema match { @@ -3416,20 +3471,18 @@ case class TextFileValueContentV2(ontologySchema: OntologySchema, } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatTextFile: TextFileValueContentV2 => fileValue == thatTextFile.fileValue case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatTextFile: TextFileValueContentV2 => fileValue == thatTextFile.fileValue && @@ -3437,22 +3490,21 @@ case class TextFileValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[TextFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[TextFileValueContentV2]] objects based on JSON-LD input. + */ object TextFileValueContentV2 extends ValueContentReaderV2[TextFileValueContentV2] { - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TextFileValueContentV2] = { + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TextFileValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -3467,29 +3519,30 @@ object TextFileValueContentV2 extends ValueContentReaderV2[TextFileValueContentV _ = if (!settings.textMimeTypes.contains(fileValueWithSipiMetadata.fileValue.internalMimeType)) { throw BadRequestException( - s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for text files") + s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for text files" + ) } - } yield - TextFileValueContentV2( - ontologySchema = ApiV2Complex, - fileValue = fileValueWithSipiMetadata.fileValue, - comment = getComment(jsonLDObject) - ) + } yield TextFileValueContentV2( + ontologySchema = ApiV2Complex, + fileValue = fileValueWithSipiMetadata.fileValue, + comment = getComment(jsonLDObject) + ) } } /** - * Represents audio file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param duration the duration of the audio file in seconds. - * @param comment a comment on this [[AudioFileValueContentV2]], if any. - */ -case class AudioFileValueContentV2(ontologySchema: OntologySchema, - fileValue: FileValueV2, - duration: Option[BigDecimal] = None, - comment: Option[String] = None) - extends FileValueContentV2 { + * Represents audio file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param duration the duration of the audio file in seconds. + * @param comment a comment on this [[AudioFileValueContentV2]], if any. + */ +case class AudioFileValueContentV2( + ontologySchema: OntologySchema, + fileValue: FileValueV2, + duration: Option[BigDecimal] = None, + comment: Option[String] = None +) extends FileValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.AudioFileValue.toSmartIri.toOntologySchema(ontologySchema) @@ -3500,10 +3553,12 @@ case class AudioFileValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): AudioFileValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val fileUrl: String = s"${settings.externalSipiBaseUrl}/${projectADM.shortcode}/${fileValue.internalFilename}/file" targetSchema match { @@ -3514,20 +3569,18 @@ case class AudioFileValueContentV2(ontologySchema: OntologySchema, } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatAudioFile: AudioFileValueContentV2 => fileValue == thatAudioFile.fileValue case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatAudioFile: AudioFileValueContentV2 => fileValue == thatAudioFile.fileValue && @@ -3535,22 +3588,21 @@ case class AudioFileValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[AudioFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[AudioFileValueContentV2]] objects based on JSON-LD input. + */ object AudioFileValueContentV2 extends ValueContentReaderV2[AudioFileValueContentV2] { - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[AudioFileValueContentV2] = { + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[AudioFileValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -3565,36 +3617,37 @@ object AudioFileValueContentV2 extends ValueContentReaderV2[AudioFileValueConten _ = if (!settings.audioMimeTypes.contains(fileValueWithSipiMetadata.fileValue.internalMimeType)) { throw BadRequestException( - s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for audio files") + s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for audio files" + ) } - } yield - AudioFileValueContentV2( - ontologySchema = ApiV2Complex, - fileValue = fileValueWithSipiMetadata.fileValue, - duration = fileValueWithSipiMetadata.sipiFileMetadata.duration, - comment = getComment(jsonLDObject) - ) + } yield AudioFileValueContentV2( + ontologySchema = ApiV2Complex, + fileValue = fileValueWithSipiMetadata.fileValue, + duration = fileValueWithSipiMetadata.sipiFileMetadata.duration, + comment = getComment(jsonLDObject) + ) } } /** - * Represents video file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param dimX the with of the the image in pixels. - * @param dimY the height of the the image in pixels. - * @param fps the frame rate of the video. - * @param duration the duration of the video file in seconds. - * @param comment a comment on this [[MovingImageFileValueContentV2]], if any. - */ -case class MovingImageFileValueContentV2(ontologySchema: OntologySchema, - fileValue: FileValueV2, - dimX: Int, - dimY: Int, - fps: Option[BigDecimal] = None, - duration: Option[BigDecimal] = None, - comment: Option[String] = None) - extends FileValueContentV2 { + * Represents video file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param dimX the with of the the image in pixels. + * @param dimY the height of the the image in pixels. + * @param fps the frame rate of the video. + * @param duration the duration of the video file in seconds. + * @param comment a comment on this [[MovingImageFileValueContentV2]], if any. + */ +case class MovingImageFileValueContentV2( + ontologySchema: OntologySchema, + fileValue: FileValueV2, + dimX: Int, + dimY: Int, + fps: Option[BigDecimal] = None, + duration: Option[BigDecimal] = None, + comment: Option[String] = None +) extends FileValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.MovingImageFileValue.toSmartIri.toOntologySchema(ontologySchema) @@ -3605,10 +3658,12 @@ case class MovingImageFileValueContentV2(ontologySchema: OntologySchema, override def toOntologySchema(targetSchema: OntologySchema): MovingImageFileValueContentV2 = copy(ontologySchema = targetSchema) - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = { val fileUrl: String = s"${settings.externalSipiBaseUrl}/${projectADM.shortcode}/${fileValue.internalFilename}/file" targetSchema match { @@ -3619,24 +3674,23 @@ case class MovingImageFileValueContentV2(ontologySchema: OntologySchema, toJsonLDObjectMapInComplexSchema(fileUrl) ++ Map( OntologyConstants.KnoraApiV2Complex.MovingImageFileValueHasDimX -> JsonLDInt(dimX), OntologyConstants.KnoraApiV2Complex.MovingImageFileValueHasDimY -> JsonLDInt(dimY) - )) + ) + ) } } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatVideoFile: MovingImageFileValueContentV2 => fileValue == thatVideoFile.fileValue case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatVideoFile: MovingImageFileValueContentV2 => fileValue == thatVideoFile.fileValue && @@ -3644,22 +3698,21 @@ case class MovingImageFileValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[MovingImageFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[MovingImageFileValueContentV2]] objects based on JSON-LD input. + */ object MovingImageFileValueContentV2 extends ValueContentReaderV2[MovingImageFileValueContentV2] { - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[MovingImageFileValueContentV2] = { + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[MovingImageFileValueContentV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance for { @@ -3674,41 +3727,42 @@ object MovingImageFileValueContentV2 extends ValueContentReaderV2[MovingImageFil _ = if (!settings.videoMimeTypes.contains(fileValueWithSipiMetadata.fileValue.internalMimeType)) { throw BadRequestException( - s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for video files") + s"File ${fileValueWithSipiMetadata.fileValue.internalFilename} has MIME type ${fileValueWithSipiMetadata.fileValue.internalMimeType}, which is not supported for video files" + ) } - } yield - MovingImageFileValueContentV2( - ontologySchema = ApiV2Complex, - fileValue = fileValueWithSipiMetadata.fileValue, - duration = fileValueWithSipiMetadata.sipiFileMetadata.duration, - dimX = fileValueWithSipiMetadata.sipiFileMetadata.width - .getOrElse(throw SipiException(s"Sipi did not return the video width")), - dimY = fileValueWithSipiMetadata.sipiFileMetadata.height - .getOrElse(throw SipiException(s"Sipi did not return the video height")), - fps = fileValueWithSipiMetadata.sipiFileMetadata.fps, - comment = getComment(jsonLDObject) - ) + } yield MovingImageFileValueContentV2( + ontologySchema = ApiV2Complex, + fileValue = fileValueWithSipiMetadata.fileValue, + duration = fileValueWithSipiMetadata.sipiFileMetadata.duration, + dimX = fileValueWithSipiMetadata.sipiFileMetadata.width + .getOrElse(throw SipiException(s"Sipi did not return the video width")), + dimY = fileValueWithSipiMetadata.sipiFileMetadata.height + .getOrElse(throw SipiException(s"Sipi did not return the video height")), + fps = fileValueWithSipiMetadata.sipiFileMetadata.fps, + comment = getComment(jsonLDObject) + ) } } /** - * Represents a Knora link value. - * - * @param referredResourceIri the IRI of resource that this link value refers to (either the source - * of an incoming link, or the target of an outgoing link). - * @param referredResourceExists `true` if the referred resource already exists, `false` if it is being created in the - * same transaction. - * @param isIncomingLink indicates if it is an incoming link. - * @param nestedResource information about the nested resource, if given. - * @param comment a comment on the link. - */ -case class LinkValueContentV2(ontologySchema: OntologySchema, - referredResourceIri: IRI, - referredResourceExists: Boolean = true, - isIncomingLink: Boolean = false, - nestedResource: Option[ReadResourceV2] = None, - comment: Option[String] = None) - extends ValueContentV2 { + * Represents a Knora link value. + * + * @param referredResourceIri the IRI of resource that this link value refers to (either the source + * of an incoming link, or the target of an outgoing link). + * @param referredResourceExists `true` if the referred resource already exists, `false` if it is being created in the + * same transaction. + * @param isIncomingLink indicates if it is an incoming link. + * @param nestedResource information about the nested resource, if given. + * @param comment a comment on the link. + */ +case class LinkValueContentV2( + ontologySchema: OntologySchema, + referredResourceIri: IRI, + referredResourceExists: Boolean = true, + isIncomingLink: Boolean = false, + nestedResource: Option[ReadResourceV2] = None, + comment: Option[String] = None +) extends ValueContentV2 { override def valueType: SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance OntologyConstants.KnoraBase.LinkValue.toSmartIri.toOntologySchema(ontologySchema) @@ -3732,10 +3786,12 @@ case class LinkValueContentV2(ontologySchema: OntologySchema, ) } - override def toJsonLDValue(targetSchema: ApiV2Schema, - projectADM: ProjectADM, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDValue = { + override def toJsonLDValue( + targetSchema: ApiV2Schema, + projectADM: ProjectADM, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDValue = targetSchema match { case ApiV2Simple => JsonLDUtil.iriToJsonLDObject(referredResourceIri) @@ -3761,23 +3817,25 @@ case class LinkValueContentV2(ontologySchema: OntologySchema, if (!isIncomingLink) { Map( OntologyConstants.KnoraApiV2Complex.LinkValueHasTargetIri -> JsonLDUtil.iriToJsonLDObject( - referredResourceIri)) + referredResourceIri + ) + ) } else { Map( OntologyConstants.KnoraApiV2Complex.LinkValueHasSourceIri -> JsonLDUtil.iriToJsonLDObject( - referredResourceIri)) + referredResourceIri + ) + ) } } JsonLDObject(objectMap) } - } - override def unescape: ValueContentV2 = { + override def unescape: ValueContentV2 = copy(comment = comment.map(commentStr => stringFormatter.fromSparqlEncodedString(commentStr))) - } - override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = { + override def wouldDuplicateOtherValue(that: ValueContentV2): Boolean = that match { case thatLinkValue: LinkValueContentV2 => referredResourceIri == thatLinkValue.referredResourceIri && @@ -3785,9 +3843,8 @@ case class LinkValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${that.valueType}>") } - } - override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = { + override def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean = currentVersion match { case thatLinkValue: LinkValueContentV2 => referredResourceIri == thatLinkValue.referredResourceIri && @@ -3796,29 +3853,30 @@ case class LinkValueContentV2(ontologySchema: OntologySchema, case _ => throw AssertionException(s"Can't compare a <$valueType> to a <${currentVersion.valueType}>") } - } } /** - * Constructs [[LinkValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[LinkValueContentV2]] objects based on JSON-LD input. + */ object LinkValueContentV2 extends ValueContentReaderV2[LinkValueContentV2] { - override def fromJsonLDObject(jsonLDObject: JsonLDObject, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - log: LoggingAdapter)(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[LinkValueContentV2] = { + override def fromJsonLDObject( + jsonLDObject: JsonLDObject, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[LinkValueContentV2] = Future(fromJsonLDObjectSync(jsonLDObject)) - } private def fromJsonLDObjectSync(jsonLDObject: JsonLDObject): LinkValueContentV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - val targetIri: SmartIri = jsonLDObject.requireIriInObject(OntologyConstants.KnoraApiV2Complex.LinkValueHasTargetIri, - stringFormatter.toSmartIriWithErr) + val targetIri: SmartIri = jsonLDObject.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.LinkValueHasTargetIri, + stringFormatter.toSmartIriWithErr + ) if (!targetIri.isKnoraDataIri) { throw BadRequestException(s"Link target IRI <$targetIri> is not a Knora data IRI") diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/routing/authenticationmessages/AuthenticationMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/routing/authenticationmessages/AuthenticationMessagesV2.scala index 90065e8dc8..caaddce17b 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/routing/authenticationmessages/AuthenticationMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/routing/authenticationmessages/AuthenticationMessagesV2.scala @@ -29,18 +29,20 @@ import spray.json._ // API requests /** - * Represents an API request payload that asks the Knora API server to authenticate the user and create a JWT token. - * Only one of IRI, username, or email as identifier is allowed. - * - * @param iri the user's IRI. - * @param email the user's email. - * @param username the user's username. - * @param password the user's password. - */ -case class LoginApiRequestPayloadV2(iri: Option[IRI] = None, - email: Option[String] = None, - username: Option[String] = None, - password: String) { + * Represents an API request payload that asks the Knora API server to authenticate the user and create a JWT token. + * Only one of IRI, username, or email as identifier is allowed. + * + * @param iri the user's IRI. + * @param email the user's email. + * @param username the user's username. + * @param password the user's password. + */ +case class LoginApiRequestPayloadV2( + iri: Option[IRI] = None, + email: Option[String] = None, + username: Option[String] = None, + password: String +) { val identifyingParameterCount: Int = List( iri, @@ -58,45 +60,45 @@ case class LoginApiRequestPayloadV2(iri: Option[IRI] = None, } /** - * An abstract knora credentials class. - */ + * An abstract knora credentials class. + */ sealed abstract class KnoraCredentialsV2() /** - * Represents id/password credentials that a user can supply within the authorization header or as URL parameters. - * - * @param identifier the supplied id. - * @param password the supplied password. - */ + * Represents id/password credentials that a user can supply within the authorization header or as URL parameters. + * + * @param identifier the supplied id. + * @param password the supplied password. + */ case class KnoraPasswordCredentialsV2(identifier: UserIdentifierADM, password: String) extends KnoraCredentialsV2 /** - * Represents token credentials that a user can supply withing the authorization header or as URL parameters. - * - * @param token the supplied json web token. - */ + * Represents token credentials that a user can supply withing the authorization header or as URL parameters. + * + * @param token the supplied json web token. + */ case class KnoraTokenCredentialsV2(token: String) extends KnoraCredentialsV2 /** - * Represents session credentials that a user can supply within the cookie header. - * - * @param token the supplied session token. - */ + * Represents session credentials that a user can supply within the cookie header. + * + * @param token the supplied session token. + */ case class KnoraSessionCredentialsV2(token: String) extends KnoraCredentialsV2 /** - * Represents a response Knora returns when communicating with the 'v2/authentication' route during the 'login' operation. - * - * @param token is the returned json web token. - */ + * Represents a response Knora returns when communicating with the 'v2/authentication' route during the 'login' operation. + * + * @param token is the returned json web token. + */ case class LoginResponse(token: String) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting /** - * A spray-json protocol for generating Knora API v2 JSON for property values. - */ + * A spray-json protocol for generating Knora API v2 JSON for property values. + */ trait AuthenticationV2JsonProtocol extends DefaultJsonProtocol with NullOptions with SprayJsonSupport { implicit val loginApiRequestPayloadV2Format: RootJsonFormat[LoginApiRequestPayloadV2] = jsonFormat(LoginApiRequestPayloadV2, "iri", "email", "username", "password") diff --git a/webapi/src/main/scala/org/knora/webapi/package.scala b/webapi/src/main/scala/org/knora/webapi/package.scala index eefe02b918..ccbe9f28bf 100644 --- a/webapi/src/main/scala/org/knora/webapi/package.scala +++ b/webapi/src/main/scala/org/knora/webapi/package.scala @@ -22,14 +22,14 @@ package org.knora package object webapi { /** - * The version of `knora-base` and of the other built-in ontologies that this version of Knora requires. - * Must be the same as the object of `knora-base:ontologyVersion` in the `knora-base` ontology being used. - */ + * The version of `knora-base` and of the other built-in ontologies that this version of Knora requires. + * Must be the same as the object of `knora-base:ontologyVersion` in the `knora-base` ontology being used. + */ val KnoraBaseVersion: String = "knora-base v12" /** - * `IRI` is a synonym for `String`, used to improve code readability. - */ + * `IRI` is a synonym for `String`, used to improve code readability. + */ type IRI = String } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/IriLocker.scala b/webapi/src/main/scala/org/knora/webapi/responders/IriLocker.scala index 2fbf9007c7..843e19c19d 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/IriLocker.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/IriLocker.scala @@ -30,83 +30,83 @@ import scala.annotation.tailrec import scala.concurrent.{ExecutionContext, Future} /** - * Implements JVM-wide, reentrant, application-level update locks on data represented by IRIs, such as Knora - * resources. Each API operation that intends to perform an update receives an API request ID from its API route. It - * can then use that ID to perform update operations while holding an update lock, - * by using the `runWithIriLock` method provided by this class. - */ + * Implements JVM-wide, reentrant, application-level update locks on data represented by IRIs, such as Knora + * resources. Each API operation that intends to perform an update receives an API request ID from its API route. It + * can then use that ID to perform update operations while holding an update lock, + * by using the `runWithIriLock` method provided by this class. + */ object IriLocker { /** - * Represents an update lock on an IRI. - * - * @param apiRequestID the ID of the API request that has locked the IRI. - * @param entryCount the number of times the API request has acquired the lock without releasing it. - */ + * Represents an update lock on an IRI. + * + * @param apiRequestID the ID of the API request that has locked the IRI. + * @param entryCount the number of times the API request has acquired the lock without releasing it. + */ private case class IriLock(apiRequestID: UUID, entryCount: Int) /** - * Contains an entry for each locked IRI. The first time an API request acquires a lock - * on an IRI, the entry count is set to 1. If an API request already has the lock and asks for it - * again, the entry count is incremented. Each time the API request asks to release the lock, the - * entry count is decremented. The lock is released only when the entry count reaches 0. - */ + * Contains an entry for each locked IRI. The first time an API request acquires a lock + * on an IRI, the entry count is set to 1. If an API request already has the lock and asks for it + * again, the entry count is incremented. Each time the API request asks to release the lock, the + * entry count is decremented. The lock is released only when the entry count reaches 0. + */ private val lockMap = new ConcurrentHashMap[IRI, IriLock]() /** - * The number of milliseconds to wait between attempts to acquire a lock. - */ + * The number of milliseconds to wait between attempts to acquire a lock. + */ private val LOCK_RETRY_MILLIS = 100 /** - * The number of times to try to acquire a lock before giving up. - */ + * The number of times to try to acquire a lock before giving up. + */ private val MAX_LOCK_RETRIES = 150 /** - * The total number of milliseconds that an API request should wait before giving up on acquiring a lock. - */ + * The total number of milliseconds that an API request should wait before giving up on acquiring a lock. + */ private val MAX_LOCK_RETRY_MILLIS = LOCK_RETRY_MILLIS * MAX_LOCK_RETRIES /** - * Acquires an update lock on an IRI, then runs a task that updates the IRI. The lock implementation - * is reentrant: if the API request requesting the lock already has it, this will not cause a deadlock. The lock is - * released after all tasks that used it for the same API request have either completed or failed. (Failure means - * either throwing an exception or returning a failed `Future`.) If the lock cannot be acquired because another API - * request has it, this method waits and retries. (The wait duration and maximum number of retries are defined in - * this class. The waiting is done in a [[Future]], so this method does not block.) If this method still cannot - * acquire the lock after the maximum number of retries, it throws [[ApplicationLockException]]. - * - * @param apiRequestID the ID of the API request that needs the lock. - * @param iri the IRI to be locked. - * @param task a function returning a [[Future]] that performs the update. This function will be called only after - * the lock has been acquired. If the task throws an exception or returns a failed `Future`, - * this method will return a failed `Future`. - * @return the result of the task. - */ - def runWithIriLock[T](apiRequestID: UUID, iri: IRI, task: () => Future[T])( - implicit executionContext: ExecutionContext): Future[T] = { + * Acquires an update lock on an IRI, then runs a task that updates the IRI. The lock implementation + * is reentrant: if the API request requesting the lock already has it, this will not cause a deadlock. The lock is + * released after all tasks that used it for the same API request have either completed or failed. (Failure means + * either throwing an exception or returning a failed `Future`.) If the lock cannot be acquired because another API + * request has it, this method waits and retries. (The wait duration and maximum number of retries are defined in + * this class. The waiting is done in a [[Future]], so this method does not block.) If this method still cannot + * acquire the lock after the maximum number of retries, it throws [[ApplicationLockException]]. + * + * @param apiRequestID the ID of the API request that needs the lock. + * @param iri the IRI to be locked. + * @param task a function returning a [[Future]] that performs the update. This function will be called only after + * the lock has been acquired. If the task throws an exception or returns a failed `Future`, + * this method will return a failed `Future`. + * @return the result of the task. + */ + def runWithIriLock[T](apiRequestID: UUID, iri: IRI, task: () => Future[T])(implicit + executionContext: ExecutionContext + ): Future[T] = // Try to acquire the lock in a future. If we can't acquire the lock, this future will fail without running // the task. Future(acquireOrIncrementLock(iri, apiRequestID, MAX_LOCK_RETRIES)).flatMap { _ => // Once we've acquired the lock, run the task in a future, because it might throw an exception rather than // returning a future. Flatten nested futures into one, then decrement or release the lock. - Future(task()).flatten.andThen { - case _ => decrementOrReleaseLock(iri, apiRequestID) + Future(task()).flatten.andThen { case _ => + decrementOrReleaseLock(iri, apiRequestID) } } - } /** - * Tries to acquire an update lock for an API request on an IRI. If the API request already - * has the lock, the lock's entry count is incremented. If another API request has the lock, - * waits and retries. If the lock is still unavailable after the maximum number of retries, - * throws [[ApplicationLockException]]. - * - * @param iri the IRI to be locked. - * @param apiRequestID the ID of the API request that needs the lock. - * @param tries the number of times to try to acquire the lock. - */ + * Tries to acquire an update lock for an API request on an IRI. If the API request already + * has the lock, the lock's entry count is incremented. If another API request has the lock, + * waits and retries. If the lock is still unavailable after the maximum number of retries, + * throws [[ApplicationLockException]]. + * + * @param iri the IRI to be locked. + * @param apiRequestID the ID of the API request that needs the lock. + * @param tries the number of times to try to acquire the lock. + */ @tailrec private def acquireOrIncrementLock(iri: IRI, apiRequestID: UUID, tries: Int): Unit = { // Try to acquire the lock, giving it an initial entry count of 1 if it's unused. @@ -139,19 +139,20 @@ object IriLocker { } else { // No, we've run out of retries, so throw an exception. throw ApplicationLockException( - s"Could not acquire update lock on $iri for API request $apiRequestID within $MAX_LOCK_RETRY_MILLIS ms, because API request ${newLock.apiRequestID} is holding it") + s"Could not acquire update lock on $iri for API request $apiRequestID within $MAX_LOCK_RETRY_MILLIS ms, because API request ${newLock.apiRequestID} is holding it" + ) } } } /** - * Checks that the specified API request has a lock on the specified IRI, then either decrements - * the lock's entry count or releases the lock. - * - * @param iri the IRI that is locked. - * @param apiRequestID the ID of the API request that has the lock. - */ - private def decrementOrReleaseLock(iri: IRI, apiRequestID: UUID): Unit = { + * Checks that the specified API request has a lock on the specified IRI, then either decrements + * the lock's entry count or releases the lock. + * + * @param iri the IRI that is locked. + * @param apiRequestID the ID of the API request that has the lock. + */ + private def decrementOrReleaseLock(iri: IRI, apiRequestID: UUID): Unit = lockMap.compute( iri, JavaUtil.biFunction({ (_, maybeCurrentLock) => @@ -171,19 +172,19 @@ object IriLocker { } else { // Another API request has the lock. This shouldn't happen. throw ApplicationLockException( - s"API request $apiRequestID was supposed to have an update lock on $iri, but API request ${currentLock.apiRequestID} has it") + s"API request $apiRequestID was supposed to have an update lock on $iri, but API request ${currentLock.apiRequestID} has it" + ) } case None => // The lock is unused. This shouldn't happen. throw ApplicationLockException( - s"API request $apiRequestID was supposed to have an update lock on $iri, but the lock is unused") + s"API request $apiRequestID was supposed to have an update lock on $iri, but the lock is unused" + ) } }) ) - } - def dumpLockMap(): Unit = { + def dumpLockMap(): Unit = println(lockMap) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala b/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala index 746c285719..7499c137a8 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala @@ -111,7 +111,7 @@ abstract class Responder(responderData: ResponderData) extends LazyLogging { /** * Provides logging */ - protected val log: Logger = logger + protected val log: Logger = logger protected val loggingAdapter: LoggingAdapter = akka.event.Logging(system, this.getClass) /** @@ -130,18 +130,18 @@ abstract class Responder(responderData: ResponderData) extends LazyLogging { ): Future[Boolean] = for { isEntityUsedSparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .isEntityUsed( - triplestore = settings.triplestoreType, - entityIri = entityIri, - ignoreKnoraConstraints = ignoreKnoraConstraints, - ignoreRdfSubjectAndObject = ignoreRdfSubjectAndObject - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .isEntityUsed( + triplestore = settings.triplestoreType, + entityIri = entityIri, + ignoreKnoraConstraints = ignoreKnoraConstraints, + ignoreRdfSubjectAndObject = ignoreRdfSubjectAndObject + ) + .toString() + ) isEntityUsedResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(isEntityUsedSparql)) - .mapTo[SparqlSelectResult] + .mapTo[SparqlSelectResult] } yield isEntityUsedResponse.results.bindings.nonEmpty @@ -163,8 +163,8 @@ abstract class Responder(responderData: ResponderData) extends LazyLogging { entityIsUsed: Boolean <- isEntityUsed(entityIri, ignoreKnoraConstraints, ignoreRdfSubjectAndObject) _ = if (entityIsUsed) { - errorFun - } + errorFun + } } yield () /** @@ -183,14 +183,14 @@ abstract class Responder(responderData: ResponderData) extends LazyLogging { result <- stringFormatter.checkIriExists(entityIriAsString, storeManager) _ = if (result) { - throw DuplicateValueException(s"IRI: '$entityIriAsString' already exists, try another one.") - } + throw DuplicateValueException(s"IRI: '$entityIriAsString' already exists, try another one.") + } // Check that given entityIRI ends with a UUID ending: String = entityIriAsString.split('/').last _ = stringFormatter.validateBase64EncodedUuid( - ending, - throw BadRequestException(s"IRI: '$entityIriAsString' must end with a valid base 64 UUID.") - ) + ending, + throw BadRequestException(s"IRI: '$entityIriAsString' must end with a valid base 64 UUID.") + ) } yield entityIriAsString diff --git a/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala b/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala index 25657af132..aaf1a205e6 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala @@ -55,22 +55,22 @@ import org.knora.webapi.util.ActorUtil._ import scala.concurrent.ExecutionContext /** - * This actor receives messages representing client requests, and forwards them to pools of specialised actors - * that it supervises. - * - * @param appActor the main application actor. - */ + * This actor receives messages representing client requests, and forwards them to pools of specialised actors + * that it supervises. + * + * @param appActor the main application actor. + */ class ResponderManager(appActor: ActorRef, responderData: ResponderData) extends Actor with ActorLogging { this: ActorMaker => /** - * The responder's Akka actor system. - */ + * The responder's Akka actor system. + */ protected implicit val system: ActorSystem = context.system /** - * The Akka actor system's execution context for futures. - */ + * The Akka actor system's execution context for futures. + */ protected implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) @@ -79,93 +79,93 @@ class ResponderManager(appActor: ActorRef, responderData: ResponderData) extends // responder, a subclass can call one of the protected methods below. /** - * Constructs the default [[CkanResponderV1]]. - */ + * Constructs the default [[CkanResponderV1]]. + */ protected final def makeDefaultCkanResponderV1: CkanResponderV1 = new CkanResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default Ckan responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default Ckan responder. + */ protected lazy val ckanResponderV1: CkanResponderV1 = makeDefaultCkanResponderV1 /** - * Constructs the default [[ResourcesResponderV1]]. - */ + * Constructs the default [[ResourcesResponderV1]]. + */ protected final def makeDefaultResourcesResponderV1: ResourcesResponderV1 = new ResourcesResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default resources V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default resources V1 responder. + */ protected lazy val resourcesResponderV1: ResourcesResponderV1 = makeDefaultResourcesResponderV1 /** - * Constructs the default [[ValuesResponderV1]]. - */ + * Constructs the default [[ValuesResponderV1]]. + */ protected final def makeDefaultValuesResponderV1: ValuesResponderV1 = new ValuesResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default values V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default values V1 responder. + */ protected lazy val valuesResponderV1: ValuesResponderV1 = makeDefaultValuesResponderV1 /** - * Constructs the default [[StandoffResponderV1]]. - */ + * Constructs the default [[StandoffResponderV1]]. + */ protected final def makeDefaultStandoffResponderV1: StandoffResponderV1 = new StandoffResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default standoff V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default standoff V1 responder. + */ protected lazy val standoffResponderV1: StandoffResponderV1 = makeDefaultStandoffResponderV1 /** - * Constructs the default [[UsersResponderV1]]. - */ + * Constructs the default [[UsersResponderV1]]. + */ protected final def makeDefaultUsersResponderV1: UsersResponderV1 = new UsersResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default users V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default users V1 responder. + */ protected lazy val usersResponderV1: UsersResponderV1 = makeDefaultUsersResponderV1 /** - * Constructs the default Akka routing actor that routes messages to [[ListsResponderV1]]. - */ + * Constructs the default Akka routing actor that routes messages to [[ListsResponderV1]]. + */ protected final def makeDefaultListsResponderV1: ListsResponderV1 = new ListsResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default lists V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default lists V1 responder. + */ protected lazy val listsResponderV1: ListsResponderV1 = makeDefaultListsResponderV1 /** - * Constructs the default Akka routing actor that routes messages to [[SearchResponderV1]]. - */ + * Constructs the default Akka routing actor that routes messages to [[SearchResponderV1]]. + */ protected final def makeDefaultSearchResponderV1: SearchResponderV1 = new SearchResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default search V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default search V1 responder. + */ protected lazy val searchResponderV1: SearchResponderV1 = makeDefaultSearchResponderV1 /** - * Constructs the default Akka routing actor that routes messages to [[OntologyResponderV1]]. - */ + * Constructs the default Akka routing actor that routes messages to [[OntologyResponderV1]]. + */ protected final def makeDefaultOntologyResponderV1: OntologyResponderV1 = new OntologyResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default ontology V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default ontology V1 responder. + */ protected lazy val ontologyResponderV1: OntologyResponderV1 = makeDefaultOntologyResponderV1 /** - * Constructs the default Akka routing actor that routes messages to [[ProjectsResponderV1]]. - */ + * Constructs the default Akka routing actor that routes messages to [[ProjectsResponderV1]]. + */ protected final def makeDefaultProjectsResponderV1: ProjectsResponderV1 = new ProjectsResponderV1(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default projects V1 responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default projects V1 responder. + */ protected lazy val projectsResponderV1: ProjectsResponderV1 = makeDefaultProjectsResponderV1 // @@ -173,73 +173,73 @@ class ResponderManager(appActor: ActorRef, responderData: ResponderData) extends // /** - * Constructs the default [[OntologyResponderV2]]. - */ + * Constructs the default [[OntologyResponderV2]]. + */ protected final def makeDefaultOntologiesResponderV2: OntologyResponderV2 = new OntologyResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default ontologies responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default ontologies responder. + */ protected val ontologiesResponderV2: OntologyResponderV2 = makeDefaultOntologiesResponderV2 /** - * Constructs the default [[SearchResponderV2]]. - */ + * Constructs the default [[SearchResponderV2]]. + */ protected final def makeDefaultSearchResponderV2: SearchResponderV2 = new SearchResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default search responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default search responder. + */ protected val searchResponderV2: SearchResponderV2 = makeDefaultSearchResponderV2 /** - * Constructs the default [[ResourcesResponderV2]]. - */ + * Constructs the default [[ResourcesResponderV2]]. + */ protected final def makeDefaultResourcesResponderV2: ResourcesResponderV2 = new ResourcesResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default resources responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default resources responder. + */ protected val resourcesResponderV2: ResourcesResponderV2 = makeDefaultResourcesResponderV2 /** - * Constructs the default [[ValuesResponderV2]]. - */ + * Constructs the default [[ValuesResponderV2]]. + */ protected final def makeDefaultValuesResponderV2: ValuesResponderV2 = new ValuesResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default values responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default values responder. + */ protected val valuesResponderV2: ValuesResponderV2 = makeDefaultValuesResponderV2 /** - * Constructs the default [[StandoffResponderV2]]. - */ + * Constructs the default [[StandoffResponderV2]]. + */ protected final def makeDefaultStandoffResponderV2: StandoffResponderV2 = new StandoffResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default standoff responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default standoff responder. + */ protected val standoffResponderV2: StandoffResponderV2 = makeDefaultStandoffResponderV2 /** - * Constructs the default [[ListsResponderV2]]. - */ + * Constructs the default [[ListsResponderV2]]. + */ protected final def makeDefaultListsResponderV2: ListsResponderV2 = new ListsResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default lists responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default lists responder. + */ protected val listsResponderV2: ListsResponderV2 = makeDefaultListsResponderV2 /** - * Constructs the default [[MetadataResponderV2]]. - */ + * Constructs the default [[MetadataResponderV2]]. + */ protected final def makeDefaultMetadataResponderV2: MetadataResponderV2 = new MetadataResponderV2(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default metadata responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default metadata responder. + */ protected val metadataResponderV2: MetadataResponderV2 = makeDefaultMetadataResponderV2 // @@ -247,81 +247,81 @@ class ResponderManager(appActor: ActorRef, responderData: ResponderData) extends // /** - * Constructs the default [[GroupsResponderADM]]. - */ + * Constructs the default [[GroupsResponderADM]]. + */ protected final def makeDefaultGroupsResponderADM: GroupsResponderADM = new GroupsResponderADM(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default groups responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default groups responder. + */ protected val groupsResponderADM: GroupsResponderADM = makeDefaultGroupsResponderADM /** - * Constructs the default [[ListsResponderADM]]. - */ + * Constructs the default [[ListsResponderADM]]. + */ protected final def makeDefaultListsResponderADM: ListsResponderADM = new ListsResponderADM(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default admin lists responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default admin lists responder. + */ protected val listsResponderADM: ListsResponderADM = makeDefaultListsResponderADM /** - * Constructs the default [[PermissionsResponderADM]]. - */ + * Constructs the default [[PermissionsResponderADM]]. + */ protected final def makeDefaultPermissionsResponderADM: PermissionsResponderADM = new PermissionsResponderADM(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default permissions responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default permissions responder. + */ protected val permissionsResponderADM: PermissionsResponderADM = makeDefaultPermissionsResponderADM /** - * Constructs the default [[ProjectsResponderADM]]. - */ + * Constructs the default [[ProjectsResponderADM]]. + */ protected final def makeDefaultProjectsResponderADM: ProjectsResponderADM = new ProjectsResponderADM(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default projects responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default projects responder. + */ protected val projectsResponderADM: ProjectsResponderADM = makeDefaultProjectsResponderADM /** - * Constructs the default [[StoresResponderADM]]. - */ + * Constructs the default [[StoresResponderADM]]. + */ protected final def makeDefaultStoreResponderADM: StoresResponderADM = new StoresResponderADM(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default store responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default store responder. + */ protected val storeResponderADM: StoresResponderADM = makeDefaultStoreResponderADM /** - * Constructs the default [[UsersResponderADM]]. - */ + * Constructs the default [[UsersResponderADM]]. + */ protected final def makeDefaultUsersResponderADM: UsersResponderADM = new UsersResponderADM(responderData) /** - * Subclasses can override this member to substitute a custom implementation instead of the default users responder. - */ + * Subclasses can override this member to substitute a custom implementation instead of the default users responder. + */ protected val usersResponderADM: UsersResponderADM = makeDefaultUsersResponderADM /** - * Constructs the default [[SipiResponderADM]]. - */ + * Constructs the default [[SipiResponderADM]]. + */ protected final def makeDefaultSipiResponderADM: SipiResponderADM = new SipiResponderADM(responderData) /** - * Subclasses can override this member to substitute it with a custom implementation instead of the default sipi responder. - */ + * Subclasses can override this member to substitute it with a custom implementation instead of the default sipi responder. + */ protected lazy val sipiRouterADM: SipiResponderADM = makeDefaultSipiResponderADM /** - * Each responder's receive method is called and only messages of the allowed type are supplied as the parameter. - * If a serious error occurs (i.e. an error that isn't the client's fault), the future2Message method first - * returns `Failure` to the sender, then throws an exception. - */ + * Each responder's receive method is called and only messages of the allowed type are supplied as the parameter. + * If a serious error occurs (i.e. an error that isn't the client's fault), the future2Message method first + * returns `Failure` to the sender, then throws an exception. + */ def receive: Receive = LoggingReceive { // Knora API V1 messages case ckanResponderRequestV1: CkanResponderRequestV1 => diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala index ae89a0545d..56aed36ef6 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala @@ -41,16 +41,16 @@ import org.knora.webapi.responders.{IriLocker, Responder} import scala.concurrent.Future /** - * Returns information about Knora projects. - */ + * Returns information about Knora projects. + */ class GroupsResponderADM(responderData: ResponderData) extends Responder(responderData) with GroupsADMJsonProtocol { // Global lock IRI used for group creation and updating private val GROUPS_GLOBAL_LOCK_IRI: IRI = "http://rdfh.ch/groups" /** - * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message - */ + * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message + */ def receive(msg: GroupsResponderRequestADM) = msg match { case GroupsGetADM(featureFactoryConfig, requestingUser) => groupsGetADM(featureFactoryConfig, requestingUser) case GroupsGetRequestADM(featureFactoryConfig, requestingUser) => @@ -66,29 +66,35 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond case GroupCreateRequestADM(newGroupInfo, featureFactoryConfig, requestingUser, apiRequestID) => createGroupADM(newGroupInfo, featureFactoryConfig, requestingUser, apiRequestID) case GroupChangeRequestADM(groupIri, changeGroupRequest, featureFactoryConfig, requestingUser, apiRequestID) => - changeGroupBasicInformationRequestADM(groupIri, - changeGroupRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) - case GroupChangeStatusRequestADM(groupIri, - changeGroupRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) => + changeGroupBasicInformationRequestADM( + groupIri, + changeGroupRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) + case GroupChangeStatusRequestADM( + groupIri, + changeGroupRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => changeGroupStatusRequestADM(groupIri, changeGroupRequest, featureFactoryConfig, requestingUser, apiRequestID) case other => handleUnexpectedMessage(other, log, this.getClass.getName) } /** - * Gets all the groups (without built-in groups) and returns them as a sequence of [[GroupADM]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return all the groups as a sequence of [[GroupADM]]. - */ - private def groupsGetADM(featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Seq[GroupADM]] = { + * Gets all the groups (without built-in groups) and returns them as a sequence of [[GroupADM]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return all the groups as a sequence of [[GroupADM]]. + */ + private def groupsGetADM( + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Seq[GroupADM]] = { log.debug("groupsGetADM") @@ -99,7 +105,8 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond triplestore = settings.triplestoreType, maybeIri = None ) - .toString()) + .toString() + ) groupsResponse <- (storeManager ? SparqlExtendedConstructRequest( sparql = sparqlQuery, @@ -111,8 +118,10 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond groups: Seq[Future[GroupADM]] = statements.map { case (groupIri: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => val projectIri: IRI = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.BelongsToProject.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no project attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.BelongsToProject.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no project attached") + ) .head .asInstanceOf[IriLiteralV2] .value @@ -128,33 +137,42 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond case Some(project) => project case None => throw InconsistentRepositoryDataException( - s"Project $projectIri was referenced by $groupIri but was not found in the triplestore.") + s"Project $projectIri was referenced by $groupIri but was not found in the triplestore." + ) } group = GroupADM( id = groupIri.toString, name = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.GroupName.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no name attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.GroupName.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no name attached") + ) .head .asInstanceOf[StringLiteralV2] .value, description = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.GroupDescription.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no description attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.GroupDescription.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no description attached") + ) .head .asInstanceOf[StringLiteralV2] .value, project = projectADM, status = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.Status.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no status attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.Status.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no status attached") + ) .head .asInstanceOf[BooleanLiteralV2] .value, selfjoin = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.HasSelfJoinEnabled.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no status attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.HasSelfJoinEnabled.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no status attached") + ) .head .asInstanceOf[BooleanLiteralV2] .value @@ -167,13 +185,15 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Gets all the groups and returns them as a [[GroupsGetResponseADM]]. - * - * @param requestingUser the user initiating the request. - * @return all the groups as a [[GroupsGetResponseADM]]. - */ - private def groupsGetRequestADM(featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[GroupsGetResponseADM] = { + * Gets all the groups and returns them as a [[GroupsGetResponseADM]]. + * + * @param requestingUser the user initiating the request. + * @return all the groups as a [[GroupsGetResponseADM]]. + */ + private def groupsGetRequestADM( + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[GroupsGetResponseADM] = for { maybeGroupsListToReturn <- groupsGetADM( featureFactoryConfig = featureFactoryConfig, @@ -185,19 +205,19 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond case _ => throw NotFoundException(s"No groups found") } } yield result - } /** - * Gets the group with the given group IRI and returns the information as a [[GroupADM]]. - * - * @param groupIri the IRI of the group requested. - * @param requestingUser the user initiating the request. - * @return information about the group as a [[GroupADM]] - */ - private def groupGetADM(groupIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Option[GroupADM]] = { - + * Gets the group with the given group IRI and returns the information as a [[GroupADM]]. + * + * @param groupIri the IRI of the group requested. + * @param requestingUser the user initiating the request. + * @return information about the group as a [[GroupADM]] + */ + private def groupGetADM( + groupIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Option[GroupADM]] = for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -205,40 +225,42 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond triplestore = settings.triplestoreType, maybeIri = Some(groupIri) ) - .toString()) + .toString() + ) groupResponse <- (storeManager ? SparqlExtendedConstructRequest( sparql = sparqlQuery, featureFactoryConfig = featureFactoryConfig )).mapTo[SparqlExtendedConstructResponse] - maybeGroup: Option[GroupADM] <- if (groupResponse.statements.isEmpty) { - FastFuture.successful(None) - } else { - statements2GroupADM( - statements = groupResponse.statements.head, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - } + maybeGroup: Option[GroupADM] <- + if (groupResponse.statements.isEmpty) { + FastFuture.successful(None) + } else { + statements2GroupADM( + statements = groupResponse.statements.head, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } _ = log.debug("groupGetADM - result: {}", maybeGroup) } yield maybeGroup - } /** - * Gets the group with the given group IRI and returns the information as a [[GroupGetResponseADM]]. - * - * @param groupIri the IRI of the group requested. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @return information about the group as a [[GroupGetResponseADM]]. - */ - private def groupGetRequestADM(groupIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[GroupGetResponseADM] = { - + * Gets the group with the given group IRI and returns the information as a [[GroupGetResponseADM]]. + * + * @param groupIri the IRI of the group requested. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @return information about the group as a [[GroupGetResponseADM]]. + */ + private def groupGetRequestADM( + groupIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[GroupGetResponseADM] = for { maybeGroupADM: Option[GroupADM] <- groupGetADM( groupIri = groupIri, @@ -251,18 +273,19 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond case None => throw NotFoundException(s"Group <$groupIri> not found") } } yield result - } /** - * Gets the groups with the given IRIs and returns a set of [[GroupGetResponseADM]] objects. - * - * @param groupIris the IRIs of the groups being requested. - * @param requestingUser the user initiating the request. - * @return information about the group as a set of [[GroupGetResponseADM]] objects. - */ - private def multipleGroupsGetRequestADM(groupIris: Set[IRI], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Set[GroupGetResponseADM]] = { + * Gets the groups with the given IRIs and returns a set of [[GroupGetResponseADM]] objects. + * + * @param groupIris the IRIs of the groups being requested. + * @param requestingUser the user initiating the request. + * @return information about the group as a set of [[GroupGetResponseADM]] objects. + */ + private def multipleGroupsGetRequestADM( + groupIris: Set[IRI], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Set[GroupGetResponseADM]] = { val groupResponseFutures: Set[Future[GroupGetResponseADM]] = groupIris.map { groupIri => groupGetRequestADM( groupIri = groupIri, @@ -275,16 +298,18 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Gets the members with the given group IRI and returns the information as a sequence of [[UserADM]]. - * - * @param groupIri the IRI of the group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @return A sequence of [[UserADM]] - */ - private def groupMembersGetADM(groupIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Seq[UserADM]] = { + * Gets the members with the given group IRI and returns the information as a sequence of [[UserADM]]. + * + * @param groupIri the IRI of the group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @return A sequence of [[UserADM]] + */ + private def groupMembersGetADM( + groupIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Seq[UserADM]] = { log.debug("groupMembersGetADM - groupIri: {}", groupIri) @@ -298,7 +323,11 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond _ = maybeGroupADM match { case Some(group) => // check if the requesting user is allowed to access the information - if (!requestingUser.permissions.isProjectAdmin(group.project.id) && !requestingUser.permissions.isSystemAdmin && !requestingUser.isSystemUser) { + if ( + !requestingUser.permissions.isProjectAdmin( + group.project.id + ) && !requestingUser.permissions.isSystemAdmin && !requestingUser.isSystemUser + ) { // not a project admin and not a system admin throw ForbiddenException("Project members can only be retrieved by a project or system admin.") } @@ -312,18 +341,20 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond triplestore = settings.triplestoreType, groupIri = groupIri ) - .toString()) + .toString() + ) //_ = log.debug(s"groupMembersByIRIGetRequestV1 - query: $sparqlQueryString") groupMembersResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] //_ = log.debug(s"groupMembersByIRIGetRequestV1 - result: {}", MessageUtil.toSource(groupMembersResponse)) // get project member IRI from results rows - groupMemberIris: Seq[IRI] = if (groupMembersResponse.results.bindings.nonEmpty) { - groupMembersResponse.results.bindings.map(_.rowMap("s")) - } else { - Seq.empty[IRI] - } + groupMemberIris: Seq[IRI] = + if (groupMembersResponse.results.bindings.nonEmpty) { + groupMembersResponse.results.bindings.map(_.rowMap("s")) + } else { + Seq.empty[IRI] + } _ = log.debug("groupMembersGetRequestADM - groupMemberIris: {}", groupMemberIris) @@ -344,17 +375,19 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Gets the group members with the given group IRI and returns the information as a [[GroupMembersGetResponseADM]]. - * Only project and system admins are allowed to access this information. - * - * @param groupIri the IRI of the group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user initiating the request. - * @return A [[GroupMembersGetResponseADM]] - */ - private def groupMembersGetRequestADM(groupIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[GroupMembersGetResponseADM] = { + * Gets the group members with the given group IRI and returns the information as a [[GroupMembersGetResponseADM]]. + * Only project and system admins are allowed to access this information. + * + * @param groupIri the IRI of the group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user initiating the request. + * @return A [[GroupMembersGetResponseADM]] + */ + private def groupMembersGetRequestADM( + groupIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[GroupMembersGetResponseADM] = { log.debug("groupMembersGetRequestADM - groupIri: {}", groupIri) @@ -373,31 +406,37 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Create a new group. - * - * @param createRequest the create request information. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the unique request ID. - * @return a [[GroupOperationResponseADM]] - */ - private def createGroupADM(createRequest: CreateGroupApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[GroupOperationResponseADM] = { + * Create a new group. + * + * @param createRequest the create request information. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the unique request ID. + * @return a [[GroupOperationResponseADM]] + */ + private def createGroupADM( + createRequest: CreateGroupApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[GroupOperationResponseADM] = { log.debug("createGroupADM - createRequest: {}", createRequest) - def createGroupTask(createRequest: CreateGroupApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID): Future[GroupOperationResponseADM] = + def createGroupTask( + createRequest: CreateGroupApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[GroupOperationResponseADM] = for { /* check if username or password are not empty */ _ <- Future(if (createRequest.name.isEmpty) throw BadRequestException("Group name cannot be empty")) _ = if (createRequest.project.isEmpty) throw BadRequestException("Project IRI cannot be empty") /* check if the requesting user is allowed to create group */ - _ = if (!requestingUser.permissions.isProjectAdmin(createRequest.project) && !requestingUser.permissions.isSystemAdmin) { + _ = if ( + !requestingUser.permissions.isProjectAdmin(createRequest.project) && !requestingUser.permissions.isSystemAdmin + ) { // not a project admin and not a system admin throw ForbiddenException("A new group can only be created by a project or system admin.") } @@ -417,13 +456,16 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond case Some(p) => p case None => throw NotFoundException( - s"Cannot create group inside project <${createRequest.project}>. The project was not found.") + s"Cannot create group inside project <${createRequest.project}>. The project was not found." + ) } // check the custom IRI; if not given, create an unused IRI customGroupIri: Option[SmartIri] = createRequest.id.map(iri => iri.toSmartIri) - groupIri: IRI <- checkOrCreateEntityIri(customGroupIri, - stringFormatter.makeRandomGroupIri(projectADM.shortcode)) + groupIri: IRI <- checkOrCreateEntityIri( + customGroupIri, + stringFormatter.makeRandomGroupIri(projectADM.shortcode) + ) /* create the group */ createNewGroupSparqlString = org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -452,7 +494,8 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond ) createdGroup: GroupADM = maybeCreatedGroup.getOrElse( - throw UpdateNotPerformedException(s"Group was not created. Please report this as a possible bug.")) + throw UpdateNotPerformedException(s"Group was not created. Please report this as a possible bug.") + ) } yield GroupOperationResponseADM(group = createdGroup) @@ -467,27 +510,31 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Change group's basic information. - * - * @param groupIri the IRI of the group we want to change. - * @param changeGroupRequest the change request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the unique request ID. - * @return a [[GroupOperationResponseADM]]. - */ - private def changeGroupBasicInformationRequestADM(groupIri: IRI, - changeGroupRequest: ChangeGroupApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[GroupOperationResponseADM] = { + * Change group's basic information. + * + * @param groupIri the IRI of the group we want to change. + * @param changeGroupRequest the change request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the unique request ID. + * @return a [[GroupOperationResponseADM]]. + */ + private def changeGroupBasicInformationRequestADM( + groupIri: IRI, + changeGroupRequest: ChangeGroupApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[GroupOperationResponseADM] = { /** - * The actual change group task run with an IRI lock. - */ - def changeGroupTask(groupIri: IRI, - changeGroupRequest: ChangeGroupApiRequestADM, - requestingUser: UserADM): Future[GroupOperationResponseADM] = + * The actual change group task run with an IRI lock. + */ + def changeGroupTask( + groupIri: IRI, + changeGroupRequest: ChangeGroupApiRequestADM, + requestingUser: UserADM + ): Future[GroupOperationResponseADM] = for { _ <- Future( @@ -503,10 +550,13 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond ) groupADM: GroupADM = maybeGroupADM.getOrElse( - throw NotFoundException(s"Group <$groupIri> not found. Aborting update request.")) + throw NotFoundException(s"Group <$groupIri> not found. Aborting update request.") + ) /* check if the requesting user is allowed to perform updates */ - _ = if (!requestingUser.permissions.isProjectAdmin(groupADM.project.id) && !requestingUser.permissions.isSystemAdmin) { + _ = if ( + !requestingUser.permissions.isProjectAdmin(groupADM.project.id) && !requestingUser.permissions.isSystemAdmin + ) { // not a project admin and not a system admin throw ForbiddenException("Group's information can only be changed by a project or system admin.") } @@ -540,27 +590,31 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Change group's basic information. - * - * @param groupIri the IRI of the group we want to change. - * @param changeGroupRequest the change request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the unique request ID. - * @return a [[GroupOperationResponseADM]]. - */ - private def changeGroupStatusRequestADM(groupIri: IRI, - changeGroupRequest: ChangeGroupApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[GroupOperationResponseADM] = { + * Change group's basic information. + * + * @param groupIri the IRI of the group we want to change. + * @param changeGroupRequest the change request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the unique request ID. + * @return a [[GroupOperationResponseADM]]. + */ + private def changeGroupStatusRequestADM( + groupIri: IRI, + changeGroupRequest: ChangeGroupApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[GroupOperationResponseADM] = { /** - * The actual change group task run with an IRI lock. - */ - def changeGroupStatusTask(groupIri: IRI, - changeGroupRequest: ChangeGroupApiRequestADM, - requestingUser: UserADM): Future[GroupOperationResponseADM] = + * The actual change group task run with an IRI lock. + */ + def changeGroupStatusTask( + groupIri: IRI, + changeGroupRequest: ChangeGroupApiRequestADM, + requestingUser: UserADM + ): Future[GroupOperationResponseADM] = for { _ <- Future( @@ -576,10 +630,13 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond ) groupADM: GroupADM = maybeGroupADM.getOrElse( - throw NotFoundException(s"Group <$groupIri> not found. Aborting update request.")) + throw NotFoundException(s"Group <$groupIri> not found. Aborting update request.") + ) /* check if the requesting user is allowed to perform updates */ - _ = if (!requestingUser.permissions.isProjectAdmin(groupADM.project.id) && !requestingUser.permissions.isSystemAdmin) { + _ = if ( + !requestingUser.permissions.isProjectAdmin(groupADM.project.id) && !requestingUser.permissions.isSystemAdmin + ) { // not a project admin and not a system admin throw ForbiddenException("Group's status can only be changed by a project or system admin.") } @@ -618,25 +675,29 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Main group update method. - * - * @param groupIri the IRI of the group we are updating. - * @param groupUpdatePayload the payload holding the information which we want to update. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the profile of the user making the request. - * @return a [[GroupOperationResponseADM]] - */ - private def updateGroupADM(groupIri: IRI, - groupUpdatePayload: GroupUpdatePayloadADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[GroupOperationResponseADM] = { + * Main group update method. + * + * @param groupIri the IRI of the group we are updating. + * @param groupUpdatePayload the payload holding the information which we want to update. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the profile of the user making the request. + * @return a [[GroupOperationResponseADM]] + */ + private def updateGroupADM( + groupIri: IRI, + groupUpdatePayload: GroupUpdatePayloadADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[GroupOperationResponseADM] = { log.debug("updateGroupADM - groupIri: {}, groupUpdatePayload: {}", groupIri, groupUpdatePayload) - val parametersCount: Int = List(groupUpdatePayload.name, - groupUpdatePayload.description, - groupUpdatePayload.status, - groupUpdatePayload.selfjoin).flatten.size + val parametersCount: Int = List( + groupUpdatePayload.name, + groupUpdatePayload.description, + groupUpdatePayload.status, + groupUpdatePayload.selfjoin + ).flatten.size if (parametersCount == 0) throw BadRequestException("No data would be changed. Aborting update request.") @@ -649,15 +710,17 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond ) groupADM: GroupADM = maybeGroupADM.getOrElse( - throw NotFoundException(s"Group <$groupIri> not found. Aborting update request.")) + throw NotFoundException(s"Group <$groupIri> not found. Aborting update request.") + ) /* Verify that the potentially new name is unique */ - groupByNameAlreadyExists <- if (groupUpdatePayload.name.nonEmpty) { - val newName = groupUpdatePayload.name.get - groupByNameAndProjectExists(newName, groupADM.project.id) - } else { - FastFuture.successful(false) - } + groupByNameAlreadyExists <- + if (groupUpdatePayload.name.nonEmpty) { + val newName = groupUpdatePayload.name.get + groupByNameAndProjectExists(newName, groupADM.project.id) + } else { + FastFuture.successful(false) + } _ = if (groupByNameAlreadyExists) { log.debug("updateGroupADM - about to throw an exception. Group with that name already exists.") @@ -677,7 +740,8 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond maybeStatus = groupUpdatePayload.status, maybeSelfjoin = groupUpdatePayload.selfjoin ) - .toString) + .toString + ) //_ = log.debug(s"updateProjectV1 - query: {}",updateProjectSparqlString) updateGroupResponse <- (storeManager ? SparqlUpdateRequest(updateProjectSparqlString)).mapTo[SparqlUpdateResponse] @@ -690,7 +754,8 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond ) updatedGroup: GroupADM = maybeUpdatedGroup.getOrElse( - throw UpdateNotPerformedException("Group was not updated. Please report this as a possible bug.")) + throw UpdateNotPerformedException("Group was not updated. Please report this as a possible bug.") + ) //_ = log.debug("updateProjectV1 - projectUpdatePayload: {} / updatedProject: {}", projectUpdatePayload, updatedProject) @@ -702,7 +767,8 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond _ = if (groupUpdatePayload.description.isDefined) { if (updatedGroup.description != groupUpdatePayload.description.get) throw UpdateNotPerformedException( - "Group's 'description' was not updated. Please report this as a possible bug.") + "Group's 'description' was not updated. Please report this as a possible bug." + ) } /* @@ -719,7 +785,8 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond _ = if (groupUpdatePayload.selfjoin.isDefined) { if (updatedGroup.selfjoin != groupUpdatePayload.selfjoin.get) throw UpdateNotPerformedException( - "Group's 'selfjoin' status was not updated. Please report this as a possible bug.") + "Group's 'selfjoin' status was not updated. Please report this as a possible bug." + ) } } yield GroupOperationResponseADM(group = updatedGroup) @@ -731,16 +798,18 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond //////////////////// /** - * Helper method that turns SPARQL result rows into a [[GroupADM]]. - * - * @param statements results from the SPARQL query representing information about the group. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user that is making the request. - * @return a [[GroupADM]] representing information about the group. - */ - private def statements2GroupADM(statements: (SubjectV2, Map[SmartIri, Seq[LiteralV2]]), - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Option[GroupADM]] = { + * Helper method that turns SPARQL result rows into a [[GroupADM]]. + * + * @param statements results from the SPARQL query representing information about the group. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user that is making the request. + * @return a [[GroupADM]] representing information about the group. + */ + private def statements2GroupADM( + statements: (SubjectV2, Map[SmartIri, Seq[LiteralV2]]), + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Option[GroupADM]] = { log.debug("statements2GroupADM - statements: {}", statements) @@ -766,32 +835,41 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond )).mapTo[Option[ProjectADM]] project: ProjectADM = maybeProject.getOrElse( - throw InconsistentRepositoryDataException(s"Group $groupIri has no project attached.")) + throw InconsistentRepositoryDataException(s"Group $groupIri has no project attached.") + ) groupADM: GroupADM = GroupADM( id = groupIri, name = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.GroupName.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no groupName attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.GroupName.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no groupName attached") + ) .head .asInstanceOf[StringLiteralV2] .value, description = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.GroupDescription.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no description attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.GroupDescription.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no description attached") + ) .head .asInstanceOf[StringLiteralV2] .value, project = project, status = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.Status.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no status attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.Status.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no status attached") + ) .head .asInstanceOf[BooleanLiteralV2] .value, selfjoin = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.HasSelfJoinEnabled.toSmartIri, - throw InconsistentRepositoryDataException(s"Group $groupIri has no selfJoin attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.HasSelfJoinEnabled.toSmartIri, + throw InconsistentRepositoryDataException(s"Group $groupIri has no selfJoin attached") + ) .head .asInstanceOf[BooleanLiteralV2] .value @@ -803,37 +881,37 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } /** - * Helper method for checking if a group identified by IRI exists. - * - * @param groupIri the IRI of the group. - * @return a [[Boolean]]. - */ - private def groupExists(groupIri: IRI): Future[Boolean] = { + * Helper method for checking if a group identified by IRI exists. + * + * @param groupIri the IRI of the group. + * @return a [[Boolean]]. + */ + private def groupExists(groupIri: IRI): Future[Boolean] = for { askString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkGroupExistsByIri(groupIri = groupIri).toString) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkGroupExistsByIri(groupIri = groupIri).toString + ) //_ = log.debug("groupExists - query: {}", askString) checkGroupExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkGroupExistsResponse.result } yield result - } /** - * Helper method for checking if a group identified by name / project IRI exists. - * - * @param name the name of the group. - * @param projectIri the IRI of the project. - * @return a [[Boolean]]. - */ - private def groupByNameAndProjectExists(name: String, projectIri: IRI): Future[Boolean] = { - + * Helper method for checking if a group identified by name / project IRI exists. + * + * @param name the name of the group. + * @param projectIri the IRI of the project. + * @return a [[Boolean]]. + */ + private def groupByNameAndProjectExists(name: String, projectIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkGroupExistsByName(projectIri = projectIri, name = name) - .toString) + .toString + ) //_ = log.debug("groupExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] @@ -842,21 +920,20 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond _ = log.debug("groupByNameAndProjectExists - name: {}, projectIri: {}, result: {}", name, projectIri, result) } yield result - } - /** - * In the case that the group was deactivated (status = false), the - * group members need to be removed from the group. - * - * @param changedGroup the group with the new status. - * @param featureFactoryConfig the feature factory configuration. - * @param apiRequestID the unique request ID. - * @return a [[GroupOperationResponseADM]] - */ - private def removeGroupMembersIfNecessary(changedGroup: GroupADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[GroupOperationResponseADM] = { - + * In the case that the group was deactivated (status = false), the + * group members need to be removed from the group. + * + * @param changedGroup the group with the new status. + * @param featureFactoryConfig the feature factory configuration. + * @param apiRequestID the unique request ID. + * @return a [[GroupOperationResponseADM]] + */ + private def removeGroupMembersIfNecessary( + changedGroup: GroupADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[GroupOperationResponseADM] = if (changedGroup.status) { // group active. no need to remove members. log.debug("removeGroupMembersIfNecessary - group active. no need to remove members.") @@ -885,6 +962,4 @@ class GroupsResponderADM(responderData: ResponderData) extends Responder(respond } yield GroupOperationResponseADM(group = changedGroup) } - } - } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala index 61bf6c4be3..b4ff968280 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala @@ -42,16 +42,16 @@ import scala.annotation.tailrec import scala.concurrent.Future /** - * A responder that returns information about hierarchical lists. - */ + * A responder that returns information about hierarchical lists. + */ class ListsResponderADM(responderData: ResponderData) extends Responder(responderData) { // The IRI used to lock user creation and update private val LISTS_GLOBAL_LOCK_IRI = "http://rdfh.ch/lists" /** - * Receives a message of type [[ListsResponderRequestADM]], and returns an appropriate response message. - */ + * Receives a message of type [[ListsResponderRequestADM]], and returns an appropriate response message. + */ def receive(msg: ListsResponderRequestADM) = msg match { case ListsGetRequestADM(projectIri, featureFactoryConfig, requestingUser) => listsGetRequestADM(projectIri, featureFactoryConfig, requestingUser) @@ -69,23 +69,29 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde nodeInfoChangeRequest(nodeIri, changeNodeRequest, featureFactoryConfig, apiRequestID) case NodeNameChangeRequestADM(nodeIri, changeNodeNameRequest, featureFactoryConfig, requestingUser, apiRequestID) => nodeNameChangeRequest(nodeIri, changeNodeNameRequest, featureFactoryConfig, requestingUser, apiRequestID) - case NodeLabelsChangeRequestADM(nodeIri, - changeNodeLabelsRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) => + case NodeLabelsChangeRequestADM( + nodeIri, + changeNodeLabelsRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => nodeLabelsChangeRequest(nodeIri, changeNodeLabelsRequest, featureFactoryConfig, requestingUser, apiRequestID) - case NodeCommentsChangeRequestADM(nodeIri, - changeNodeCommentsRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) => + case NodeCommentsChangeRequestADM( + nodeIri, + changeNodeCommentsRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => nodeCommentsChangeRequest(nodeIri, changeNodeCommentsRequest, featureFactoryConfig, requestingUser, apiRequestID) - case NodePositionChangeRequestADM(nodeIri, - changeNodePositionRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) => + case NodePositionChangeRequestADM( + nodeIri, + changeNodePositionRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => nodePositionChangeRequest(nodeIri, changeNodePositionRequest, featureFactoryConfig, requestingUser, apiRequestID) case ListItemDeleteRequestADM(nodeIri, featureFactoryConfig, requestingUser, apiRequestID) => deleteListItemRequestADM(nodeIri, featureFactoryConfig, requestingUser, apiRequestID) @@ -93,21 +99,21 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Gets all lists and returns them as a [[ListsGetResponseADM]]. For performance reasons - * (as lists can be very large), we only return the head of the list, i.e. the root node without - * any children. - * - * @param projectIri the IRI of the project the list belongs to. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[ListsGetResponseADM]]. - */ - private def listsGetRequestADM(projectIri: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ListsGetResponseADM] = { - + * Gets all lists and returns them as a [[ListsGetResponseADM]]. For performance reasons + * (as lists can be very large), we only return the head of the list, i.e. the root node without + * any children. + * + * @param projectIri the IRI of the project the list belongs to. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[ListsGetResponseADM]]. + */ + private def listsGetRequestADM( + projectIri: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ListsGetResponseADM] = // log.debug("listsGetRequestV2") - for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -159,78 +165,84 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // _ = log.debug("listsGetAdminRequest - items: {}", items) } yield ListsGetResponseADM(lists = lists) - } /** - * Retrieves a complete list (root and all children) from the triplestore and returns it as a optional [[ListADM]]. - * - * @param rootNodeIri the Iri if the root node of the list to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a optional [[ListADM]]. - */ - private def listGetADM(rootNodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Option[ListADM]] = { - + * Retrieves a complete list (root and all children) from the triplestore and returns it as a optional [[ListADM]]. + * + * @param rootNodeIri the Iri if the root node of the list to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a optional [[ListADM]]. + */ + private def listGetADM( + rootNodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Option[ListADM]] = for { // this query will give us only the information about the root node. exists <- rootNodeByIriExists(rootNodeIri) // _ = log.debug(s"listGetADM - exists: {}", exists) - maybeList: Option[ListADM] <- if (exists) { - for { - // here we know that the list exists and it is fine if children is an empty list - children: Seq[ListChildNodeADM] <- getChildren( - ofNodeIri = rootNodeIri, - shallow = false, - featureFactoryConfig = featureFactoryConfig, - KnoraSystemInstances.Users.SystemUser - ) + maybeList: Option[ListADM] <- + if (exists) { + for { + // here we know that the list exists and it is fine if children is an empty list + children: Seq[ListChildNodeADM] <- getChildren( + ofNodeIri = rootNodeIri, + shallow = false, + featureFactoryConfig = featureFactoryConfig, + KnoraSystemInstances.Users.SystemUser + ) - maybeRootNodeInfo <- listNodeInfoGetADM( - nodeIri = rootNodeIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + maybeRootNodeInfo <- listNodeInfoGetADM( + nodeIri = rootNodeIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) - // _ = log.debug(s"listGetADM - maybeRootNodeInfo: {}", maybeRootNodeInfo) + // _ = log.debug(s"listGetADM - maybeRootNodeInfo: {}", maybeRootNodeInfo) - rootNodeInfo = maybeRootNodeInfo match { - case Some(info: ListRootNodeInfoADM) => info.asInstanceOf[ListRootNodeInfoADM] - case Some(_: ListChildNodeInfoADM) => - throw InconsistentRepositoryDataException( - "A child node info was found, although we are expecting a root node info. Please report this as a possible bug.") - case Some(_) | None => - throw InconsistentRepositoryDataException( - "No info about list node found, although list node should exist. Please report this as a possible bug.") - } + rootNodeInfo = maybeRootNodeInfo match { + case Some(info: ListRootNodeInfoADM) => info.asInstanceOf[ListRootNodeInfoADM] + case Some(_: ListChildNodeInfoADM) => + throw InconsistentRepositoryDataException( + "A child node info was found, although we are expecting a root node info. Please report this as a possible bug." + ) + case Some(_) | None => + throw InconsistentRepositoryDataException( + "No info about list node found, although list node should exist. Please report this as a possible bug." + ) + } - list = ListADM(listinfo = rootNodeInfo, children = children) - } yield Some(list) - } else { - FastFuture.successful(None) - } + list = ListADM(listinfo = rootNodeInfo, children = children) + } yield Some(list) + } else { + FastFuture.successful(None) + } } yield maybeList - } /** - * Retrieves a complete node (root or child) with all children from the triplestore and returns it as a [[ListItemGetResponseADM]]. - * If an IRI of a root node is given, the response is a list with root node info and all chilren of the list. - * If an IRI of a child node is given, the response is a node with its information and all children of the sublist. - * - * @param nodeIri the Iri if the required node. - * @param requestingUser the user making the request. - * @return a [[ListItemGetResponseADM]]. - */ - private def listGetRequestADM(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ListItemGetResponseADM] = { - - def getNodeADM(childNode: ListChildNodeADM, - featureFactoryConfig: FeatureFactoryConfig): Future[ListNodeGetResponseADM] = { + * Retrieves a complete node (root or child) with all children from the triplestore and returns it as a [[ListItemGetResponseADM]]. + * If an IRI of a root node is given, the response is a list with root node info and all chilren of the list. + * If an IRI of a child node is given, the response is a node with its information and all children of the sublist. + * + * @param nodeIri the Iri if the required node. + * @param requestingUser the user making the request. + * @return a [[ListItemGetResponseADM]]. + */ + private def listGetRequestADM( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ListItemGetResponseADM] = { + + def getNodeADM( + childNode: ListChildNodeADM, + featureFactoryConfig: FeatureFactoryConfig + ): Future[ListNodeGetResponseADM] = for { maybeNodeInfo <- listNodeInfoGetADM( nodeIri = nodeIri, @@ -251,61 +263,63 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde ) ) } yield entirenode - } for { exists <- rootNodeByIriExists(nodeIri) // Is root node IRI given? - result <- if (exists) { - for { - // Yes. Get the entire list - maybeList <- listGetADM( - rootNodeIri = nodeIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + result <- + if (exists) { + for { + // Yes. Get the entire list + maybeList <- listGetADM( + rootNodeIri = nodeIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - entireList = maybeList match { - case Some(list) => ListGetResponseADM(list = list) - case None => throw NotFoundException(s"List '$nodeIri' not found") - } - } yield entireList - } else { - for { - // No. Get the node and all its sublist children. - // First, get node itself and all children. - maybeNode <- listNodeGetADM( - nodeIri = nodeIri, - shallow = true, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + entireList = maybeList match { + case Some(list) => ListGetResponseADM(list = list) + case None => throw NotFoundException(s"List '$nodeIri' not found") + } + } yield entireList + } else { + for { + // No. Get the node and all its sublist children. + // First, get node itself and all children. + maybeNode <- listNodeGetADM( + nodeIri = nodeIri, + shallow = true, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - entireNode <- maybeNode match { - // make sure that it is a child node - case Some(childNode: ListChildNodeADM) => - // get the info of the child node - getNodeADM(childNode, featureFactoryConfig) + entireNode <- maybeNode match { + // make sure that it is a child node + case Some(childNode: ListChildNodeADM) => + // get the info of the child node + getNodeADM(childNode, featureFactoryConfig) - case _ => throw NotFoundException(s"Node '$nodeIri' not found") - } - } yield entireNode - } + case _ => throw NotFoundException(s"Node '$nodeIri' not found") + } + } yield entireNode + } } yield result } /** - * Retrieves information about a single node (without information about children). The single node can be the - * lists root node or child node - * - * @param nodeIri the Iri if the list node to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a optional [[ListNodeInfoADM]]. - */ - private def listNodeInfoGetADM(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Option[ListNodeInfoADM]] = { + * Retrieves information about a single node (without information about children). The single node can be the + * lists root node or child node + * + * @param nodeIri the Iri if the list node to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a optional [[ListNodeInfoADM]]. + */ + private def listNodeInfoGetADM( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Option[ListNodeInfoADM]] = { for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -327,174 +341,10 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // _ = log.debug(s"listNodeInfoGetADM - statements: {}", statements) - maybeListNodeInfo = if (statements.nonEmpty) { - - val nodeInfo: ListNodeInfoADM = statements.head match { - case (nodeIri: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => - val labels: Seq[StringLiteralV2] = propsMap - .getOrElse(OntologyConstants.Rdfs.Label.toSmartIri, Seq.empty[StringLiteralV2]) - .map(_.asInstanceOf[StringLiteralV2]) - val comments: Seq[StringLiteralV2] = propsMap - .getOrElse(OntologyConstants.Rdfs.Comment.toSmartIri, Seq.empty[StringLiteralV2]) - .map(_.asInstanceOf[StringLiteralV2]) - - val attachedToProjectOption: Option[IRI] = - propsMap.get(OntologyConstants.KnoraBase.AttachedToProject.toSmartIri) match { - case Some(iris: Seq[LiteralV2]) => - iris.headOption match { - case Some(iri: IriLiteralV2) => Some(iri.value) - case other => - throw InconsistentRepositoryDataException( - s"Expected attached to project Iri as an IriLiteralV2 for list node $nodeIri, but got $other") - } - - case None => None - } - - val hasRootNodeOption: Option[IRI] = - propsMap.get(OntologyConstants.KnoraBase.HasRootNode.toSmartIri) match { - case Some(iris: Seq[LiteralV2]) => - iris.headOption match { - case Some(iri: IriLiteralV2) => Some(iri.value) - case other => - throw InconsistentRepositoryDataException( - s"Expected root node Iri as an IriLiteralV2 for list node $nodeIri, but got $other") - } - - case None => None - } - - val isRootNode: Boolean = propsMap.get(OntologyConstants.KnoraBase.IsRootNode.toSmartIri) match { - case Some(values: Seq[LiteralV2]) => - values.headOption match { - case Some(value: BooleanLiteralV2) => value.value - case Some(other) => - throw InconsistentRepositoryDataException( - s"Expected isRootNode as an BooleanLiteralV2 for list node $nodeIri, but got $other") - case None => false - } - - case None => false - } - - val positionOption: Option[Int] = propsMap - .get(OntologyConstants.KnoraBase.ListNodePosition.toSmartIri) - .map(_.head.asInstanceOf[IntLiteralV2].value) - - if (isRootNode) { - ListRootNodeInfoADM( - id = nodeIri.toString, - projectIri = attachedToProjectOption.getOrElse( - throw InconsistentRepositoryDataException( - s"Required attachedToProject property missing for list node $nodeIri.")), - name = propsMap - .get(OntologyConstants.KnoraBase.ListNodeName.toSmartIri) - .map(_.head.asInstanceOf[StringLiteralV2].value), - labels = StringLiteralSequenceV2(labels.toVector.sortBy(_.language)), - comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)) - ).unescape - } else { - ListChildNodeInfoADM( - id = nodeIri.toString, - name = propsMap - .get(OntologyConstants.KnoraBase.ListNodeName.toSmartIri) - .map(_.head.asInstanceOf[StringLiteralV2].value), - labels = StringLiteralSequenceV2(labels.toVector.sortBy(_.language)), - comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)), - position = positionOption.getOrElse( - throw InconsistentRepositoryDataException( - s"Required position property missing for list node $nodeIri.")), - hasRootNode = hasRootNodeOption.getOrElse( - throw InconsistentRepositoryDataException( - s"Required hasRootNode property missing for list node $nodeIri.")) - ).unescape - } - } - Some(nodeInfo) - } else { - None - } - - // _ = log.debug(s"listNodeInfoGetADM - maybeListNodeInfo: {}", maybeListNodeInfo) - - } yield maybeListNodeInfo - - } + maybeListNodeInfo = + if (statements.nonEmpty) { - /** - * Retrieves information about a single node (without information about children). The single node can be a - * root node or child node - * - * @param nodeIri the IRI of the list node to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[ChildNodeInfoGetResponseADM]]. - */ - private def listNodeInfoGetRequestADM(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[NodeInfoGetResponseADM] = { - for { - maybeListNodeInfoADM <- listNodeInfoGetADM( - nodeIri = nodeIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - result = maybeListNodeInfoADM match { - case Some(childInfo: ListChildNodeInfoADM) => ChildNodeInfoGetResponseADM(childInfo) - case Some(rootInfo: ListRootNodeInfoADM) => RootNodeInfoGetResponseADM(rootInfo) - case _ => throw NotFoundException(s"List node '$nodeIri' not found") - } - } yield result - } - - /** - * Retrieves a complete node including children. The node can be the lists root node or child node. - * - * @param nodeIri the IRI of the list node to be queried. - * @param shallow denotes if all children or only the immediate children will be returned. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a optional [[ListNodeADM]] - */ - private def listNodeGetADM(nodeIri: IRI, - shallow: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Option[ListNodeADM]] = { - for { - // this query will give us only the information about the root node. - sparqlQuery <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .getListNode( - triplestore = settings.triplestoreType, - nodeIri = nodeIri - ) - .toString() - ) - - listInfoResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparqlQuery, - featureFactoryConfig = featureFactoryConfig, - )).mapTo[SparqlExtendedConstructResponse] - - // _ = log.debug(s"listGetADM - statements: {}", MessageUtil.toSource(listInfoResponse.statements)) - - maybeListNode: Option[ListNodeADM] <- if (listInfoResponse.statements.nonEmpty) { - for { - // here we know that the list exists and it is fine if children is an empty list - children: Seq[ListChildNodeADM] <- getChildren( - ofNodeIri = nodeIri, - shallow = shallow, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - // _ = log.debug(s"listGetADM - children count: {}", children.size) - - // Map(subjectIri -> (objectIri -> Seq(stringWithOptionalLand)) - statements = listInfoResponse.statements - - node: ListNodeADM = statements.head match { + val nodeInfo: ListNodeInfoADM = statements.head match { case (nodeIri: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => val labels: Seq[StringLiteralV2] = propsMap .getOrElse(OntologyConstants.Rdfs.Label.toSmartIri, Seq.empty[StringLiteralV2]) @@ -510,7 +360,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde case Some(iri: IriLiteralV2) => Some(iri.value) case other => throw InconsistentRepositoryDataException( - s"Expected attached to project Iri as an IriLiteralV2 for list node $nodeIri, but got $other") + s"Expected attached to project Iri as an IriLiteralV2 for list node $nodeIri, but got $other" + ) } case None => None @@ -523,7 +374,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde case Some(iri: IriLiteralV2) => Some(iri.value) case other => throw InconsistentRepositoryDataException( - s"Expected root node Iri as an IriLiteralV2 for list node $nodeIri, but got $other") + s"Expected root node Iri as an IriLiteralV2 for list node $nodeIri, but got $other" + ) } case None => None @@ -535,7 +387,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde case Some(value: BooleanLiteralV2) => value.value case Some(other) => throw InconsistentRepositoryDataException( - s"Expected isRootNode as an BooleanLiteralV2 for list node $nodeIri, but got $other") + s"Expected isRootNode as an BooleanLiteralV2 for list node $nodeIri, but got $other" + ) case None => false } @@ -547,20 +400,21 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde .map(_.head.asInstanceOf[IntLiteralV2].value) if (isRootNode) { - ListRootNodeADM( + ListRootNodeInfoADM( id = nodeIri.toString, projectIri = attachedToProjectOption.getOrElse( throw InconsistentRepositoryDataException( - s"Required attachedToProject property missing for list node $nodeIri.")), + s"Required attachedToProject property missing for list node $nodeIri." + ) + ), name = propsMap .get(OntologyConstants.KnoraBase.ListNodeName.toSmartIri) .map(_.head.asInstanceOf[StringLiteralV2].value), labels = StringLiteralSequenceV2(labels.toVector.sortBy(_.language)), - comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)), - children = children - ) + comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)) + ).unescape } else { - ListChildNodeADM( + ListChildNodeInfoADM( id = nodeIri.toString, name = propsMap .get(OntologyConstants.KnoraBase.ListNodeName.toSmartIri) @@ -569,47 +423,232 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)), position = positionOption.getOrElse( throw InconsistentRepositoryDataException( - s"Required position property missing for list node $nodeIri.")), + s"Required position property missing for list node $nodeIri." + ) + ), hasRootNode = hasRootNodeOption.getOrElse( throw InconsistentRepositoryDataException( - s"Required hasRootNode property missing for list node $nodeIri.")), - children = children - ) + s"Required hasRootNode property missing for list node $nodeIri." + ) + ) + ).unescape } } + Some(nodeInfo) + } else { + None + } - // _ = log.debug(s"listGetADM - list: {}", MessageUtil.toSource(list)) - } yield Some(node) - } else { - FastFuture.successful(None) + // _ = log.debug(s"listNodeInfoGetADM - maybeListNodeInfo: {}", maybeListNodeInfo) + + } yield maybeListNodeInfo + + } + + /** + * Retrieves information about a single node (without information about children). The single node can be a + * root node or child node + * + * @param nodeIri the IRI of the list node to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[ChildNodeInfoGetResponseADM]]. + */ + private def listNodeInfoGetRequestADM( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[NodeInfoGetResponseADM] = + for { + maybeListNodeInfoADM <- listNodeInfoGetADM( + nodeIri = nodeIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + result = maybeListNodeInfoADM match { + case Some(childInfo: ListChildNodeInfoADM) => ChildNodeInfoGetResponseADM(childInfo) + case Some(rootInfo: ListRootNodeInfoADM) => RootNodeInfoGetResponseADM(rootInfo) + case _ => throw NotFoundException(s"List node '$nodeIri' not found") } + } yield result + + /** + * Retrieves a complete node including children. The node can be the lists root node or child node. + * + * @param nodeIri the IRI of the list node to be queried. + * @param shallow denotes if all children or only the immediate children will be returned. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a optional [[ListNodeADM]] + */ + private def listNodeGetADM( + nodeIri: IRI, + shallow: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Option[ListNodeADM]] = { + for { + // this query will give us only the information about the root node. + sparqlQuery <- Future( + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .getListNode( + triplestore = settings.triplestoreType, + nodeIri = nodeIri + ) + .toString() + ) + + listInfoResponse <- (storeManager ? SparqlExtendedConstructRequest( + sparql = sparqlQuery, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] + + // _ = log.debug(s"listGetADM - statements: {}", MessageUtil.toSource(listInfoResponse.statements)) + + maybeListNode: Option[ListNodeADM] <- + if (listInfoResponse.statements.nonEmpty) { + for { + // here we know that the list exists and it is fine if children is an empty list + children: Seq[ListChildNodeADM] <- getChildren( + ofNodeIri = nodeIri, + shallow = shallow, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + // _ = log.debug(s"listGetADM - children count: {}", children.size) + + // Map(subjectIri -> (objectIri -> Seq(stringWithOptionalLand)) + statements = listInfoResponse.statements + + node: ListNodeADM = statements.head match { + case (nodeIri: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => + val labels: Seq[StringLiteralV2] = propsMap + .getOrElse(OntologyConstants.Rdfs.Label.toSmartIri, Seq.empty[StringLiteralV2]) + .map(_.asInstanceOf[StringLiteralV2]) + val comments: Seq[StringLiteralV2] = propsMap + .getOrElse(OntologyConstants.Rdfs.Comment.toSmartIri, Seq.empty[StringLiteralV2]) + .map(_.asInstanceOf[StringLiteralV2]) + + val attachedToProjectOption: Option[IRI] = + propsMap.get(OntologyConstants.KnoraBase.AttachedToProject.toSmartIri) match { + case Some(iris: Seq[LiteralV2]) => + iris.headOption match { + case Some(iri: IriLiteralV2) => Some(iri.value) + case other => + throw InconsistentRepositoryDataException( + s"Expected attached to project Iri as an IriLiteralV2 for list node $nodeIri, but got $other" + ) + } + + case None => None + } + + val hasRootNodeOption: Option[IRI] = + propsMap.get(OntologyConstants.KnoraBase.HasRootNode.toSmartIri) match { + case Some(iris: Seq[LiteralV2]) => + iris.headOption match { + case Some(iri: IriLiteralV2) => Some(iri.value) + case other => + throw InconsistentRepositoryDataException( + s"Expected root node Iri as an IriLiteralV2 for list node $nodeIri, but got $other" + ) + } + + case None => None + } + + val isRootNode: Boolean = propsMap.get(OntologyConstants.KnoraBase.IsRootNode.toSmartIri) match { + case Some(values: Seq[LiteralV2]) => + values.headOption match { + case Some(value: BooleanLiteralV2) => value.value + case Some(other) => + throw InconsistentRepositoryDataException( + s"Expected isRootNode as an BooleanLiteralV2 for list node $nodeIri, but got $other" + ) + case None => false + } + + case None => false + } + + val positionOption: Option[Int] = propsMap + .get(OntologyConstants.KnoraBase.ListNodePosition.toSmartIri) + .map(_.head.asInstanceOf[IntLiteralV2].value) + + if (isRootNode) { + ListRootNodeADM( + id = nodeIri.toString, + projectIri = attachedToProjectOption.getOrElse( + throw InconsistentRepositoryDataException( + s"Required attachedToProject property missing for list node $nodeIri." + ) + ), + name = propsMap + .get(OntologyConstants.KnoraBase.ListNodeName.toSmartIri) + .map(_.head.asInstanceOf[StringLiteralV2].value), + labels = StringLiteralSequenceV2(labels.toVector.sortBy(_.language)), + comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)), + children = children + ) + } else { + ListChildNodeADM( + id = nodeIri.toString, + name = propsMap + .get(OntologyConstants.KnoraBase.ListNodeName.toSmartIri) + .map(_.head.asInstanceOf[StringLiteralV2].value), + labels = StringLiteralSequenceV2(labels.toVector.sortBy(_.language)), + comments = StringLiteralSequenceV2(comments.toVector.sortBy(_.language)), + position = positionOption.getOrElse( + throw InconsistentRepositoryDataException( + s"Required position property missing for list node $nodeIri." + ) + ), + hasRootNode = hasRootNodeOption.getOrElse( + throw InconsistentRepositoryDataException( + s"Required hasRootNode property missing for list node $nodeIri." + ) + ), + children = children + ) + } + } + + // _ = log.debug(s"listGetADM - list: {}", MessageUtil.toSource(list)) + } yield Some(node) + } else { + FastFuture.successful(None) + } } yield maybeListNode } /** - * Retrieves the child nodes from the triplestore. If shallow is true, then only the immediate children will be - * returned, otherwise all children and their children's children will be returned. - * - * @param ofNodeIri the IRI of the node for which children are to be returned. - * @param shallow denotes if all children or only the immediate children will be returned. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a sequence of [[ListChildNodeADM]]. - */ - private def getChildren(ofNodeIri: IRI, - shallow: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Seq[ListChildNodeADM]] = { + * Retrieves the child nodes from the triplestore. If shallow is true, then only the immediate children will be + * returned, otherwise all children and their children's children will be returned. + * + * @param ofNodeIri the IRI of the node for which children are to be returned. + * @param shallow denotes if all children or only the immediate children will be returned. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a sequence of [[ListChildNodeADM]]. + */ + private def getChildren( + ofNodeIri: IRI, + shallow: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Seq[ListChildNodeADM]] = { /** - * This function recursively transforms SPARQL query results representing a hierarchical list into a [[ListChildNodeADM]]. - * - * @param nodeIri the IRI of the node to be created. - * @param statements a [[Map]] in which each key is the IRI of a node in the hierarchical list, and each value is a [[Seq]] - * of SPARQL query results representing that node's children. - * @return a [[ListChildNodeADM]]. - */ + * This function recursively transforms SPARQL query results representing a hierarchical list into a [[ListChildNodeADM]]. + * + * @param nodeIri the IRI of the node to be created. + * @param statements a [[Map]] in which each key is the IRI of a node in the hierarchical list, and each value is a [[Seq]] + * of SPARQL query results representing that node's children. + * @return a [[ListChildNodeADM]]. + */ def createChildNode(nodeIri: IRI, statements: Seq[(SubjectV2, Map[SmartIri, Seq[LiteralV2]])]): ListChildNodeADM = { val propsMap: Map[SmartIri, Seq[LiteralV2]] = statements.filter(_._1 == IriSubjectV2(nodeIri)).head._2 @@ -637,7 +676,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde .get(OntologyConstants.KnoraBase.ListNodePosition.toSmartIri) .map(_.head.asInstanceOf[IntLiteralV2].value) val position = positionOption.getOrElse( - throw InconsistentRepositoryDataException(s"Required position property missing for list node $nodeIri.")) + throw InconsistentRepositoryDataException(s"Required position property missing for list node $nodeIri.") + ) val children: Seq[ListChildNodeADM] = propsMap.get(OntologyConstants.KnoraBase.HasSubListNode.toSmartIri) match { case Some(iris: Seq[LiteralV2]) => @@ -677,14 +717,16 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde nodeWithChildrenResponse <- (storeManager ? SparqlExtendedConstructRequest( sparql = nodeChildrenQuery, - featureFactoryConfig = featureFactoryConfig, + featureFactoryConfig = featureFactoryConfig )).mapTo[SparqlExtendedConstructResponse] statements: Seq[(SubjectV2, Map[SmartIri, Seq[LiteralV2]])] = nodeWithChildrenResponse.statements.toList startNodePropsMap: Map[SmartIri, Seq[LiteralV2]] = statements.filter(_._1 == IriSubjectV2(ofNodeIri)).head._2 - children: Seq[ListChildNodeADM] = startNodePropsMap.get(OntologyConstants.KnoraBase.HasSubListNode.toSmartIri) match { + children: Seq[ListChildNodeADM] = startNodePropsMap.get( + OntologyConstants.KnoraBase.HasSubListNode.toSmartIri + ) match { case Some(iris: Seq[LiteralV2]) => iris.map { iri => createChildNode(iri.toString, statements) @@ -699,27 +741,29 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Provides the path to a particular hierarchical list node. - * - * @param queryNodeIri the IRI of the node whose path is to be queried. - * @param requestingUser the user making the request. - */ + * Provides the path to a particular hierarchical list node. + * + * @param queryNodeIri the IRI of the node whose path is to be queried. + * @param requestingUser the user making the request. + */ private def nodePathGetAdminRequest(queryNodeIri: IRI, requestingUser: UserADM): Future[NodePathGetResponseADM] = { /** - * Recursively constructs the path to a node. - * - * @param node the IRI of the node whose path is to be constructed. - * @param nodeMap a [[Map]] of node IRIs to query result row data, in the format described below. - * @param parentMap a [[Map]] of child node IRIs to parent node IRIs. - * @param path the path constructed so far. - * @return the complete path to `node`. - */ + * Recursively constructs the path to a node. + * + * @param node the IRI of the node whose path is to be constructed. + * @param nodeMap a [[Map]] of node IRIs to query result row data, in the format described below. + * @param parentMap a [[Map]] of child node IRIs to parent node IRIs. + * @param path the path constructed so far. + * @return the complete path to `node`. + */ @tailrec - def makePath(node: IRI, - nodeMap: Map[IRI, Map[String, String]], - parentMap: Map[IRI, IRI], - path: Seq[NodePathElementADM]): Seq[NodePathElementADM] = { + def makePath( + node: IRI, + nodeMap: Map[IRI, Map[String, String]], + parentMap: Map[IRI, IRI], + path: Seq[NodePathElementADM] + ): Seq[NodePathElementADM] = { // Get the details of the node. val nodeData = nodeMap(node) @@ -786,31 +830,33 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde }.toMap // A Map of child node IRIs to parent node IRIs. - parentMap: Map[IRI, IRI] = nodePathResponse.results.bindings.foldLeft(Map.empty[IRI, IRI]) { - case (acc, row) => - row.rowMap.get("child") match { - case Some(child) => acc + (child -> row.rowMap("node")) - case None => acc - } + parentMap: Map[IRI, IRI] = nodePathResponse.results.bindings.foldLeft(Map.empty[IRI, IRI]) { case (acc, row) => + row.rowMap.get("child") match { + case Some(child) => acc + (child -> row.rowMap("node")) + case None => acc + } } } yield NodePathGetResponseADM(elements = makePath(queryNodeIri, nodeMap, parentMap, Nil)) } /** - * Creates a node (root or child). - * - * @param createNodeRequest the new node's information. - * @param featureFactoryConfig the feature factory configuration. - * @return a [newListNodeIri] - */ - private def createNode(createNodeRequest: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig): Future[IRI] = { + * Creates a node (root or child). + * + * @param createNodeRequest the new node's information. + * @param featureFactoryConfig the feature factory configuration. + * @return a [newListNodeIri] + */ + private def createNode( + createNodeRequest: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig + ): Future[IRI] = { def getPositionOfNewChild(children: Seq[ListChildNodeADM]): Int = { if (createNodeRequest.position.exists(_ > children.size)) { val givenPosition = createNodeRequest.position.get throw BadRequestException( - s"Invalid position given $givenPosition, maximum allowed position is = ${children.size}.") + s"Invalid position given $givenPosition, maximum allowed position is = ${children.size}." + ) } val position = if (createNodeRequest.position.isEmpty || createNodeRequest.position.exists(_.equals(-1))) { @@ -821,16 +867,17 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde position } - def getRootNodeIri(parentListNode: ListNodeADM): IRI = { + def getRootNodeIri(parentListNode: ListNodeADM): IRI = parentListNode match { case root: ListRootNodeADM => root.id case child: ListChildNodeADM => child.hasRootNode } - } - def getRootNodeAndPositionOfNewChild(parentNodeIri: IRI, - dataNamedGraph: IRI, - featureFactoryConfig: FeatureFactoryConfig): Future[(Some[Int], Some[IRI])] = { + def getRootNodeAndPositionOfNewChild( + parentNodeIri: IRI, + dataNamedGraph: IRI, + featureFactoryConfig: FeatureFactoryConfig + ): Future[(Some[Int], Some[IRI])] = for { /* Verify that the list node exists by retrieving the whole node including children one level deep (need for position calculation) */ maybeParentListNode <- listNodeGetADM( @@ -850,29 +897,29 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde position = getPositionOfNewChild(children) // Is the node supposed to be inserted in a specific position in array of children? - _ <- if (position != children.size) { - // Yes. Shift the siblings after the given position to right in order to free the position. - for { - // shift siblings that are after given position to right - updatedSiblings <- shiftNodes( - startPos = position, - endPos = children.size - 1, - nodes = children, - shiftToLeft = false, - dataNamedGraph = dataNamedGraph, - featureFactoryConfig = featureFactoryConfig - ) - } yield updatedSiblings - } else { - // No. new node will be appended to the end, no shifting is necessary. - Future.successful(children) - } + _ <- + if (position != children.size) { + // Yes. Shift the siblings after the given position to right in order to free the position. + for { + // shift siblings that are after given position to right + updatedSiblings <- shiftNodes( + startPos = position, + endPos = children.size - 1, + nodes = children, + shiftToLeft = false, + dataNamedGraph = dataNamedGraph, + featureFactoryConfig = featureFactoryConfig + ) + } yield updatedSiblings + } else { + // No. new node will be appended to the end, no shifting is necessary. + Future.successful(children) + } /* get the root node, depending on the type of the parent */ rootNodeIri = getRootNodeIri(parentListNode) } yield (Some(position), Some(rootNodeIri)) - } for { /* Verify that the project exists by retrieving it. We need the project information so that we can calculate the data graph and IRI for the new node. */ @@ -893,20 +940,24 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde val escapedName = createNodeRequest.name.get val unescapedName = stringFormatter.fromSparqlEncodedString(escapedName) throw BadRequestException( - s"The node name ${unescapedName} is already used by a list inside the project ${createNodeRequest.projectIri}.") + s"The node name ${unescapedName} is already used by a list inside the project ${createNodeRequest.projectIri}." + ) } // calculate the data named graph dataNamedGraph: IRI = stringFormatter.projectDataNamedGraphV2(project) // if parent node is known, find the root node of the list and the position of the new child node - (position: Option[Int], rootNodeIri: Option[IRI]) <- if (createNodeRequest.parentNodeIri.nonEmpty) { - getRootNodeAndPositionOfNewChild(parentNodeIri = createNodeRequest.parentNodeIri.get, - dataNamedGraph = dataNamedGraph, - featureFactoryConfig = featureFactoryConfig) - } else { - Future(None, None) - } + (position: Option[Int], rootNodeIri: Option[IRI]) <- + if (createNodeRequest.parentNodeIri.nonEmpty) { + getRootNodeAndPositionOfNewChild( + parentNodeIri = createNodeRequest.parentNodeIri.get, + dataNamedGraph = dataNamedGraph, + featureFactoryConfig = featureFactoryConfig + ) + } else { + Future(None, None) + } // check the custom IRI; if not given, create an unused IRI customListIri: Option[SmartIri] = createNodeRequest.id.map(iri => iri.toSmartIri) @@ -935,23 +986,27 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Creates a list. - * - * @param createRootRequest the new list's information. - * @param featureFactoryConfig the feature factory configuration. - * @param apiRequestID the unique api request ID. - * @return a [[RootNodeInfoGetResponseADM]] - */ - private def listCreateRequestADM(createRootRequest: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[ListGetResponseADM] = { + * Creates a list. + * + * @param createRootRequest the new list's information. + * @param featureFactoryConfig the feature factory configuration. + * @param apiRequestID the unique api request ID. + * @return a [[RootNodeInfoGetResponseADM]] + */ + private def listCreateRequestADM( + createRootRequest: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[ListGetResponseADM] = { /** - * The actual task run with an IRI lock. - */ - def listCreateTask(createRootRequest: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[ListGetResponseADM] = + * The actual task run with an IRI lock. + */ + def listCreateTask( + createRootRequest: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[ListGetResponseADM] = for { listRootIri <- createNode(createRootRequest, featureFactoryConfig) @@ -964,8 +1019,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde ) newListADM = maybeNewListADM.getOrElse( - throw UpdateNotPerformedException( - s"List $listRootIri was not created. Please report this as a possible bug.")) + throw UpdateNotPerformedException(s"List $listRootIri was not created. Please report this as a possible bug.") + ) // _ = log.debug(s"listCreateRequestADM - newListADM: $newListADM") @@ -982,21 +1037,23 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Changes basic node information stored (root or child) - * - * @param nodeIri the list's IRI. - * @param changeNodeRequest the new node information. - * @param featureFactoryConfig the feature factory configuration. - * @param apiRequestID the unique api request ID. - * @return a [[NodeInfoGetResponseADM]] - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws BadRequestException in the case when the list IRI given in the path does not match with the one given in the payload. - * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. - */ - private def nodeInfoChangeRequest(nodeIri: IRI, - changeNodeRequest: ChangeNodeInfoApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = { + * Changes basic node information stored (root or child) + * + * @param nodeIri the list's IRI. + * @param changeNodeRequest the new node information. + * @param featureFactoryConfig the feature factory configuration. + * @param apiRequestID the unique api request ID. + * @return a [[NodeInfoGetResponseADM]] + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws BadRequestException in the case when the list IRI given in the path does not match with the one given in the payload. + * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. + */ + private def nodeInfoChangeRequest( + nodeIri: IRI, + changeNodeRequest: ChangeNodeInfoApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = { def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = { @@ -1017,12 +1074,14 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * The actual task run with an IRI lock. - */ - def nodeInfoChangeTask(nodeIri: IRI, - changeNodeRequest: ChangeNodeInfoApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = + * The actual task run with an IRI lock. + */ + def nodeInfoChangeTask( + nodeIri: IRI, + changeNodeRequest: ChangeNodeInfoApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = for { // check if nodeIRI in path and payload match @@ -1068,23 +1127,27 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Creates a new child node and appends it to an existing list node. - * - * @param createChildNodeRequest the new list node's information. - * @param featureFactoryConfig the feature factory configuration. - * @param apiRequestID the unique api request ID. - * @return a [[ChildNodeInfoGetResponseADM]] - */ - private def listChildNodeCreateRequestADM(createChildNodeRequest: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[ChildNodeInfoGetResponseADM] = { + * Creates a new child node and appends it to an existing list node. + * + * @param createChildNodeRequest the new list node's information. + * @param featureFactoryConfig the feature factory configuration. + * @param apiRequestID the unique api request ID. + * @return a [[ChildNodeInfoGetResponseADM]] + */ + private def listChildNodeCreateRequestADM( + createChildNodeRequest: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[ChildNodeInfoGetResponseADM] = { /** - * The actual task run with an IRI lock. - */ - def listChildNodeCreateTask(createChildNodeRequest: CreateNodeApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - apiRequestID: UUID): Future[ChildNodeInfoGetResponseADM] = + * The actual task run with an IRI lock. + */ + def listChildNodeCreateTask( + createChildNodeRequest: CreateNodeApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + apiRequestID: UUID + ): Future[ChildNodeInfoGetResponseADM] = for { newListNodeIri <- createNode(createChildNodeRequest, featureFactoryConfig) // Verify that the list node was created. @@ -1097,10 +1160,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde case Some(childNode: ListChildNodeInfoADM) => childNode case Some(_: ListRootNodeInfoADM) => throw UpdateNotPerformedException( - s"Child node ${createChildNodeRequest.name} could not be created. Probably parent node Iri is missing in payload.") + s"Child node ${createChildNodeRequest.name} could not be created. Probably parent node Iri is missing in payload." + ) case _ => throw UpdateNotPerformedException( - s"List node $newListNodeIri was not created. Please report this as a possible bug.") + s"List node $newListNodeIri was not created. Please report this as a possible bug." + ) } } yield ChildNodeInfoGetResponseADM(nodeinfo = newListNode) @@ -1117,35 +1182,38 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Changes name of the node (root or child) - * - * @param nodeIri the node's IRI. - * @param changeNodeNameRequest the new node name. - * @param featureFactoryConfig the feature factory configuration. - * @param apiRequestID the unique api request ID. - * @return a [[NodeInfoGetResponseADM]] - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. - */ - private def nodeNameChangeRequest(nodeIri: IRI, - changeNodeNameRequest: ChangeNodeNameApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = { - - def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = { + * Changes name of the node (root or child) + * + * @param nodeIri the node's IRI. + * @param changeNodeNameRequest the new node name. + * @param featureFactoryConfig the feature factory configuration. + * @param apiRequestID the unique api request ID. + * @return a [[NodeInfoGetResponseADM]] + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. + */ + private def nodeNameChangeRequest( + nodeIri: IRI, + changeNodeNameRequest: ChangeNodeNameApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = { + + def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = if (updatedNode.getName.nonEmpty && updatedNode.getName.get != changeNodeNameRequest.name) throw UpdateNotPerformedException("Node's 'name' was not updated. Please report this as a possible bug.") - } /** - * The actual task run with an IRI lock. - */ - def nodeNameChangeTask(nodeIri: IRI, - changeNodeNameRequest: ChangeNodeNameApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = + * The actual task run with an IRI lock. + */ + def nodeNameChangeTask( + nodeIri: IRI, + changeNodeNameRequest: ChangeNodeNameApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = for { projectIri <- getProjectIriFromNode(nodeIri, featureFactoryConfig) @@ -1199,37 +1267,39 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Changes labels of the node (root or child) - * - * @param nodeIri the node's IRI. - * @param changeNodeLabelsRequest the new node labels. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[NodeInfoGetResponseADM]] - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. - */ - private def nodeLabelsChangeRequest(nodeIri: IRI, - changeNodeLabelsRequest: ChangeNodeLabelsApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = { - - def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = { + * Changes labels of the node (root or child) + * + * @param nodeIri the node's IRI. + * @param changeNodeLabelsRequest the new node labels. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[NodeInfoGetResponseADM]] + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. + */ + private def nodeLabelsChangeRequest( + nodeIri: IRI, + changeNodeLabelsRequest: ChangeNodeLabelsApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = { + + def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = if (updatedNode.getLabels.stringLiterals.diff(changeNodeLabelsRequest.labels).nonEmpty) throw UpdateNotPerformedException("Node's 'labels' were not updated. Please report this as a possible bug.") - } - /** - * The actual task run with an IRI lock. - */ - def nodeLabelsChangeTask(nodeIri: IRI, - changeNodeLabelsRequest: ChangeNodeLabelsApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = + * The actual task run with an IRI lock. + */ + def nodeLabelsChangeTask( + nodeIri: IRI, + changeNodeLabelsRequest: ChangeNodeLabelsApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = for { projectIri <- getProjectIriFromNode(nodeIri, featureFactoryConfig) @@ -1282,36 +1352,38 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Changes comments of the node (root or child) - * - * @param nodeIri the node's IRI. - * @param changeNodeCommentsRequest the new node comments. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[NodeInfoGetResponseADM]] - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. - */ - private def nodeCommentsChangeRequest(nodeIri: IRI, - changeNodeCommentsRequest: ChangeNodeCommentsApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = { - def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = { + * Changes comments of the node (root or child) + * + * @param nodeIri the node's IRI. + * @param changeNodeCommentsRequest the new node comments. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[NodeInfoGetResponseADM]] + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. + */ + private def nodeCommentsChangeRequest( + nodeIri: IRI, + changeNodeCommentsRequest: ChangeNodeCommentsApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = { + def verifyUpdatedNode(updatedNode: ListNodeInfoADM): Unit = if (updatedNode.getComments.stringLiterals.diff(changeNodeCommentsRequest.comments).nonEmpty) throw UpdateNotPerformedException("Node's 'comments' were not updated. Please report this as a possible bug.") - } - /** - * The actual task run with an IRI lock. - */ - def nodeCommentsChangeTask(nodeIri: IRI, - changeNodeCommentsRequest: ChangeNodeCommentsApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodeInfoGetResponseADM] = + * The actual task run with an IRI lock. + */ + def nodeCommentsChangeTask( + nodeIri: IRI, + changeNodeCommentsRequest: ChangeNodeCommentsApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodeInfoGetResponseADM] = for { projectIri <- getProjectIriFromNode(nodeIri, featureFactoryConfig) @@ -1365,32 +1437,34 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Changes position of the node - * - * @param nodeIri the node's IRI. - * @param changeNodePositionRequest the new node comments. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[NodePositionChangeResponseADM]] - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. - */ - private def nodePositionChangeRequest(nodeIri: IRI, - changeNodePositionRequest: ChangeNodePositionApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodePositionChangeResponseADM] = { + * Changes position of the node + * + * @param nodeIri the node's IRI. + * @param changeNodePositionRequest the new node comments. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[NodePositionChangeResponseADM]] + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed. + */ + private def nodePositionChangeRequest( + nodeIri: IRI, + changeNodePositionRequest: ChangeNodePositionApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodePositionChangeResponseADM] = { /** - * Checks if the given position is in range. - * The highest position a node can be placed is to the end of the parents children; that means length of existing - * children + 1 - * - * @param parentNode the parent to which the node should belong. - * @param isNewParent identifier that node is added to another parent or not. - * @throws BadRequestException if given position is out of range. - */ + * Checks if the given position is in range. + * The highest position a node can be placed is to the end of the parents children; that means length of existing + * children + 1 + * + * @param parentNode the parent to which the node should belong. + * @param isNewParent identifier that node is added to another parent or not. + * @throws BadRequestException if given position is out of range. + */ def isNewPositionValid(parentNode: ListNodeADM, isNewParent: Boolean): Unit = { val numberOfChildren = parentNode.getChildren.size // If the node must be added to a new parent, highest valid position is numberOfChildre. @@ -1416,13 +1490,13 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Checks that the position of the node is updated and node is sublist of specified parent. - * It also checks the sibling nodes are shifted accordingly. - * - * @param newPosition the new position of the node. - * @return the updated parent node with all its children as [[ListNodeADM]] - * @throws UpdateNotPerformedException if some thing has gone wrong during the update. - */ + * Checks that the position of the node is updated and node is sublist of specified parent. + * It also checks the sibling nodes are shifted accordingly. + * + * @param newPosition the new position of the node. + * @return the updated parent node with all its children as [[ListNodeADM]] + * @throws UpdateNotPerformedException if some thing has gone wrong during the update. + */ def verifyParentChildrenUpdate(newPosition: Int): Future[ListNodeADM] = for { maybeParentNode <- listNodeGetADM( @@ -1434,42 +1508,48 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde updatedParent = maybeParentNode.get updatedChildren: Seq[ListChildNodeADM] = updatedParent.getChildren (siblingsPositionedBefore: Seq[ListChildNodeADM], rest: Seq[ListChildNodeADM]) = updatedChildren.partition( - node => node.position < newPosition) + node => node.position < newPosition + ) // verify that node is among children of specified parent in correct position updatedNode = rest.head _ = if (updatedNode.id != nodeIri || updatedNode.position != newPosition) { throw UpdateNotPerformedException( - s"Node is not repositioned correctly in specified parent node. Please report this as a bug.") + s"Node is not repositioned correctly in specified parent node. Please report this as a bug." + ) } leftPositions: Seq[Int] = siblingsPositionedBefore.map(child => child.position) _ = if (leftPositions != leftPositions.sorted) { throw UpdateNotPerformedException( - s"Something has gone wrong with shifting nodes. Please report this as a bug.") + s"Something has gone wrong with shifting nodes. Please report this as a bug." + ) } siblingsPositionedAfter = rest.slice(1, rest.length) rightSiblings: Seq[Int] = siblingsPositionedAfter.map(child => child.position) _ = if (rightSiblings != rightSiblings.sorted) { throw UpdateNotPerformedException( - s"Something has gone wrong with shifting nodes. Please report this as a bug.") + s"Something has gone wrong with shifting nodes. Please report this as a bug." + ) } } yield updatedParent /** - * Changes position of the node within its original parent. - * - * @param node the node whose position should be updated. - * @param parentIri the IRI of the parent node. - * @param givenPosition the new node position. - * @param dataNamedGraph the new node position. - * @return the new position of the node [[Int]] - * @throws UpdateNotPerformedException in the case the given new position is the same as current position. - */ - def updatePositionWithinSameParent(node: ListChildNodeADM, - parentIri: IRI, - givenPosition: Int, - dataNamedGraph: IRI): Future[Int] = + * Changes position of the node within its original parent. + * + * @param node the node whose position should be updated. + * @param parentIri the IRI of the parent node. + * @param givenPosition the new node position. + * @param dataNamedGraph the new node position. + * @return the new position of the node [[Int]] + * @throws UpdateNotPerformedException in the case the given new position is the same as current position. + */ + def updatePositionWithinSameParent( + node: ListChildNodeADM, + parentIri: IRI, + givenPosition: Int, + dataNamedGraph: IRI + ): Future[Int] = for { // get parent node with its immediate children @@ -1480,15 +1560,17 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde requestingUser = KnoraSystemInstances.Users.SystemUser ) parentNode = maybeParentNode.getOrElse( - throw BadRequestException(s"The parent node ${parentIri} could node be found, report this as a bug.")) + throw BadRequestException(s"The parent node ${parentIri} could node be found, report this as a bug.") + ) _ = isNewPositionValid(parentNode, false) parentChildren = parentNode.getChildren currPosition = node.position // if givenPosition is -1, append the child to the end of the list of children - newPosition = if (givenPosition == -1) { - parentChildren.size - 1 - } else givenPosition + newPosition = + if (givenPosition == -1) { + parentChildren.size - 1 + } else givenPosition // update the position of the node itself _ <- updatePositionOfNode( @@ -1499,52 +1581,55 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde ) // update position of siblings - _ <- if (currPosition < newPosition) { - for { - // shift siblings to left - updatedSiblings <- shiftNodes( - startPos = currPosition + 1, - endPos = newPosition, - nodes = parentChildren, - shiftToLeft = true, - dataNamedGraph = dataNamedGraph, - featureFactoryConfig = featureFactoryConfig - ) - } yield updatedSiblings - } else if (currPosition > newPosition) { - for { - // shift siblings to right - updatedSiblings <- shiftNodes( - startPos = newPosition, - endPos = currPosition - 1, - nodes = parentChildren, - shiftToLeft = false, - dataNamedGraph = dataNamedGraph, - featureFactoryConfig = featureFactoryConfig - ) - } yield updatedSiblings - } else { - throw UpdateNotPerformedException(s"The given position is the same as node's current position.") - } + _ <- + if (currPosition < newPosition) { + for { + // shift siblings to left + updatedSiblings <- shiftNodes( + startPos = currPosition + 1, + endPos = newPosition, + nodes = parentChildren, + shiftToLeft = true, + dataNamedGraph = dataNamedGraph, + featureFactoryConfig = featureFactoryConfig + ) + } yield updatedSiblings + } else if (currPosition > newPosition) { + for { + // shift siblings to right + updatedSiblings <- shiftNodes( + startPos = newPosition, + endPos = currPosition - 1, + nodes = parentChildren, + shiftToLeft = false, + dataNamedGraph = dataNamedGraph, + featureFactoryConfig = featureFactoryConfig + ) + } yield updatedSiblings + } else { + throw UpdateNotPerformedException(s"The given position is the same as node's current position.") + } } yield newPosition /** - * Changes position of the node, remove from current parent and add to the specified parent. - * It shifts the new siblings and old siblings. - * - * @param node the node whose position should be updated. - * @param newParentIri the IRI of the new parent node. - * @param currParentIri the IRI of the current parent node. - * @param givenPosition the new node position. - * @param dataNamedGraph the new node position. - * @return the new position of the node [[Int]] - * @throws UpdateNotPerformedException in the case the given new position is the same as current position. - */ - def updateParentAndPosition(node: ListChildNodeADM, - newParentIri: IRI, - currParentIri: IRI, - givenPosition: Int, - dataNamedGraph: IRI): Future[Int] = + * Changes position of the node, remove from current parent and add to the specified parent. + * It shifts the new siblings and old siblings. + * + * @param node the node whose position should be updated. + * @param newParentIri the IRI of the new parent node. + * @param currParentIri the IRI of the current parent node. + * @param givenPosition the new node position. + * @param dataNamedGraph the new node position. + * @return the new position of the node [[Int]] + * @throws UpdateNotPerformedException in the case the given new position is the same as current position. + */ + def updateParentAndPosition( + node: ListChildNodeADM, + newParentIri: IRI, + currParentIri: IRI, + givenPosition: Int, + dataNamedGraph: IRI + ): Future[Int] = for { // get current parent node with its immediate children maybeCurrentParentNode <- listNodeGetADM( @@ -1568,9 +1653,10 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde currentNodePosition = node.position // if givenPosition is -1, append the child to the end of the list of children - newPosition = if (givenPosition == -1) { - newSiblings.size - } else givenPosition + newPosition = + if (givenPosition == -1) { + newSiblings.size + } else givenPosition // update the position of the node itself _ <- updatePositionOfNode( @@ -1591,23 +1677,24 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde ) // Is node supposed to be added to the end of new parent's children list? - _ <- if (givenPosition == -1 || givenPosition == newSiblings.size) { - // Yes. New siblings should not be shifted - Future(newSiblings) - } else { - // No. Shift new siblings with the same and higher position - // to right, as if the node is inserted in the given position - for { - updatedSiblings <- shiftNodes( - startPos = newPosition, - endPos = newSiblings.last.position, - nodes = newSiblings, - shiftToLeft = false, - dataNamedGraph = dataNamedGraph, - featureFactoryConfig = featureFactoryConfig - ) - } yield updatedSiblings - } + _ <- + if (givenPosition == -1 || givenPosition == newSiblings.size) { + // Yes. New siblings should not be shifted + Future(newSiblings) + } else { + // No. Shift new siblings with the same and higher position + // to right, as if the node is inserted in the given position + for { + updatedSiblings <- shiftNodes( + startPos = newPosition, + endPos = newSiblings.last.position, + nodes = newSiblings, + shiftToLeft = false, + dataNamedGraph = dataNamedGraph, + featureFactoryConfig = featureFactoryConfig + ) + } yield updatedSiblings + } /* update the sublists of parent nodes */ _ <- changeParentNode( @@ -1621,13 +1708,15 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield newPosition /** - * The actual task run with an IRI lock. - */ - def nodePositionChangeTask(nodeIri: IRI, - changeNodePositionRequest: ChangeNodePositionApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[NodePositionChangeResponseADM] = + * The actual task run with an IRI lock. + */ + def nodePositionChangeTask( + nodeIri: IRI, + changeNodePositionRequest: ChangeNodePositionApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[NodePositionChangeResponseADM] = for { projectIri <- getProjectIriFromNode(nodeIri, featureFactoryConfig) @@ -1656,22 +1745,23 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // get node's current parent currentParentNodeIri: IRI <- getParentNodeIRI(nodeIri, featureFactoryConfig) - newPosition <- if (currentParentNodeIri == changeNodePositionRequest.parentIri) { - updatePositionWithinSameParent( - node = node, - parentIri = currentParentNodeIri, - givenPosition = changeNodePositionRequest.position, - dataNamedGraph = dataNamedGraph - ) - } else { - updateParentAndPosition( - node = node, - newParentIri = changeNodePositionRequest.parentIri, - currParentIri = currentParentNodeIri, - givenPosition = changeNodePositionRequest.position, - dataNamedGraph = dataNamedGraph - ) - } + newPosition <- + if (currentParentNodeIri == changeNodePositionRequest.parentIri) { + updatePositionWithinSameParent( + node = node, + parentIri = currentParentNodeIri, + givenPosition = changeNodePositionRequest.position, + dataNamedGraph = dataNamedGraph + ) + } else { + updateParentAndPosition( + node = node, + newParentIri = changeNodePositionRequest.parentIri, + currParentIri = currentParentNodeIri, + givenPosition = changeNodePositionRequest.position, + dataNamedGraph = dataNamedGraph + ) + } /* Verify that the node position and parent children position were updated */ parentNode <- verifyParentChildrenUpdate(newPosition) } yield NodePositionChangeResponseADM(node = parentNode) @@ -1688,29 +1778,31 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } /** - * Delete a node (root or child). If a root node is given, check for its usage in data and ontology. If not used, - * delete the list and return a confirmation message. - * - * @param nodeIri the node's IRI. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[NodeInfoGetResponseADM]] - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws UpdateNotPerformedException in the case the node is in use and cannot be deleted. - */ - private def deleteListItemRequestADM(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[ListItemDeleteResponseADM] = { + * Delete a node (root or child). If a root node is given, check for its usage in data and ontology. If not used, + * delete the list and return a confirmation message. + * + * @param nodeIri the node's IRI. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[NodeInfoGetResponseADM]] + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws UpdateNotPerformedException in the case the node is in use and cannot be deleted. + */ + private def deleteListItemRequestADM( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[ListItemDeleteResponseADM] = { /** - * Checks if node itself or any of its children is in use. - * - * @param nodeIri the node's IRI. - * @param nodeChildren the children of the node. - * @throws BadRequestException in case a node or one of its children is in use. - */ + * Checks if node itself or any of its children is in use. + * + * @param nodeIri the node's IRI. + * @param nodeChildren the children of the node. + * @throws BadRequestException in case a node or one of its children is in use. + */ def isNodeOrItsChildrenUsed(nodeIri: IRI, nodeChildren: Seq[ListChildNodeADM]): Future[Unit] = for { // Is node itself in use? @@ -1732,27 +1824,30 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield () /** - * Delete a list (root node) or a child node after verifying that neither the node itself nor any of its children - * are used. If not used, delete the children of the node first, then delete the node itself. - * - * @param nodeIri the node's IRI. - * @param projectIri the feature factory configuration. - * @param children the children of the node. - * @param isRootNode the flag to determine the type of the node, root or child. - * @return a [[IRI]] - * @throws UpdateNotPerformedException in case a node is in use. - */ - def deleteListItem(nodeIri: IRI, - projectIri: IRI, - children: Seq[ListChildNodeADM], - isRootNode: Boolean): Future[IRI] = + * Delete a list (root node) or a child node after verifying that neither the node itself nor any of its children + * are used. If not used, delete the children of the node first, then delete the node itself. + * + * @param nodeIri the node's IRI. + * @param projectIri the feature factory configuration. + * @param children the children of the node. + * @param isRootNode the flag to determine the type of the node, root or child. + * @return a [[IRI]] + * @throws UpdateNotPerformedException in case a node is in use. + */ + def deleteListItem( + nodeIri: IRI, + projectIri: IRI, + children: Seq[ListChildNodeADM], + isRootNode: Boolean + ): Future[IRI] = for { // get the data graph of the project. dataNamedGraph <- getDataNamedGraph(projectIri, featureFactoryConfig) // delete the children errorCheckFutures: Seq[Future[Unit]] = children.map(child => - deleteNode(dataNamedGraph, child.id, isRootNode = false)) + deleteNode(dataNamedGraph, child.id, isRootNode = false) + ) _ <- Future.sequence(errorCheckFutures) // delete the node itself @@ -1761,22 +1856,24 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield dataNamedGraph /** - * Update the parent node of the deleted node by updating its remaining children. - * Shift the remaining children of the parent node with respect to the position of the deleted node. - * - * @param deletedNodeIri the IRI of the deleted node. - * @param positionOfDeletedNode the position of the deleted node. - * @param parentNodeIri the IRI of the deleted node's parent. - * @param dataNamedGraph the data named graph. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[ListNodeADM]] - * @throws UpdateNotPerformedException if the node that had to be deleted is still in the list of parent's children. - */ - def updateParentNode(deletedNodeIri: IRI, - positionOfDeletedNode: Int, - parentNodeIri: IRI, - dataNamedGraph: IRI, - featureFactoryConfig: FeatureFactoryConfig): Future[ListNodeADM] = + * Update the parent node of the deleted node by updating its remaining children. + * Shift the remaining children of the parent node with respect to the position of the deleted node. + * + * @param deletedNodeIri the IRI of the deleted node. + * @param positionOfDeletedNode the position of the deleted node. + * @param parentNodeIri the IRI of the deleted node's parent. + * @param dataNamedGraph the data named graph. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[ListNodeADM]] + * @throws UpdateNotPerformedException if the node that had to be deleted is still in the list of parent's children. + */ + def updateParentNode( + deletedNodeIri: IRI, + positionOfDeletedNode: Int, + parentNodeIri: IRI, + dataNamedGraph: IRI, + featureFactoryConfig: FeatureFactoryConfig + ): Future[ListNodeADM] = for { maybeNode <- listNodeGetADM( nodeIri = parentNodeIri, @@ -1786,7 +1883,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde ) parentNode: ListNodeADM = maybeNode.getOrElse( - throw BadRequestException(s"The parent node of $deletedNodeIri not found, report this as a bug.")) + throw BadRequestException(s"The parent node of $deletedNodeIri not found, report this as a bug.") + ) remainingChildren = parentNode.getChildren @@ -1795,20 +1893,21 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } // shift the siblings that were positioned after the deleted node, one place to left. - updatedChildren <- if (remainingChildren.size > 0) { - for { - shiftedChildren <- shiftNodes( - startPos = positionOfDeletedNode + 1, - endPos = remainingChildren.last.position, - nodes = remainingChildren, - shiftToLeft = true, - dataNamedGraph = dataNamedGraph, - featureFactoryConfig = featureFactoryConfig - ) - } yield shiftedChildren - } else { - Future.successful(remainingChildren) - } + updatedChildren <- + if (remainingChildren.size > 0) { + for { + shiftedChildren <- shiftNodes( + startPos = positionOfDeletedNode + 1, + endPos = remainingChildren.last.position, + nodes = remainingChildren, + shiftToLeft = true, + dataNamedGraph = dataNamedGraph, + featureFactoryConfig = featureFactoryConfig + ) + } yield shiftedChildren + } else { + Future.successful(remainingChildren) + } // return updated parent node with shifted children. updatedParentNode = parentNode match { @@ -1836,12 +1935,14 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield updatedParentNode /** - * The actual task run with an IRI lock. - */ - def nodeDeleteTask(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[ListItemDeleteResponseADM] = + * The actual task run with an IRI lock. + */ + def nodeDeleteTask( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[ListItemDeleteResponseADM] = for { projectIri <- getProjectIriFromNode(nodeIri, featureFactoryConfig) @@ -1917,12 +2018,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde //////////////////// /** - * Helper method for checking if a project identified by IRI exists. - * - * @param projectIri the IRI of the project. - * @return a [[Boolean]]. - */ - private def projectByIriExists(projectIri: IRI): Future[Boolean] = { + * Helper method for checking if a project identified by IRI exists. + * + * @param projectIri the IRI of the project. + * @return a [[Boolean]]. + */ + private def projectByIriExists(projectIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkProjectExistsByIri(projectIri).toString @@ -1933,15 +2034,14 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde result = askResponse.result } yield result - } /** - * Helper method for checking if a list node identified by IRI exists and is a root node. - * - * @param rootNodeIri the IRI of the project. - * @return a [[Boolean]]. - */ - private def rootNodeByIriExists(rootNodeIri: IRI): Future[Boolean] = { + * Helper method for checking if a list node identified by IRI exists and is a root node. + * + * @param rootNodeIri the IRI of the project. + * @return a [[Boolean]]. + */ + private def rootNodeByIriExists(rootNodeIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkListRootNodeExistsByIri(rootNodeIri).toString @@ -1952,15 +2052,14 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde result = askResponse.result } yield result - } /** - * Helper method for checking if a node identified by IRI exists. - * - * @param nodeIri the IRI of the project. - * @return a [[Boolean]]. - */ - private def nodeByIriExists(nodeIri: IRI): Future[Boolean] = { + * Helper method for checking if a node identified by IRI exists. + * + * @param nodeIri the IRI of the project. + * @return a [[Boolean]]. + */ + private def nodeByIriExists(nodeIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkListNodeExistsByIri(nodeIri).toString @@ -1971,17 +2070,16 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde result = askResponse.result } yield result - } /** - * Helper method for checking if a list node name is not used in any list inside a project. Returns a 'TRUE' if the - * name is NOT used inside any list of this project. - * - * @param projectIri the IRI of the project. - * @param listNodeName the list node name. - * @return a [[Boolean]]. - */ - private def listNodeNameIsProjectUnique(projectIri: IRI, listNodeName: Option[String]): Future[Boolean] = { + * Helper method for checking if a list node name is not used in any list inside a project. Returns a 'TRUE' if the + * name is NOT used inside any list of this project. + * + * @param projectIri the IRI of the project. + * @param listNodeName the list node name. + * @return a [[Boolean]]. + */ + private def listNodeNameIsProjectUnique(projectIri: IRI, listNodeName: Option[String]): Future[Boolean] = listNodeName match { case Some(name) => for { @@ -2002,27 +2100,31 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde case None => FastFuture.successful(true) } - } /** - * Helper method to generate a sparql statement for updating node information. - * - * @param changeNodeInfoRequest the node information to change. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[String]]. - */ - private def getUpdateNodeInfoSparqlStatement(changeNodeInfoRequest: ChangeNodeInfoApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig): Future[String] = + * Helper method to generate a sparql statement for updating node information. + * + * @param changeNodeInfoRequest the node information to change. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[String]]. + */ + private def getUpdateNodeInfoSparqlStatement( + changeNodeInfoRequest: ChangeNodeInfoApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig + ): Future[String] = for { // get the data graph of the project. dataNamedGraph <- getDataNamedGraph(changeNodeInfoRequest.projectIri, featureFactoryConfig) /* verify that the list name is unique for the project */ - nodeNameUnique: Boolean <- listNodeNameIsProjectUnique(changeNodeInfoRequest.projectIri, - changeNodeInfoRequest.name) + nodeNameUnique: Boolean <- listNodeNameIsProjectUnique( + changeNodeInfoRequest.projectIri, + changeNodeInfoRequest.name + ) _ = if (!nodeNameUnique) { throw DuplicateValueException( - s"The name ${changeNodeInfoRequest.name.get} is already used by a list inside the project ${changeNodeInfoRequest.projectIri}.") + s"The name ${changeNodeInfoRequest.name.get} is already used by a list inside the project ${changeNodeInfoRequest.projectIri}." + ) } /* Verify that the node with Iri exists. */ @@ -2034,7 +2136,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde ) node = maybeNode.getOrElse( - throw BadRequestException(s"List item with '${changeNodeInfoRequest.listIri}' not found.")) + throw BadRequestException(s"List item with '${changeNodeInfoRequest.listIri}' not found.") + ) isRootNode = maybeNode match { case Some(_: ListRootNodeADM) => true @@ -2062,12 +2165,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield changeNodeInfoSparqlString /** - * Helper method to get projectIri of a node. - * - * @param nodeIri the IRI of the node. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[IRI]]. - */ + * Helper method to get projectIri of a node. + * + * @param nodeIri the IRI of the node. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[IRI]]. + */ private def getProjectIriFromNode(nodeIri: IRI, featureFactoryConfig: FeatureFactoryConfig): Future[IRI] = for { maybeNode <- listNodeGetADM( @@ -2099,12 +2202,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield projectIri /** - * Helper method to check if a node is in use. - * - * @param nodeIri the IRI of the node. - * @param errorFun a function that throws an exception. It will be called if the node is used. - * @return a [[Boolean]]. - */ + * Helper method to check if a node is in use. + * + * @param nodeIri the IRI of the node. + * @param errorFun a function that throws an exception. It will be called if the node is used. + * @return a [[Boolean]]. + */ protected def isNodeUsed(nodeIri: IRI, errorFun: => Nothing): Future[Unit] = for { isNodeUsedSparql <- Future( @@ -2125,12 +2228,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield () /** - * Helper method to get the data named graph of a project. - * - * @param projectIri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @return an [[IRI]]. - */ + * Helper method to get the data named graph of a project. + * + * @param projectIri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @return an [[IRI]]. + */ protected def getDataNamedGraph(projectIri: IRI, featureFactoryConfig: FeatureFactoryConfig): Future[IRI] = for { /* Get the project information */ @@ -2152,12 +2255,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield dataNamedGraph /** - * Helper method to get parent of a node. - * - * @param nodeIri the IRI of the node. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[ListNodeADM]]. - */ + * Helper method to get parent of a node. + * + * @param nodeIri the IRI of the node. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[ListNodeADM]]. + */ protected def getParentNodeIRI(nodeIri: IRI, featureFactoryConfig: FeatureFactoryConfig): Future[IRI] = for { // query statement @@ -2183,14 +2286,14 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield parentNodeIri /** - * Helper method to delete a node. - * - * @param dataNamedGraph the data named graph of the project. - * @param nodeIri the IRI of the node. - * @param isRootNode is the node to be deleted a root node? - * @throws UpdateNotPerformedException if the node could not be deleted. - * @return a [[ListNodeADM]]. - */ + * Helper method to delete a node. + * + * @param dataNamedGraph the data named graph of the project. + * @param nodeIri the IRI of the node. + * @param isRootNode is the node to be deleted a root node? + * @throws UpdateNotPerformedException if the node could not be deleted. + * @return a [[ListNodeADM]]. + */ protected def deleteNode(dataNamedGraph: IRI, nodeIri: IRI, isRootNode: Boolean): Future[Unit] = for { @@ -2218,19 +2321,21 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield () /** - * Helper method to update position of a node without changing its parent. - * - * @param nodeIri the IRI of the node that must be shifted. - * @param newPosition the new position of the child node. - * @param dataNamedGraph the data named graph of the project. - * @param featureFactoryConfig the feature factory configuration. - * @throws UpdateNotPerformedException if the position of the node could not be updated. - * @return a [[ListChildNodeADM]]. - */ - protected def updatePositionOfNode(nodeIri: IRI, - newPosition: Int, - dataNamedGraph: IRI, - featureFactoryConfig: FeatureFactoryConfig): Future[ListChildNodeADM] = + * Helper method to update position of a node without changing its parent. + * + * @param nodeIri the IRI of the node that must be shifted. + * @param newPosition the new position of the child node. + * @param dataNamedGraph the data named graph of the project. + * @param featureFactoryConfig the feature factory configuration. + * @throws UpdateNotPerformedException if the position of the node could not be updated. + * @return a [[ListChildNodeADM]]. + */ + protected def updatePositionOfNode( + nodeIri: IRI, + newPosition: Int, + dataNamedGraph: IRI, + featureFactoryConfig: FeatureFactoryConfig + ): Future[ListChildNodeADM] = for { // Generate SPARQL for erasing a node. sparqlUpdateNodePosition: String <- Future( @@ -2260,34 +2365,38 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde _ = if (!childNode.position.equals(newPosition)) { throw UpdateNotPerformedException( - s"The position of the node $nodeIri could not be updated, report this as a possible bug.") + s"The position of the node $nodeIri could not be updated, report this as a possible bug." + ) } } yield childNode /** - * Helper method to shift nodes between positions startPos and endPos to the left if 'shiftToLeft' is true, - * otherwise shift them one position to the right. - * - * @param startPos the position of first node in range that must be shifted. - * @param endPos the position of last node in range that must be shifted. - * @param nodes the list of all nodes. - * @param shiftToLeft shift nodes to left if true, otherwise to right. - * @param dataNamedGraph the data named graph of the project. - * @param featureFactoryConfig the feature factory configuration. - * @throws UpdateNotPerformedException if the position of a node could not be updated. - * @return a sequence of [[ListChildNodeADM]]. - */ - protected def shiftNodes(startPos: Int, - endPos: Int, - nodes: Seq[ListChildNodeADM], - shiftToLeft: Boolean, - dataNamedGraph: IRI, - featureFactoryConfig: FeatureFactoryConfig): Future[Seq[ListChildNodeADM]] = + * Helper method to shift nodes between positions startPos and endPos to the left if 'shiftToLeft' is true, + * otherwise shift them one position to the right. + * + * @param startPos the position of first node in range that must be shifted. + * @param endPos the position of last node in range that must be shifted. + * @param nodes the list of all nodes. + * @param shiftToLeft shift nodes to left if true, otherwise to right. + * @param dataNamedGraph the data named graph of the project. + * @param featureFactoryConfig the feature factory configuration. + * @throws UpdateNotPerformedException if the position of a node could not be updated. + * @return a sequence of [[ListChildNodeADM]]. + */ + protected def shiftNodes( + startPos: Int, + endPos: Int, + nodes: Seq[ListChildNodeADM], + shiftToLeft: Boolean, + dataNamedGraph: IRI, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Seq[ListChildNodeADM]] = for { nodesTobeUpdated: Seq[ListChildNodeADM] <- Future( - nodes.filter(node => node.position >= startPos && node.position <= endPos)) + nodes.filter(node => node.position >= startPos && node.position <= endPos) + ) staticStartNodes = nodes.filter(node => node.position < startPos) staticEndNotes = nodes.filter(node => node.position > endPos) updatePositionFutures = nodesTobeUpdated.map { child => @@ -2307,20 +2416,22 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde } yield staticStartNodes ++ updatedNodes ++ staticEndNotes /** - * Helper method to change parent node of a node. - * - * @param nodeIri the IRI of the node. - * @param oldParentIri the IRI of the current parent node. - * @param newParentIri the IRI of the new parent node. - * @param dataNamedGraph the data named graph of the project. - * @param featureFactoryConfig the feature factory configuration. - * @throws UpdateNotPerformedException if the parent of a node could not be updated. - */ - protected def changeParentNode(nodeIri: IRI, - oldParentIri: IRI, - newParentIri: IRI, - dataNamedGraph: IRI, - featureFactoryConfig: FeatureFactoryConfig): Future[Unit] = + * Helper method to change parent node of a node. + * + * @param nodeIri the IRI of the node. + * @param oldParentIri the IRI of the current parent node. + * @param newParentIri the IRI of the new parent node. + * @param dataNamedGraph the data named graph of the project. + * @param featureFactoryConfig the feature factory configuration. + * @throws UpdateNotPerformedException if the parent of a node could not be updated. + */ + protected def changeParentNode( + nodeIri: IRI, + oldParentIri: IRI, + newParentIri: IRI, + dataNamedGraph: IRI, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Unit] = for { // Generate SPARQL for changing the parent node of the node. diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala index 66d95b1103..41b5583aec 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala @@ -45,8 +45,8 @@ import scala.collection.mutable.ListBuffer import scala.concurrent.Future /** - * Provides information about permissions to other responders. - */ + * Provides information about permissions to other responders. + */ class PermissionsResponderADM(responderData: ResponderData) extends Responder(responderData) { private val PERMISSIONS_GLOBAL_LOCK_IRI = "http://rdfh.ch/permissions" @@ -55,21 +55,25 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re private val PropertyEntityType = "property" /** - * Receives a message extending [[PermissionsResponderRequestADM]], and returns an appropriate response message. - */ + * Receives a message extending [[PermissionsResponderRequestADM]], and returns an appropriate response message. + */ def receive(msg: PermissionsResponderRequestADM) = msg match { - case PermissionDataGetADM(projectIris, - groupIris, - isInProjectAdminGroup, - isInSystemAdminGroup, - featureFactoryConfig, - requestingUser) => - permissionsDataGetADM(projectIris, - groupIris, - isInProjectAdminGroup, - isInSystemAdminGroup, - featureFactoryConfig, - requestingUser) + case PermissionDataGetADM( + projectIris, + groupIris, + isInProjectAdminGroup, + isInSystemAdminGroup, + featureFactoryConfig, + requestingUser + ) => + permissionsDataGetADM( + projectIris, + groupIris, + isInProjectAdminGroup, + isInSystemAdminGroup, + featureFactoryConfig, + requestingUser + ) case AdministrativePermissionsForProjectGetRequestADM(projectIri, requestingUser, apiRequestID) => administrativePermissionsForProjectGetRequestADM(projectIri, requestingUser, apiRequestID) case AdministrativePermissionForIriGetRequestADM(administrativePermissionIri, requestingUser, apiRequestID) => @@ -78,85 +82,115 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re administrativePermissionForProjectGroupGetADM(projectIri, groupIri, requestingUser) case AdministrativePermissionForProjectGroupGetRequestADM(projectIri, groupIri, requestingUser) => administrativePermissionForProjectGroupGetRequestADM(projectIri, groupIri, requestingUser) - case AdministrativePermissionCreateRequestADM(newAdministrativePermission, - featureFactoryConfig, - requestingUser, - apiRequestID) => - administrativePermissionCreateRequestADM(newAdministrativePermission.prepareHasPermissions, - featureFactoryConfig, - requestingUser, - apiRequestID) + case AdministrativePermissionCreateRequestADM( + newAdministrativePermission, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => + administrativePermissionCreateRequestADM( + newAdministrativePermission.prepareHasPermissions, + featureFactoryConfig, + requestingUser, + apiRequestID + ) case ObjectAccessPermissionsForResourceGetADM(resourceIri, requestingUser) => objectAccessPermissionsForResourceGetADM(resourceIri, requestingUser) case ObjectAccessPermissionsForValueGetADM(valueIri, requestingUser) => objectAccessPermissionsForValueGetADM(valueIri, requestingUser) case DefaultObjectAccessPermissionsForProjectGetRequestADM(projectIri, requestingUser, apiRequestID) => defaultObjectAccessPermissionsForProjectGetRequestADM(projectIri, requestingUser, apiRequestID) - case DefaultObjectAccessPermissionForIriGetRequestADM(defaultObjectAccessPermissionIri, - requestingUser, - apiRequestID) => + case DefaultObjectAccessPermissionForIriGetRequestADM( + defaultObjectAccessPermissionIri, + requestingUser, + apiRequestID + ) => defaultObjectAccessPermissionForIriGetRequestADM(defaultObjectAccessPermissionIri, requestingUser, apiRequestID) - case DefaultObjectAccessPermissionGetRequestADM(projectIri, - groupIri, - resourceClassIri, - propertyIri, - requestingUser) => + case DefaultObjectAccessPermissionGetRequestADM( + projectIri, + groupIri, + resourceClassIri, + propertyIri, + requestingUser + ) => defaultObjectAccessPermissionGetRequestADM(projectIri, groupIri, resourceClassIri, propertyIri, requestingUser) - case DefaultObjectAccessPermissionsStringForResourceClassGetADM(projectIri, - resourceClassIri, - targetUser, - requestingUser) => - defaultObjectAccessPermissionsStringForEntityGetADM(projectIri, - resourceClassIri, - None, - ResourceEntityType, - targetUser, - requestingUser) - case DefaultObjectAccessPermissionsStringForPropertyGetADM(projectIri, - resourceClassIri, - propertyTypeIri, - targetUser, - requestingUser) => - defaultObjectAccessPermissionsStringForEntityGetADM(projectIri, - resourceClassIri, - Some(propertyTypeIri), - PropertyEntityType, - targetUser, - requestingUser) - case DefaultObjectAccessPermissionCreateRequestADM(createRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) => - defaultObjectAccessPermissionCreateRequestADM(createRequest.prepareHasPermissions, - featureFactoryConfig, - requestingUser, - apiRequestID) + case DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri, + resourceClassIri, + targetUser, + requestingUser + ) => + defaultObjectAccessPermissionsStringForEntityGetADM( + projectIri, + resourceClassIri, + None, + ResourceEntityType, + targetUser, + requestingUser + ) + case DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri, + resourceClassIri, + propertyTypeIri, + targetUser, + requestingUser + ) => + defaultObjectAccessPermissionsStringForEntityGetADM( + projectIri, + resourceClassIri, + Some(propertyTypeIri), + PropertyEntityType, + targetUser, + requestingUser + ) + case DefaultObjectAccessPermissionCreateRequestADM( + createRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => + defaultObjectAccessPermissionCreateRequestADM( + createRequest.prepareHasPermissions, + featureFactoryConfig, + requestingUser, + apiRequestID + ) case PermissionsForProjectGetRequestADM(projectIri, groupIri, featureFactoryConfig, requestingUser) => permissionsForProjectGetRequestADM(projectIri, groupIri, featureFactoryConfig, requestingUser) case PermissionByIriGetRequestADM(permissionIri, requestingUser) => permissionByIriGetRequestADM(permissionIri, requestingUser) case PermissionChangeGroupRequestADM(permissionIri, changePermissionGroupRequest, requestingUser, apiRequestID) => permissionGroupChangeRequestADM(permissionIri, changePermissionGroupRequest, requestingUser, apiRequestID) - case PermissionChangeHasPermissionsRequestADM(permissionIri, - changePermissionHasPermissionsRequest, - requestingUser, - apiRequestID) => - permissionHasPermissionsChangeRequestADM(permissionIri, - changePermissionHasPermissionsRequest, - requestingUser, - apiRequestID) - case PermissionChangeResourceClassRequestADM(permissionIri, - changePermissionResourceClassRequest, - requestingUser, - apiRequestID) => - permissionResourceClassChangeRequestADM(permissionIri, - changePermissionResourceClassRequest, - requestingUser, - apiRequestID) - case PermissionChangePropertyRequestADM(permissionIri, - changePermissionPropertyRequest, - requestingUser, - apiRequestID) => + case PermissionChangeHasPermissionsRequestADM( + permissionIri, + changePermissionHasPermissionsRequest, + requestingUser, + apiRequestID + ) => + permissionHasPermissionsChangeRequestADM( + permissionIri, + changePermissionHasPermissionsRequest, + requestingUser, + apiRequestID + ) + case PermissionChangeResourceClassRequestADM( + permissionIri, + changePermissionResourceClassRequest, + requestingUser, + apiRequestID + ) => + permissionResourceClassChangeRequestADM( + permissionIri, + changePermissionResourceClassRequest, + requestingUser, + apiRequestID + ) + case PermissionChangePropertyRequestADM( + permissionIri, + changePermissionPropertyRequest, + requestingUser, + apiRequestID + ) => permissionPropertyChangeRequestADM(permissionIri, changePermissionPropertyRequest, requestingUser, apiRequestID) case PermissionDeleteRequestADM(permissionIri, requestingUser, apiRequestID) => permissionDeleteRequestADM(permissionIri, requestingUser, apiRequestID) @@ -168,21 +202,23 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /////////////////////////////////////////////////////////////////////////// /** - * Creates the user's [[PermissionsDataADM]] - * - * @param projectIris the projects the user is part of. - * @param groupIris the groups the user is member of (without ProjectMember, ProjectAdmin, SystemAdmin) - * @param isInProjectAdminGroups the projects in which the user is member of the ProjectAdmin group. - * @param isInSystemAdminGroup the flag denoting membership in the SystemAdmin group. - * @param featureFactoryConfig the feature factory configuration. - * @return - */ - private def permissionsDataGetADM(projectIris: Seq[IRI], - groupIris: Seq[IRI], - isInProjectAdminGroups: Seq[IRI], - isInSystemAdminGroup: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[PermissionsDataADM] = { + * Creates the user's [[PermissionsDataADM]] + * + * @param projectIris the projects the user is part of. + * @param groupIris the groups the user is member of (without ProjectMember, ProjectAdmin, SystemAdmin) + * @param isInProjectAdminGroups the projects in which the user is member of the ProjectAdmin group. + * @param isInSystemAdminGroup the flag denoting membership in the SystemAdmin group. + * @param featureFactoryConfig the feature factory configuration. + * @return + */ + private def permissionsDataGetADM( + projectIris: Seq[IRI], + groupIris: Seq[IRI], + isInProjectAdminGroups: Seq[IRI], + isInSystemAdminGroup: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[PermissionsDataADM] = { // find out which project each group belongs to //_ = log.debug("getPermissionsProfileV1 - find out to which project each group belongs to") @@ -197,7 +233,9 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re group = maybeGroup.getOrElse( throw InconsistentRepositoryDataException( - s"Cannot find information for group: '$groupIri'. Please report as possible bug.")) + s"Cannot find information for group: '$groupIri'. Please report as possible bug." + ) + ) res = (group.project.id, groupIri) } yield res } @@ -212,33 +250,36 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re //_ = log.debug(s"permissionsProfileGetV1 - groups: {}", MessageUtil.toSource(groups)) /* materialize implicit membership in 'http://www.knora.org/ontology/knora-base#ProjectMember' group for each project */ - projectMembers: Seq[(IRI, IRI)] = if (projectIris.nonEmpty) { - for { - projectIri <- projectIris.toVector - res = (projectIri, OntologyConstants.KnoraAdmin.ProjectMember) - } yield res - } else { - Seq.empty[(IRI, IRI)] - } + projectMembers: Seq[(IRI, IRI)] = + if (projectIris.nonEmpty) { + for { + projectIri <- projectIris.toVector + res = (projectIri, OntologyConstants.KnoraAdmin.ProjectMember) + } yield res + } else { + Seq.empty[(IRI, IRI)] + } //_ = log.debug(s"permissionsProfileGetV1 - projectMembers: {}", MessageUtil.toSource(projectMembers)) /* materialize implicit membership in 'http://www.knora.org/ontology/knora-base#ProjectAdmin' group for each project */ - projectAdmins: Seq[(IRI, IRI)] = if (projectIris.nonEmpty) { - for { - projectAdminForGroup <- isInProjectAdminGroups - res = (projectAdminForGroup, OntologyConstants.KnoraAdmin.ProjectAdmin) - } yield res - } else { - Seq.empty[(IRI, IRI)] - } + projectAdmins: Seq[(IRI, IRI)] = + if (projectIris.nonEmpty) { + for { + projectAdminForGroup <- isInProjectAdminGroups + res = (projectAdminForGroup, OntologyConstants.KnoraAdmin.ProjectAdmin) + } yield res + } else { + Seq.empty[(IRI, IRI)] + } //_ = log.debug("permissionsProfileGetV1 - projectAdmins: {}", MessageUtil.toSource(projectAdmins)) /* materialize implicit membership in 'http://www.knora.org/ontology/knora-base#SystemAdmin' group */ - systemAdmin: Seq[(IRI, IRI)] = if (isInSystemAdminGroup) { - Seq((OntologyConstants.KnoraAdmin.SystemProject, OntologyConstants.KnoraAdmin.SystemAdmin)) - } else { - Seq.empty[(IRI, IRI)] - } + systemAdmin: Seq[(IRI, IRI)] = + if (isInSystemAdminGroup) { + Seq((OntologyConstants.KnoraAdmin.SystemProject, OntologyConstants.KnoraAdmin.SystemAdmin)) + } else { + Seq.empty[(IRI, IRI)] + } //_ = log.debug(s"permissionsProfileGetV1 - systemAdmin: {}", MessageUtil.toSource(systemAdmin)) /* combine explicit groups with materialized implicit groups */ @@ -249,11 +290,12 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // _ = log.debug(s"permissionsProfileGetV1 - groupsPerProject: {}", MessageUtil.toSource(groupsPerProject)) /* retrieve the administrative permissions for each group per project the user is member of */ - administrativePermissionsPerProjectFuture: Future[Map[IRI, Set[PermissionADM]]] = if (projectIris.nonEmpty) { - userAdministrativePermissionsGetADM(groupsPerProject) - } else { - Future(Map.empty[IRI, Set[PermissionADM]]) - } + administrativePermissionsPerProjectFuture: Future[Map[IRI, Set[PermissionADM]]] = + if (projectIris.nonEmpty) { + userAdministrativePermissionsGetADM(groupsPerProject) + } else { + Future(Map.empty[IRI, Set[PermissionADM]]) + } administrativePermissionsPerProject <- administrativePermissionsPerProjectFuture /* construct the permission profile from the different parts */ @@ -267,14 +309,15 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * By providing all the projects and groups in which the user is a member of, calculate the user's - * administrative permissions of each project by applying the precedence rules. - * - * @param groupsPerProject the groups inside each project the user is member of. - * @return a the user's resulting set of administrative permissions for each project. - */ + * By providing all the projects and groups in which the user is a member of, calculate the user's + * administrative permissions of each project by applying the precedence rules. + * + * @param groupsPerProject the groups inside each project the user is member of. + * @return a the user's resulting set of administrative permissions for each project. + */ private def userAdministrativePermissionsGetADM( - groupsPerProject: Map[IRI, Seq[IRI]]): Future[Map[IRI, Set[PermissionADM]]] = { + groupsPerProject: Map[IRI, Seq[IRI]] + ): Future[Map[IRI, Set[PermissionADM]]] = { /* Get all permissions per project, applying permission precedence rule */ def calculatePermission(projectIri: IRI, extendedUserGroups: Seq[IRI]): Future[(IRI, Set[PermissionADM])] = { @@ -289,7 +332,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get administrative permissions for the knora-base:ProjectAdmin group */ administrativePermissionsOnProjectAdminGroup: Set[PermissionADM] <- administrativePermissionForGroupsGetADM( projectIri, - List(OntologyConstants.KnoraAdmin.ProjectAdmin)) + List(OntologyConstants.KnoraAdmin.ProjectAdmin) + ) _ = if (administrativePermissionsOnProjectAdminGroup.nonEmpty) { if (extendedUserGroups.contains(OntologyConstants.KnoraAdmin.ProjectAdmin)) { permissionsListBuffer += (("ProjectAdmin", administrativePermissionsOnProjectAdminGroup)) @@ -299,9 +343,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get administrative permissions for custom groups (all groups other than the built-in groups) */ administrativePermissionsOnCustomGroups: Set[PermissionADM] <- { - val customGroups = extendedUserGroups diff List(OntologyConstants.KnoraAdmin.KnownUser, - OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + val customGroups = extendedUserGroups diff List( + OntologyConstants.KnoraAdmin.KnownUser, + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) if (customGroups.nonEmpty) { administrativePermissionForGroupsGetADM(projectIri, customGroups) } else { @@ -318,7 +364,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get administrative permissions for the knora-base:ProjectMember group */ administrativePermissionsOnProjectMemberGroup: Set[PermissionADM] <- administrativePermissionForGroupsGetADM( projectIri, - List(OntologyConstants.KnoraAdmin.ProjectMember)) + List(OntologyConstants.KnoraAdmin.ProjectMember) + ) _ = if (administrativePermissionsOnProjectMemberGroup.nonEmpty) { if (permissionsListBuffer.isEmpty) { if (extendedUserGroups.contains(OntologyConstants.KnoraAdmin.ProjectMember)) { @@ -331,7 +378,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get administrative permissions for the knora-base:KnownUser group */ administrativePermissionsOnKnownUserGroup: Set[PermissionADM] <- administrativePermissionForGroupsGetADM( projectIri, - List(OntologyConstants.KnoraAdmin.KnownUser)) + List(OntologyConstants.KnoraAdmin.KnownUser) + ) _ = if (administrativePermissionsOnKnownUserGroup.nonEmpty) { if (permissionsListBuffer.isEmpty) { if (extendedUserGroups.contains(OntologyConstants.KnoraAdmin.KnownUser)) { @@ -344,13 +392,15 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re projectAdministrativePermissions: (IRI, Set[PermissionADM]) = permissionsListBuffer.length match { case 1 => log.debug( - s"userAdministrativePermissionsGetV1 - project: $projectIri, precedence: ${permissionsListBuffer.head._1}, administrativePermissions: ${permissionsListBuffer.head._2}") + s"userAdministrativePermissionsGetV1 - project: $projectIri, precedence: ${permissionsListBuffer.head._1}, administrativePermissions: ${permissionsListBuffer.head._2}" + ) (projectIri, permissionsListBuffer.head._2) case 0 => (projectIri, Set.empty[PermissionADM]) case _ => throw AssertionException( - "The permissions list buffer holding default object permissions should never be larger then 1.") + "The permissions list buffer holding default object permissions should never be larger then 1." + ) } } yield projectAdministrativePermissions @@ -372,16 +422,20 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re result } - /** ***********************************************************************/ + /** + * ********************************************************************** + */ /* ADMINISTRATIVE PERMISSIONS */ - /** ***********************************************************************/ /** - * Convenience method returning a set with combined administrative permission. Used in userAdministrativePermissionsGetV1. - * - * @param projectIri the IRI of the project. - * @param groups the list of groups for which administrative permissions are retrieved and combined. - * @return a set of [[PermissionADM]]. - */ + * ********************************************************************** + */ + /** + * Convenience method returning a set with combined administrative permission. Used in userAdministrativePermissionsGetV1. + * + * @param projectIri the IRI of the project. + * @param groups the list of groups for which administrative permissions are retrieved and combined. + * @return a set of [[PermissionADM]]. + */ private def administrativePermissionForGroupsGetADM(projectIri: IRI, groups: Seq[IRI]): Future[Set[PermissionADM]] = { /* Get administrative permissions for each group and combine them */ @@ -392,7 +446,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re groupPermissions: Future[Seq[PermissionADM]] = administrativePermissionForProjectGroupGetADM( projectIri, groupIri, - requestingUser = KnoraSystemInstances.Users.SystemUser).map { + requestingUser = KnoraSystemInstances.Users.SystemUser + ).map { case Some(ap: AdministrativePermissionADM) => ap.hasPermissions.toSeq case None => Seq.empty[PermissionADM] } @@ -421,18 +476,18 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Gets all administrative permissions defined inside a project. - * - * @param projectIRI the IRI of the project. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return a list of IRIs of [[AdministrativePermissionADM]] objects. - */ + * Gets all administrative permissions defined inside a project. + * + * @param projectIRI the IRI of the project. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return a list of IRIs of [[AdministrativePermissionADM]] objects. + */ private def administrativePermissionsForProjectGetRequestADM( - projectIRI: IRI, - requestingUser: UserADM, - apiRequestID: UUID): Future[AdministrativePermissionsForProjectGetResponseADM] = { - + projectIRI: IRI, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[AdministrativePermissionsForProjectGetResponseADM] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -440,7 +495,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re triplestore = settings.triplestoreType, projectIri = projectIRI ) - .toString()) + .toString() + ) //_ = log.debug(s"administrativePermissionsForProjectGetRequestADM - query: $sparqlQueryString") permissionsQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -451,9 +507,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionsWithProperties: Map[String, Map[String, String]] = permissionsQueryResponseRows .groupBy(_.rowMap("s")) - .map { - case (permissionIri: String, rows: Seq[VariableResultsRow]) => - (permissionIri, rows.map(row => (row.rowMap("p"), row.rowMap("o"))).toMap) + .map { case (permissionIri: String, rows: Seq[VariableResultsRow]) => + (permissionIri, rows.map(row => (row.rowMap("p"), row.rowMap("o"))).toMap) } //_ = log.debug(s"administrativePermissionsForProjectGetRequestADM - permissionsWithProperties: $permissionsWithProperties") @@ -461,8 +516,10 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case (permissionIri: IRI, propsMap: Map[String, String]) => /* parse permissions */ val hasPermissions: Set[PermissionADM] = - PermissionUtilADM.parsePermissionsWithType(propsMap.get(OntologyConstants.KnoraBase.HasPermissions), - PermissionType.AP) + PermissionUtilADM.parsePermissionsWithType( + propsMap.get(OntologyConstants.KnoraBase.HasPermissions), + PermissionType.AP + ) /* construct permission object */ AdministrativePermissionADM( @@ -470,11 +527,15 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re forProject = propsMap.getOrElse( OntologyConstants.KnoraAdmin.ForProject, throw InconsistentRepositoryDataException( - s"Administrative Permission $permissionIri has no project attached.") + s"Administrative Permission $permissionIri has no project attached." + ) + ), + forGroup = propsMap.getOrElse( + OntologyConstants.KnoraAdmin.ForGroup, + throw InconsistentRepositoryDataException( + s"Administrative Permission $permissionIri has no group attached." + ) ), - forGroup = propsMap.getOrElse(OntologyConstants.KnoraAdmin.ForGroup, - throw InconsistentRepositoryDataException( - s"Administrative Permission $permissionIri has no group attached.")), hasPermissions = hasPermissions ) }.toSeq @@ -483,20 +544,20 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re response = permissionsmessages.AdministrativePermissionsForProjectGetResponseADM(administrativePermissions) } yield response - } /** - * Gets a single administrative permission identified by it's IRI. - * - * @param administrativePermissionIri the IRI of the administrative permission. - * @param requestingUser the requesting user. - * @param apiRequestID the API request ID. - * @return a single [[AdministrativePermissionADM]] object. - */ + * Gets a single administrative permission identified by it's IRI. + * + * @param administrativePermissionIri the IRI of the administrative permission. + * @param requestingUser the requesting user. + * @param apiRequestID the API request ID. + * @return a single [[AdministrativePermissionADM]] object. + */ private def administrativePermissionForIriGetRequestADM( - administrativePermissionIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID): Future[AdministrativePermissionGetResponseADM] = { + administrativePermissionIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[AdministrativePermissionGetResponseADM] = for { administrativePermission <- permissionGetADM(administrativePermissionIri, requestingUser) result = administrativePermission match { @@ -505,20 +566,20 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case _ => throw BadRequestException(s"$administrativePermissionIri is not an administrative permission.") } } yield result - } /** - * Gets a single administrative permission identified by project and group. - * - * @param projectIri the project. - * @param groupIri the group. - * @param requestingUser the requesting user. - * @return an option containing an [[AdministrativePermissionADM]] - */ + * Gets a single administrative permission identified by project and group. + * + * @param projectIri the project. + * @param groupIri the group. + * @param requestingUser the requesting user. + * @return an option containing an [[AdministrativePermissionADM]] + */ private def administrativePermissionForProjectGroupGetADM( - projectIri: IRI, - groupIri: IRI, - requestingUser: UserADM): Future[Option[AdministrativePermissionADM]] = { + projectIri: IRI, + groupIri: IRI, + requestingUser: UserADM + ): Future[Option[AdministrativePermissionADM]] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -527,7 +588,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re projectIri = projectIri, groupIri = groupIri ) - .toString()) + .toString() + ) //_ = log.debug(s"administrativePermissionForProjectGroupGetADM - query: $sparqlQueryString") permissionQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -535,84 +597,93 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionQueryResponseRows: Seq[VariableResultsRow] = permissionQueryResponse.results.bindings - permission: Option[AdministrativePermissionADM] = if (permissionQueryResponseRows.nonEmpty) { + permission: Option[AdministrativePermissionADM] = + if (permissionQueryResponseRows.nonEmpty) { - /* check if we only got one administrative permission back */ - val apCount: Int = permissionQueryResponseRows.groupBy(_.rowMap("s")).size - if (apCount > 1) - throw InconsistentRepositoryDataException( - s"Only one administrative permission instance allowed for project: $projectIri and group: $groupIri combination, but found $apCount.") + /* check if we only got one administrative permission back */ + val apCount: Int = permissionQueryResponseRows.groupBy(_.rowMap("s")).size + if (apCount > 1) + throw InconsistentRepositoryDataException( + s"Only one administrative permission instance allowed for project: $projectIri and group: $groupIri combination, but found $apCount." + ) - /* get the iri of the retrieved permission */ - val returnedPermissionIri = permissionQueryResponse.getFirstRow.rowMap("s") + /* get the iri of the retrieved permission */ + val returnedPermissionIri = permissionQueryResponse.getFirstRow.rowMap("s") - val groupedPermissionsQueryResponse: Map[String, Seq[String]] = - permissionQueryResponseRows.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) - } - val hasPermissions = PermissionUtilADM.parsePermissionsWithType( - groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), - PermissionType.AP) - Some( - permissionsmessages.AdministrativePermissionADM(iri = returnedPermissionIri, - forProject = projectIri, - forGroup = groupIri, - hasPermissions = hasPermissions) - ) - } else { - None - } + val groupedPermissionsQueryResponse: Map[String, Seq[String]] = + permissionQueryResponseRows.groupBy(_.rowMap("p")).map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) + } + val hasPermissions = PermissionUtilADM.parsePermissionsWithType( + groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), + PermissionType.AP + ) + Some( + permissionsmessages.AdministrativePermissionADM( + iri = returnedPermissionIri, + forProject = projectIri, + forGroup = groupIri, + hasPermissions = hasPermissions + ) + ) + } else { + None + } //_ = log.debug(s"administrativePermissionForProjectGroupGetADM - projectIri: $projectIRI, groupIri: $groupIRI, administrativePermission: $permission") } yield permission - } /** - * Gets a single administrative permission identified by project and group. - * - * @param projectIri the project. - * @param groupIri the group. - * @param requestingUser the requesting user. - * @return an [[AdministrativePermissionGetResponseADM]] - */ + * Gets a single administrative permission identified by project and group. + * + * @param projectIri the project. + * @param groupIri the group. + * @param requestingUser the requesting user. + * @return an [[AdministrativePermissionGetResponseADM]] + */ private def administrativePermissionForProjectGroupGetRequestADM( - projectIri: IRI, - groupIri: IRI, - requestingUser: UserADM): Future[AdministrativePermissionGetResponseADM] = { - + projectIri: IRI, + groupIri: IRI, + requestingUser: UserADM + ): Future[AdministrativePermissionGetResponseADM] = for { - ap <- administrativePermissionForProjectGroupGetADM(projectIri, - groupIri, - requestingUser = KnoraSystemInstances.Users.SystemUser) + ap <- administrativePermissionForProjectGroupGetADM( + projectIri, + groupIri, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) result = ap match { case Some(ap) => permissionsmessages.AdministrativePermissionGetResponseADM(ap) case None => throw NotFoundException( - s"No Administrative Permission found for project: $projectIri, group: $groupIri combination") + s"No Administrative Permission found for project: $projectIri, group: $groupIri combination" + ) } } yield result - } /** - * Adds a new administrative permission (internal use). - * - * @param createRequest the administrative permission to add. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the requesting user. - * @param apiRequestID the API request ID. - * @return an optional [[AdministrativePermissionADM]] - */ + * Adds a new administrative permission (internal use). + * + * @param createRequest the administrative permission to add. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the requesting user. + * @param apiRequestID the API request ID. + * @return an optional [[AdministrativePermissionADM]] + */ private def administrativePermissionCreateRequestADM( - createRequest: CreateAdministrativePermissionAPIRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[AdministrativePermissionCreateResponseADM] = { + createRequest: CreateAdministrativePermissionAPIRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[AdministrativePermissionCreateResponseADM] = { log.debug("administrativePermissionCreateRequestADM") /** - * The actual change project task run with an IRI lock. - */ - def createPermissionTask(createRequest: CreateAdministrativePermissionAPIRequestADM, - requestingUser: UserADM): Future[AdministrativePermissionCreateResponseADM] = + * The actual change project task run with an IRI lock. + */ + def createPermissionTask( + createRequest: CreateAdministrativePermissionAPIRequestADM, + requestingUser: UserADM + ): Future[AdministrativePermissionCreateResponseADM] = for { // does the permission already exist @@ -628,7 +699,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re s"An administrative permission for project: '${createRequest.forProject}' and group: '${createRequest.forGroup}' combination already exists. " + s"This permission currently has the scope '${PermissionUtilADM .formatPermissionADMs(ap.hasPermissions, PermissionType.AP)}'. " + - s"Use its IRI ${ap.iri} to modify it, if necessary.") + s"Use its IRI ${ap.iri} to modify it, if necessary." + ) case None => () } @@ -641,28 +713,33 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // if it doesnt exist then throw an error project: ProjectADM = maybeProject.getOrElse( - throw NotFoundException(s"Project '${createRequest.forProject}' not found. Aborting request.")) + throw NotFoundException(s"Project '${createRequest.forProject}' not found. Aborting request.") + ) // get group - groupIri: IRI <- if (OntologyConstants.KnoraAdmin.BuiltInGroups.contains(createRequest.forGroup)) { - Future.successful(createRequest.forGroup) - } else { - for { - maybeGroup <- (responderManager ? GroupGetADM( - groupIri = createRequest.forGroup, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[GroupADM]] - - // if it does not exist then throw an error - group: GroupADM = maybeGroup.getOrElse( - throw NotFoundException(s"Group '${createRequest.forGroup}' not found. Aborting request.")) - } yield group.id - } + groupIri: IRI <- + if (OntologyConstants.KnoraAdmin.BuiltInGroups.contains(createRequest.forGroup)) { + Future.successful(createRequest.forGroup) + } else { + for { + maybeGroup <- (responderManager ? GroupGetADM( + groupIri = createRequest.forGroup, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[GroupADM]] + + // if it does not exist then throw an error + group: GroupADM = maybeGroup.getOrElse( + throw NotFoundException(s"Group '${createRequest.forGroup}' not found. Aborting request.") + ) + } yield group.id + } customPermissionIri: Option[SmartIri] = createRequest.id.map(iri => iri.toSmartIri) - newPermissionIri: IRI <- checkOrCreateEntityIri(customPermissionIri, - stringFormatter.makeRandomPermissionIri(project.shortcode)) + newPermissionIri: IRI <- checkOrCreateEntityIri( + customPermissionIri, + stringFormatter.makeRandomPermissionIri(project.shortcode) + ) // Create the administrative permission. createAdministrativePermissionSparqlString = org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -683,9 +760,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re .mapTo[SparqlUpdateResponse] // try to retrieve the newly created permission - maybePermission <- administrativePermissionForIriGetRequestADM(administrativePermissionIri = newPermissionIri, - requestingUser = requestingUser, - apiRequestID = apiRequestID) + maybePermission <- administrativePermissionForIriGetRequestADM( + administrativePermissionIri = newPermissionIri, + requestingUser = requestingUser, + apiRequestID = apiRequestID + ) newAdminPermission: AdministrativePermissionADM = maybePermission.administrativePermission } yield AdministrativePermissionCreateResponseADM(administrativePermission = newAdminPermission) @@ -704,22 +783,25 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /////////////////////////////////////////////////////////////////////////// /** - * Gets all permissions attached to the resource. - * - * @param resourceIri the IRI of the resource. - * @param requestingUser the requesting user. - * @return a sequence of [[PermissionADM]] - */ + * Gets all permissions attached to the resource. + * + * @param resourceIri the IRI of the resource. + * @param requestingUser the requesting user. + * @return a sequence of [[PermissionADM]] + */ private def objectAccessPermissionsForResourceGetADM( - resourceIri: IRI, - requestingUser: UserADM): Future[Option[ObjectAccessPermissionADM]] = { + resourceIri: IRI, + requestingUser: UserADM + ): Future[Option[ObjectAccessPermissionADM]] = { log.debug(s"objectAccessPermissionsForResourceGetV1 - resourceIRI: $resourceIri") for { projectIri <- getProjectOfEntity(resourceIri) // Check user's permission for the operation - _ = if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + _ = if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { throw ForbiddenException("Object access permissions can only be queried by system and project admin.") } sparqlQueryString <- Future( @@ -729,7 +811,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re resourceIri = Some(resourceIri), valueIri = None ) - .toString()) + .toString() + ) //_ = log.debug(s"objectAccessPermissionsForResourceGetV1 - query: $sparqlQueryString") permissionQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -737,42 +820,47 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionQueryResponseRows: Seq[VariableResultsRow] = permissionQueryResponse.results.bindings - permission: Option[ObjectAccessPermissionADM] = if (permissionQueryResponseRows.nonEmpty) { + permission: Option[ObjectAccessPermissionADM] = + if (permissionQueryResponseRows.nonEmpty) { - val groupedPermissionsQueryResponse: Map[String, Seq[String]] = - permissionQueryResponseRows.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) - } - val hasPermissions: Set[PermissionADM] = PermissionUtilADM.parsePermissionsWithType( - groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), - PermissionType.OAP) - Some( - ObjectAccessPermissionADM(forResource = Some(resourceIri), forValue = None, hasPermissions = hasPermissions) - ) - } else { - None - } + val groupedPermissionsQueryResponse: Map[String, Seq[String]] = + permissionQueryResponseRows.groupBy(_.rowMap("p")).map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) + } + val hasPermissions: Set[PermissionADM] = PermissionUtilADM.parsePermissionsWithType( + groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), + PermissionType.OAP + ) + Some( + ObjectAccessPermissionADM(forResource = Some(resourceIri), forValue = None, hasPermissions = hasPermissions) + ) + } else { + None + } _ = log.debug(s"objectAccessPermissionsForResourceGetV1 - permission: $permission") } yield permission } /** - * Gets all permissions attached to the value. - * - * @param valueIri the IRI of the value. - * @param requestingUser the requesting user. - * @return a sequence of [[PermissionADM]] - */ + * Gets all permissions attached to the value. + * + * @param valueIri the IRI of the value. + * @param requestingUser the requesting user. + * @return a sequence of [[PermissionADM]] + */ private def objectAccessPermissionsForValueGetADM( - valueIri: IRI, - requestingUser: UserADM): Future[Option[ObjectAccessPermissionADM]] = { + valueIri: IRI, + requestingUser: UserADM + ): Future[Option[ObjectAccessPermissionADM]] = { log.debug(s"objectAccessPermissionsForValueGetV1 - valueIRI: $valueIri") for { projectIri <- getProjectOfEntity(valueIri) // Check user's permission for the operation - _ = if (!requestingUser.isSystemAdmin - && !requestingUser.permissions.isProjectAdmin(projectIri) - && !requestingUser.isSystemUser) { + _ = if ( + !requestingUser.isSystemAdmin + && !requestingUser.permissions.isProjectAdmin(projectIri) + && !requestingUser.isSystemUser + ) { throw ForbiddenException("Object access permissions can only be queried by system and project admin.") } sparqlQueryString <- Future( @@ -782,7 +870,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re resourceIri = None, valueIri = Some(valueIri) ) - .toString()) + .toString() + ) //_ = log.debug(s"objectAccessPermissionsForValueGetV1 - query: $sparqlQueryString") permissionQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -790,21 +879,23 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionQueryResponseRows: Seq[VariableResultsRow] = permissionQueryResponse.results.bindings - permission: Option[ObjectAccessPermissionADM] = if (permissionQueryResponseRows.nonEmpty) { + permission: Option[ObjectAccessPermissionADM] = + if (permissionQueryResponseRows.nonEmpty) { - val groupedPermissionsQueryResponse: Map[String, Seq[String]] = - permissionQueryResponseRows.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) - } - val hasPermissions: Set[PermissionADM] = PermissionUtilADM.parsePermissionsWithType( - groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), - PermissionType.OAP) - Some( - ObjectAccessPermissionADM(forResource = None, forValue = Some(valueIri), hasPermissions = hasPermissions) - ) - } else { - None - } + val groupedPermissionsQueryResponse: Map[String, Seq[String]] = + permissionQueryResponseRows.groupBy(_.rowMap("p")).map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) + } + val hasPermissions: Set[PermissionADM] = PermissionUtilADM.parsePermissionsWithType( + groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), + PermissionType.OAP + ) + Some( + ObjectAccessPermissionADM(forResource = None, forValue = Some(valueIri), hasPermissions = hasPermissions) + ) + } else { + None + } _ = log.debug(s"objectAccessPermissionsForValueGetV1 - permission: $permission") } yield permission } @@ -814,17 +905,18 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /////////////////////////////////////////////////////////////////////////// /** - * Gets all IRI's of all default object access permissions defined inside a project. - * - * @param projectIri the IRI of the project. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return a list of IRIs of [[DefaultObjectAccessPermissionADM]] objects. - */ + * Gets all IRI's of all default object access permissions defined inside a project. + * + * @param projectIri the IRI of the project. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return a list of IRIs of [[DefaultObjectAccessPermissionADM]] objects. + */ private def defaultObjectAccessPermissionsForProjectGetRequestADM( - projectIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID): Future[DefaultObjectAccessPermissionsForProjectGetResponseADM] = { + projectIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[DefaultObjectAccessPermissionsForProjectGetResponseADM] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -832,7 +924,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re triplestore = settings.triplestoreType, projectIri = projectIri ) - .toString()) + .toString() + ) //_ = log.debug(s"defaultObjectAccessPermissionsForProjectGetRequestADM - query: $sparqlQueryString") permissionsQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -843,9 +936,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionsWithProperties: Map[String, Map[String, String]] = permissionsQueryResponseRows .groupBy(_.rowMap("s")) - .map { - case (permissionIri: String, rows: Seq[VariableResultsRow]) => - (permissionIri, rows.map(row => (row.rowMap("p"), row.rowMap("o"))).toMap) + .map { case (permissionIri: String, rows: Seq[VariableResultsRow]) => + (permissionIri, rows.map(row => (row.rowMap("p"), row.rowMap("o"))).toMap) } //_ = log.debug(s"defaultObjectAccessPermissionsForProjectGetRequestADM - permissionsWithProperties: $permissionsWithProperties") @@ -853,15 +945,18 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case (permissionIri: IRI, propsMap: Map[String, String]) => /* parse permissions */ val hasPermissions: Set[PermissionADM] = - PermissionUtilADM.parsePermissionsWithType(propsMap.get(OntologyConstants.KnoraBase.HasPermissions), - PermissionType.OAP) + PermissionUtilADM.parsePermissionsWithType( + propsMap.get(OntologyConstants.KnoraBase.HasPermissions), + PermissionType.OAP + ) /* construct permission object */ DefaultObjectAccessPermissionADM( iri = permissionIri, forProject = propsMap.getOrElse( OntologyConstants.KnoraAdmin.ForProject, - throw InconsistentRepositoryDataException(s"Permission $permissionIri has no project.")), + throw InconsistentRepositoryDataException(s"Permission $permissionIri has no project.") + ), forGroup = propsMap.get(OntologyConstants.KnoraAdmin.ForGroup), forResourceClass = propsMap.get(OntologyConstants.KnoraAdmin.ForResourceClass), forProperty = propsMap.get(OntologyConstants.KnoraAdmin.ForProperty), @@ -874,21 +969,19 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } yield response - } - /** - * Gets a single default object access permission identified by its IRI. - * - * @param permissionIri the IRI of the default object access permission. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return a single [[DefaultObjectAccessPermissionADM]] object. - */ + * Gets a single default object access permission identified by its IRI. + * + * @param permissionIri the IRI of the default object access permission. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return a single [[DefaultObjectAccessPermissionADM]] object. + */ private def defaultObjectAccessPermissionForIriGetRequestADM( - permissionIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID): Future[DefaultObjectAccessPermissionGetResponseADM] = { - + permissionIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[DefaultObjectAccessPermissionGetResponseADM] = for { defaultObjectAccessPermission <- permissionGetADM(permissionIri, requestingUser) result = defaultObjectAccessPermission match { @@ -897,31 +990,33 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case _ => throw BadRequestException(s"$permissionIri is not a default object access permission.") } } yield result - } /** - * Gets a single default object access permission identified by project and either: - * - group - * - resource class - * - resource class and property - * - property - * - * @param projectIri the project's IRI. - * @param groupIri the group's IRI. - * @param resourceClassIri the resource's class IRI - * @param propertyIri the property's IRI. - * @return an optional [[DefaultObjectAccessPermissionADM]] - */ + * Gets a single default object access permission identified by project and either: + * - group + * - resource class + * - resource class and property + * - property + * + * @param projectIri the project's IRI. + * @param groupIri the group's IRI. + * @param resourceClassIri the resource's class IRI + * @param propertyIri the property's IRI. + * @return an optional [[DefaultObjectAccessPermissionADM]] + */ private def defaultObjectAccessPermissionGetADM( - projectIri: IRI, - groupIri: Option[IRI], - resourceClassIri: Option[IRI], - propertyIri: Option[IRI]): Future[Option[DefaultObjectAccessPermissionADM]] = { - - val key = PermissionsMessagesUtilADM.getDefaultObjectAccessPermissionADMKey(projectIri, - groupIri, - resourceClassIri, - propertyIri) + projectIri: IRI, + groupIri: Option[IRI], + resourceClassIri: Option[IRI], + propertyIri: Option[IRI] + ): Future[Option[DefaultObjectAccessPermissionADM]] = { + + val key = PermissionsMessagesUtilADM.getDefaultObjectAccessPermissionADMKey( + projectIri, + groupIri, + resourceClassIri, + propertyIri + ) val permissionFromCache = CacheUtil.get[DefaultObjectAccessPermissionADM](PermissionsMessagesUtilADM.PermissionsCacheName, key) @@ -946,7 +1041,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re maybeResourceClassIri = resourceClassIri, maybePropertyIri = propertyIri ) - .toString()) + .toString() + ) // _ = logger.debug(s"defaultObjectAccessPermissionGetADM - query: $sparqlQueryString") @@ -955,46 +1051,53 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionQueryResponseRows: Seq[VariableResultsRow] = permissionQueryResponse.results.bindings - permission: Option[DefaultObjectAccessPermissionADM] = if (permissionQueryResponseRows.nonEmpty) { - - /* check if we only got one default object access permission back */ - val doapCount: Int = permissionQueryResponseRows.groupBy(_.rowMap("s")).size - if (doapCount > 1) - throw InconsistentRepositoryDataException( - s"Only one default object permission instance allowed for project: $projectIri and combination of group: $groupIri, resourceClass: $resourceClassIri, property: $propertyIri combination, but found: $doapCount.") - - /* get the iri of the retrieved permission */ - val permissionIri = permissionQueryResponse.getFirstRow.rowMap("s") - - val groupedPermissionsQueryResponse: Map[String, Seq[String]] = - permissionQueryResponseRows.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) - } - val hasPermissions: Set[PermissionADM] = PermissionUtilADM.parsePermissionsWithType( - groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), - PermissionType.OAP) - val doap: DefaultObjectAccessPermissionADM = DefaultObjectAccessPermissionADM( - iri = permissionIri, - forProject = groupedPermissionsQueryResponse - .getOrElse(OntologyConstants.KnoraAdmin.ForProject, - throw InconsistentRepositoryDataException(s"Permission has no project.")) - .head, - forGroup = groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForGroup).map(_.head), - forResourceClass = - groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForResourceClass).map(_.head), - forProperty = groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForProperty).map(_.head), - hasPermissions = hasPermissions - ) + permission: Option[DefaultObjectAccessPermissionADM] = + if (permissionQueryResponseRows.nonEmpty) { + + /* check if we only got one default object access permission back */ + val doapCount: Int = permissionQueryResponseRows.groupBy(_.rowMap("s")).size + if (doapCount > 1) + throw InconsistentRepositoryDataException( + s"Only one default object permission instance allowed for project: $projectIri and combination of group: $groupIri, resourceClass: $resourceClassIri, property: $propertyIri combination, but found: $doapCount." + ) + + /* get the iri of the retrieved permission */ + val permissionIri = permissionQueryResponse.getFirstRow.rowMap("s") + + val groupedPermissionsQueryResponse: Map[String, Seq[String]] = + permissionQueryResponseRows.groupBy(_.rowMap("p")).map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) + } + val hasPermissions: Set[PermissionADM] = PermissionUtilADM.parsePermissionsWithType( + groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), + PermissionType.OAP + ) + val doap: DefaultObjectAccessPermissionADM = DefaultObjectAccessPermissionADM( + iri = permissionIri, + forProject = groupedPermissionsQueryResponse + .getOrElse( + OntologyConstants.KnoraAdmin.ForProject, + throw InconsistentRepositoryDataException(s"Permission has no project.") + ) + .head, + forGroup = groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForGroup).map(_.head), + forResourceClass = + groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForResourceClass).map(_.head), + forProperty = + groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForProperty).map(_.head), + hasPermissions = hasPermissions + ) - // write permission to cache - PermissionsMessagesUtilADM.writeDefaultObjectAccessPermissionADMToCache(doap) + // write permission to cache + PermissionsMessagesUtilADM.writeDefaultObjectAccessPermissionADMToCache(doap) - Some(doap) - } else { - None - } + Some(doap) + } else { + None + } _ = logger.debug( - s"defaultObjectAccessPermissionGetADM - p: $projectIri, g: $groupIri, r: $resourceClassIri, p: $propertyIri, permission: $permission") + s"defaultObjectAccessPermissionGetADM - p: $projectIri, g: $groupIri, r: $resourceClassIri, p: $propertyIri, permission: $permission" + ) } yield permission } @@ -1003,25 +1106,25 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Gets a single default object access permission identified by project and either group / resource class / property. - * In the case of properties, an additional check is performed against the 'SystemProject', as some 'knora-base' - * properties can carry default object access permissions. Note that default access permissions defined for a system - * property inside the 'SystemProject' can be overridden by defining them for its own project. - * - * @param projectIri - * @param groupIri - * @param resourceClassIri - * @param propertyIri - * @param requestingUser - * @return a [[DefaultObjectAccessPermissionGetResponseADM]] - */ + * Gets a single default object access permission identified by project and either group / resource class / property. + * In the case of properties, an additional check is performed against the 'SystemProject', as some 'knora-base' + * properties can carry default object access permissions. Note that default access permissions defined for a system + * property inside the 'SystemProject' can be overridden by defining them for its own project. + * + * @param projectIri + * @param groupIri + * @param resourceClassIri + * @param propertyIri + * @param requestingUser + * @return a [[DefaultObjectAccessPermissionGetResponseADM]] + */ private def defaultObjectAccessPermissionGetRequestADM( - projectIri: IRI, - groupIri: Option[IRI], - resourceClassIri: Option[IRI], - propertyIri: Option[IRI], - requestingUser: UserADM): Future[DefaultObjectAccessPermissionGetResponseADM] = { - + projectIri: IRI, + groupIri: Option[IRI], + resourceClassIri: Option[IRI], + propertyIri: Option[IRI], + requestingUser: UserADM + ): Future[DefaultObjectAccessPermissionGetResponseADM] = defaultObjectAccessPermissionGetADM(projectIri, groupIri, resourceClassIri, propertyIri) .mapTo[Option[DefaultObjectAccessPermissionADM]] .flatMap { @@ -1035,34 +1138,39 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case Some(systemDoap) => DefaultObjectAccessPermissionGetResponseADM(systemDoap) case None => throw NotFoundException( - s"No Default Object Access Permission found for project: $projectIri, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination") + s"No Default Object Access Permission found for project: $projectIri, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination" + ) } } else { throw NotFoundException( - s"No Default Object Access Permission found for project: $projectIri, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination") + s"No Default Object Access Permission found for project: $projectIri, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination" + ) } } - } /** - * Convenience method returning a set with combined max default object access permissions. - * - * @param projectIri the IRI of the project. - * @param groups the list of groups for which default object access permissions are retrieved and combined. - * @return a set of [[PermissionADM]]. - */ - private def defaultObjectAccessPermissionsForGroupsGetADM(projectIri: IRI, - groups: Seq[IRI]): Future[Set[PermissionADM]] = { + * Convenience method returning a set with combined max default object access permissions. + * + * @param projectIri the IRI of the project. + * @param groups the list of groups for which default object access permissions are retrieved and combined. + * @return a set of [[PermissionADM]]. + */ + private def defaultObjectAccessPermissionsForGroupsGetADM( + projectIri: IRI, + groups: Seq[IRI] + ): Future[Set[PermissionADM]] = { /* Get default object access permissions for each group and combine them */ val gpf: Seq[Future[Seq[PermissionADM]]] = for { groupIri <- groups //_ = log.debug(s"userDefaultObjectAccessPermissionsGetV1 - projectIri: $projectIri, groupIri: $groupIri") - groupPermissions: Future[Seq[PermissionADM]] = defaultObjectAccessPermissionGetADM(projectIri = projectIri, - groupIri = Some(groupIri), - resourceClassIri = None, - propertyIri = None).map { + groupPermissions: Future[Seq[PermissionADM]] = defaultObjectAccessPermissionGetADM( + projectIri = projectIri, + groupIri = Some(groupIri), + resourceClassIri = None, + propertyIri = None + ).map { case Some(doap: DefaultObjectAccessPermissionADM) => doap.hasPermissions.toSeq case None => Seq.empty[PermissionADM] } @@ -1086,100 +1194,106 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re result: Set[PermissionADM] = PermissionUtilADM.removeDuplicatePermissions(combined) _ = logger.debug( - s"defaultObjectAccessPermissionsForGroupsGetADM - INPUT [ projectIri: $projectIri, groups: $groups ], RESULT [ $result ]") + s"defaultObjectAccessPermissionsForGroupsGetADM - INPUT [ projectIri: $projectIri, groups: $groups ], RESULT [ $result ]" + ) } yield result result } /** - * Convenience method returning a set with default object access permissions defined on a resource class. - * - * @param projectIri the IRI of the project. - * @param resourceClassIri the resource's class IRI - * @return a set of [[PermissionADM]]. - */ + * Convenience method returning a set with default object access permissions defined on a resource class. + * + * @param projectIri the IRI of the project. + * @param resourceClassIri the resource's class IRI + * @return a set of [[PermissionADM]]. + */ private def defaultObjectAccessPermissionsForResourceClassGetADM( - projectIri: IRI, - resourceClassIri: IRI): Future[Set[PermissionADM]] = { + projectIri: IRI, + resourceClassIri: IRI + ): Future[Set[PermissionADM]] = for { defaultPermissionsOption: Option[DefaultObjectAccessPermissionADM] <- defaultObjectAccessPermissionGetADM( projectIri = projectIri, groupIri = None, resourceClassIri = Some(resourceClassIri), - propertyIri = None) + propertyIri = None + ) defaultPermissions: Set[PermissionADM] = defaultPermissionsOption match { case Some(doap) => doap.hasPermissions case None => Set.empty[PermissionADM] } } yield defaultPermissions - } /** - * Convenience method returning a set with default object access permissions defined on a resource class / property combination. - * - * @param projectIri the IRI of the project. - * @param resourceClassIri the resource's class IRI - * @param propertyIri the property's IRI. - * @return a set of [[PermissionADM]]. - */ + * Convenience method returning a set with default object access permissions defined on a resource class / property combination. + * + * @param projectIri the IRI of the project. + * @param resourceClassIri the resource's class IRI + * @param propertyIri the property's IRI. + * @return a set of [[PermissionADM]]. + */ private def defaultObjectAccessPermissionsForResourceClassPropertyGetADM( - projectIri: IRI, - resourceClassIri: IRI, - propertyIri: IRI): Future[Set[PermissionADM]] = { + projectIri: IRI, + resourceClassIri: IRI, + propertyIri: IRI + ): Future[Set[PermissionADM]] = for { defaultPermissionsOption: Option[DefaultObjectAccessPermissionADM] <- defaultObjectAccessPermissionGetADM( projectIri = projectIri, groupIri = None, resourceClassIri = Some(resourceClassIri), - propertyIri = Some(propertyIri)) + propertyIri = Some(propertyIri) + ) defaultPermissions: Set[PermissionADM] = defaultPermissionsOption match { case Some(doap) => doap.hasPermissions case None => Set.empty[PermissionADM] } } yield defaultPermissions - } /** - * Convenience method returning a set with default object access permissions defined on a property. - * - * @param projectIri the IRI of the project. - * @param propertyIri the property's IRI. - * @return a set of [[PermissionADM]]. - */ - private def defaultObjectAccessPermissionsForPropertyGetADM(projectIri: IRI, - propertyIri: IRI): Future[Set[PermissionADM]] = { + * Convenience method returning a set with default object access permissions defined on a property. + * + * @param projectIri the IRI of the project. + * @param propertyIri the property's IRI. + * @return a set of [[PermissionADM]]. + */ + private def defaultObjectAccessPermissionsForPropertyGetADM( + projectIri: IRI, + propertyIri: IRI + ): Future[Set[PermissionADM]] = for { defaultPermissionsOption: Option[DefaultObjectAccessPermissionADM] <- defaultObjectAccessPermissionGetADM( projectIri = projectIri, groupIri = None, resourceClassIri = None, - propertyIri = Some(propertyIri)) + propertyIri = Some(propertyIri) + ) defaultPermissions: Set[PermissionADM] = defaultPermissionsOption match { case Some(doap) => doap.hasPermissions case None => Set.empty[PermissionADM] } } yield defaultPermissions - } /** - * Returns a string containing default object permissions statements ready for usage during creation of a new resource. - * The permissions include any default object access permissions defined for the resource class and on any groups the - * user is member of. - * - * @param projectIri the IRI of the project. - * @param resourceClassIri the IRI of the resource class for which the default object access permissions are requested. - * @param propertyIri the IRI of the property for which the default object access permissions are requested. - * @param targetUser the user for which the permissions need to be calculated. - * @param requestingUser the user initiating the request. - * @return an optional string with object access permission statements - */ + * Returns a string containing default object permissions statements ready for usage during creation of a new resource. + * The permissions include any default object access permissions defined for the resource class and on any groups the + * user is member of. + * + * @param projectIri the IRI of the project. + * @param resourceClassIri the IRI of the resource class for which the default object access permissions are requested. + * @param propertyIri the IRI of the property for which the default object access permissions are requested. + * @param targetUser the user for which the permissions need to be calculated. + * @param requestingUser the user initiating the request. + * @return an optional string with object access permission statements + */ private def defaultObjectAccessPermissionsStringForEntityGetADM( - projectIri: IRI, - resourceClassIri: IRI, - propertyIri: Option[IRI], - entityType: String, - targetUser: UserADM, - requestingUser: UserADM): Future[DefaultObjectAccessPermissionsStringResponseADM] = { + projectIri: IRI, + resourceClassIri: IRI, + propertyIri: Option[IRI], + entityType: String, + targetUser: UserADM, + requestingUser: UserADM + ): Future[DefaultObjectAccessPermissionsStringResponseADM] = { // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetADM (input) - projectIRI: $projectIri, resourceClassIRI: $resourceClassIri, propertyIRI: $propertyIri, entityType: $entityType, targetUser: $targetUser") for { /* Get the groups the user is member of. */ @@ -1190,11 +1304,12 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /* Explicitly add 'SystemAdmin' and 'KnownUser' groups. */ - extendedUserGroups: List[IRI] = if (targetUser.permissions.isSystemAdmin) { - OntologyConstants.KnoraAdmin.SystemAdmin :: OntologyConstants.KnoraAdmin.KnownUser :: userGroups.toList - } else { - OntologyConstants.KnoraAdmin.KnownUser :: userGroups.toList - } + extendedUserGroups: List[IRI] = + if (targetUser.permissions.isSystemAdmin) { + OntologyConstants.KnoraAdmin.SystemAdmin :: OntologyConstants.KnoraAdmin.KnownUser :: userGroups.toList + } else { + OntologyConstants.KnoraAdmin.KnownUser :: userGroups.toList + } // _ = log.debug("defaultObjectAccessPermissionsStringForEntityGetV1 - extendedUserGroups: {}", extendedUserGroups) @@ -1210,10 +1325,14 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get the default object access permissions for the knora-base:ProjectAdmin group */ defaultPermissionsOnProjectAdminGroup: Set[PermissionADM] <- defaultObjectAccessPermissionsForGroupsGetADM( projectIri, - List(OntologyConstants.KnoraAdmin.ProjectAdmin)) + List(OntologyConstants.KnoraAdmin.ProjectAdmin) + ) _ = if (defaultPermissionsOnProjectAdminGroup.nonEmpty) { - if (extendedUserGroups.contains(OntologyConstants.KnoraAdmin.ProjectAdmin) || extendedUserGroups.contains( - OntologyConstants.KnoraAdmin.SystemAdmin)) { + if ( + extendedUserGroups.contains(OntologyConstants.KnoraAdmin.ProjectAdmin) || extendedUserGroups.contains( + OntologyConstants.KnoraAdmin.SystemAdmin + ) + ) { permissionsListBuffer += (("ProjectAdmin", defaultPermissionsOnProjectAdminGroup)) // log.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectAdminGroup: $defaultPermissionsOnProjectAdminGroup") } @@ -1263,8 +1382,10 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get the default object access permissions defined on the resource class for the current project */ defaultPermissionsOnProjectResourceClass: Set[PermissionADM] <- { if (entityType == ResourceEntityType && permissionsListBuffer.isEmpty) { - defaultObjectAccessPermissionsForResourceClassGetADM(projectIri = projectIri, - resourceClassIri = resourceClassIri) + defaultObjectAccessPermissionsForResourceClassGetADM( + projectIri = projectIri, + resourceClassIri = resourceClassIri + ) } else { Future(Set.empty[PermissionADM]) } @@ -1278,8 +1399,10 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re defaultPermissionsOnSystemResourceClass: Set[PermissionADM] <- { if (entityType == ResourceEntityType && permissionsListBuffer.isEmpty) { val systemProject = OntologyConstants.KnoraAdmin.SystemProject - defaultObjectAccessPermissionsForResourceClassGetADM(projectIri = systemProject, - resourceClassIri = resourceClassIri) + defaultObjectAccessPermissionsForResourceClassGetADM( + projectIri = systemProject, + resourceClassIri = resourceClassIri + ) } else { Future(Set.empty[PermissionADM]) } @@ -1297,7 +1420,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re if (entityType == PropertyEntityType && permissionsListBuffer.isEmpty) { defaultObjectAccessPermissionsForPropertyGetADM( projectIri = projectIri, - propertyIri = propertyIri.getOrElse(throw BadRequestException("PropertyIri needs to be supplied."))) + propertyIri = propertyIri.getOrElse(throw BadRequestException("PropertyIri needs to be supplied.")) + ) } else { Future(Set.empty[PermissionADM]) } @@ -1313,7 +1437,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re val systemProject = OntologyConstants.KnoraAdmin.SystemProject defaultObjectAccessPermissionsForPropertyGetADM( projectIri = systemProject, - propertyIri = propertyIri.getOrElse(throw BadRequestException("PropertyIri needs to be supplied."))) + propertyIri = propertyIri.getOrElse(throw BadRequestException("PropertyIri needs to be supplied.")) + ) } else { Future(Set.empty[PermissionADM]) } @@ -1362,8 +1487,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } } _ = if (defaultPermissionsOnProjectMemberGroup.nonEmpty) { - if (extendedUserGroups.contains(OntologyConstants.KnoraAdmin.ProjectMember) || extendedUserGroups.contains( - OntologyConstants.KnoraAdmin.SystemAdmin)) { + if ( + extendedUserGroups.contains(OntologyConstants.KnoraAdmin.ProjectMember) || extendedUserGroups.contains( + OntologyConstants.KnoraAdmin.SystemAdmin + ) + ) { permissionsListBuffer += (("ProjectMember", defaultPermissionsOnProjectMemberGroup)) } // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectMemberGroup: $defaultPermissionsOnProjectMemberGroup") @@ -1391,36 +1519,42 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // FALLBACK PERMISSION IF NONE COULD BE FOUND /////////////////////// /* Set 'CR knora-base:Creator' as the fallback permission */ - _ = if (permissionsListBuffer.isEmpty) { - val defaultFallbackPermission = Set(PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator)) - permissionsListBuffer += (("Fallback", defaultFallbackPermission)) - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultFallbackPermission: $defaultFallbackPermission") - } else { - FastFuture.successful(Set.empty[PermissionADM]) - } + _ = + if (permissionsListBuffer.isEmpty) { + val defaultFallbackPermission = Set( + PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator) + ) + permissionsListBuffer += (("Fallback", defaultFallbackPermission)) + // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultFallbackPermission: $defaultFallbackPermission") + } else { + FastFuture.successful(Set.empty[PermissionADM]) + } /* Create permissions string */ result = permissionsListBuffer.length match { case 1 => PermissionUtilADM.formatPermissionADMs(permissionsListBuffer.head._2, PermissionType.OAP) case _ => throw AssertionException( - "The permissions list buffer holding default object permissions should never be larger then 1.") + "The permissions list buffer holding default object permissions should never be larger then 1." + ) } _ = logger.debug( - s"defaultObjectAccessPermissionsStringForEntityGetADM (result) - project: $projectIri, precedence: ${permissionsListBuffer.head._1}, defaultObjectAccessPermissions: $result") + s"defaultObjectAccessPermissionsStringForEntityGetADM (result) - project: $projectIri, precedence: ${permissionsListBuffer.head._1}, defaultObjectAccessPermissions: $result" + ) } yield permissionsmessages.DefaultObjectAccessPermissionsStringResponseADM(result) } /** - * Gets a single permission identified by its IRI. - * - * @param permissionIri the IRI of the permission. - * @param requestingUser the [[UserADM]] of the requesting user. - * @return a single [[DefaultObjectAccessPermissionADM]] object. - */ - private def permissionByIriGetRequestADM(permissionIri: IRI, - requestingUser: UserADM): Future[PermissionGetResponseADM] = { - + * Gets a single permission identified by its IRI. + * + * @param permissionIri the IRI of the permission. + * @param requestingUser the [[UserADM]] of the requesting user. + * @return a single [[DefaultObjectAccessPermissionADM]] object. + */ + private def permissionByIriGetRequestADM( + permissionIri: IRI, + requestingUser: UserADM + ): Future[PermissionGetResponseADM] = for { permission <- permissionGetADM(permissionIri, requestingUser) result = permission match { @@ -1432,24 +1566,28 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re throw BadRequestException(s"$permissionIri is not a default object access or an administrative permission.") } } yield result - } private def defaultObjectAccessPermissionCreateRequestADM( - createRequest: CreateDefaultObjectAccessPermissionAPIRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[DefaultObjectAccessPermissionCreateResponseADM] = { + createRequest: CreateDefaultObjectAccessPermissionAPIRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[DefaultObjectAccessPermissionCreateResponseADM] = { /** - * The actual change project task run with an IRI lock. - */ - def createPermissionTask(createRequest: CreateDefaultObjectAccessPermissionAPIRequestADM, - requestingUser: UserADM): Future[DefaultObjectAccessPermissionCreateResponseADM] = + * The actual change project task run with an IRI lock. + */ + def createPermissionTask( + createRequest: CreateDefaultObjectAccessPermissionAPIRequestADM, + requestingUser: UserADM + ): Future[DefaultObjectAccessPermissionCreateResponseADM] = for { - checkResult <- defaultObjectAccessPermissionGetADM(createRequest.forProject, - createRequest.forGroup, - createRequest.forResourceClass, - createRequest.forProperty) + checkResult <- defaultObjectAccessPermissionGetADM( + createRequest.forProject, + createRequest.forGroup, + createRequest.forResourceClass, + createRequest.forProperty + ) _ = checkResult match { case Some(doap: DefaultObjectAccessPermissionADM) => @@ -1469,7 +1607,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re errorMessage + "combination already exists. " + s"This permission currently has the scope '${PermissionUtilADM .formatPermissionADMs(doap.hasPermissions, PermissionType.OAP)}'. " + - s"Use its IRI ${doap.iri} to modify it, if necessary.") + s"Use its IRI ${doap.iri} to modify it, if necessary." + ) case None => () } @@ -1482,32 +1621,36 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // if it doesnt exist then throw an error project: ProjectADM = maybeProject.getOrElse( - throw NotFoundException(s"Project '${createRequest.forProject}' not found. Aborting request.")) + throw NotFoundException(s"Project '${createRequest.forProject}' not found. Aborting request.") + ) customPermissionIri: Option[SmartIri] = createRequest.id.map(iri => iri.toSmartIri) - newPermissionIri: IRI <- checkOrCreateEntityIri(customPermissionIri, - stringFormatter.makeRandomPermissionIri(project.shortcode)) + newPermissionIri: IRI <- checkOrCreateEntityIri( + customPermissionIri, + stringFormatter.makeRandomPermissionIri(project.shortcode) + ) // verify group, if any given. // Is a group given that is not a built-in one? - maybeGroupIri: Option[IRI] <- if (createRequest.forGroup.exists( - !OntologyConstants.KnoraAdmin.BuiltInGroups.contains(_))) { - // Yes. Check if it is a known group. - for { - maybeGroup <- (responderManager ? GroupGetADM( - groupIri = createRequest.forGroup.get, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[GroupADM]] - - group: GroupADM = maybeGroup.getOrElse( - throw NotFoundException(s"Group '${createRequest.forGroup}' not found. Aborting request.")) - } yield Some(group.id) - } else { - // No, return given group as it is. That means: - // If given group is a built-in one, no verification is necessary, return it as it is. - // In case no group IRI is given, returns None. - Future.successful(createRequest.forGroup) - } + maybeGroupIri: Option[IRI] <- + if (createRequest.forGroup.exists(!OntologyConstants.KnoraAdmin.BuiltInGroups.contains(_))) { + // Yes. Check if it is a known group. + for { + maybeGroup <- (responderManager ? GroupGetADM( + groupIri = createRequest.forGroup.get, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[GroupADM]] + + group: GroupADM = maybeGroup.getOrElse( + throw NotFoundException(s"Group '${createRequest.forGroup}' not found. Aborting request.") + ) + } yield Some(group.id) + } else { + // No, return given group as it is. That means: + // If given group is a built-in one, no verification is necessary, return it as it is. + // In case no group IRI is given, returns None. + Future.successful(createRequest.forGroup) + } // Create the default object access permission. createNewDefaultObjectAccessPermissionSparqlString = org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -1528,17 +1671,22 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re .mapTo[SparqlUpdateResponse] // try to retrieve the newly created permission - maybePermission <- defaultObjectAccessPermissionGetADM(createRequest.forProject, - createRequest.forGroup, - createRequest.forResourceClass, - createRequest.forProperty) + maybePermission <- defaultObjectAccessPermissionGetADM( + createRequest.forProject, + createRequest.forGroup, + createRequest.forResourceClass, + createRequest.forProperty + ) newDefaultObjectAcessPermission: DefaultObjectAccessPermissionADM = maybePermission.getOrElse( throw BadRequestException( - "Requested default object access permission could not be created, report this as a possible bug.")) + "Requested default object access permission could not be created, report this as a possible bug." + ) + ) - } yield - DefaultObjectAccessPermissionCreateResponseADM(defaultObjectAccessPermission = newDefaultObjectAcessPermission) + } yield DefaultObjectAccessPermissionCreateResponseADM(defaultObjectAccessPermission = + newDefaultObjectAcessPermission + ) for { // run the task with an IRI lock @@ -1551,19 +1699,20 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Gets all permissions defined inside a project. - * - * @param projectIRI the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return a list of of [[PermissionInfoADM]] objects. - */ - private def permissionsForProjectGetRequestADM(projectIRI: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[PermissionsForProjectGetResponseADM] = { - + * Gets all permissions defined inside a project. + * + * @param projectIRI the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return a list of of [[PermissionInfoADM]] objects. + */ + private def permissionsForProjectGetRequestADM( + projectIRI: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[PermissionsForProjectGetResponseADM] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -1571,7 +1720,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re triplestore = settings.triplestoreType, projectIri = projectIRI ) - .toString()) + .toString() + ) permissionsQueryResponse <- (storeManager ? SparqlConstructRequest( sparql = sparqlQueryString, @@ -1581,36 +1731,38 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* extract response statements */ permissionsQueryResponseStatements: Map[IRI, Seq[(IRI, String)]] = permissionsQueryResponse.statements - permissionsInfo: Set[PermissionInfoADM] = if (permissionsQueryResponseStatements.isEmpty) { - throw NotFoundException(s"No permission could be found for $projectIRI.") - } else { - permissionsQueryResponseStatements.map { statement => - val permissionIri = statement._1 - val (_, permissionType) = statement._2.filter(_._1 == OntologyConstants.Rdf.Type).head - PermissionInfoADM(iri = permissionIri, permissionType = permissionType) - }.toSet - } + permissionsInfo: Set[PermissionInfoADM] = + if (permissionsQueryResponseStatements.isEmpty) { + throw NotFoundException(s"No permission could be found for $projectIRI.") + } else { + permissionsQueryResponseStatements.map { statement => + val permissionIri = statement._1 + val (_, permissionType) = statement._2.filter(_._1 == OntologyConstants.Rdf.Type).head + PermissionInfoADM(iri = permissionIri, permissionType = permissionType) + }.toSet + } /* construct response object */ response = permissionsmessages.PermissionsForProjectGetResponseADM(permissionsInfo) } yield response - } /** - * Update a permission's group - * - * @param permissionIri the IRI of the permission. - * @param changePermissionGroupRequest the request to change group. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return [[PermissionGetResponseADM]]. - * @throws UpdateNotPerformedException if something has gone wrong. - */ - private def permissionGroupChangeRequestADM(permissionIri: IRI, - changePermissionGroupRequest: ChangePermissionGroupApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID): Future[PermissionGetResponseADM] = { + * Update a permission's group + * + * @param permissionIri the IRI of the permission. + * @param changePermissionGroupRequest the request to change group. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return [[PermissionGetResponseADM]]. + * @throws UpdateNotPerformedException if something has gone wrong. + */ + private def permissionGroupChangeRequestADM( + permissionIri: IRI, + changePermissionGroupRequest: ChangePermissionGroupApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[PermissionGetResponseADM] = { /* verify that the permission group is updated */ def verifyPermissionGroupUpdate: Future[PermissionItemADM] = for { @@ -1622,25 +1774,30 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case ap: AdministrativePermissionADM => if (ap.forGroup != changePermissionGroupRequest.forGroup) throw UpdateNotPerformedException( - s"The group of permission $permissionIri was not updated. Please report this as a bug.") + s"The group of permission $permissionIri was not updated. Please report this as a bug." + ) case doap: DefaultObjectAccessPermissionADM => if (doap.forGroup.get != changePermissionGroupRequest.forGroup) { throw UpdateNotPerformedException( - s"The group of permission $permissionIri was not updated. Please report this as a bug.") + s"The group of permission $permissionIri was not updated. Please report this as a bug." + ) } else { if (doap.forProperty.isDefined || doap.forResourceClass.isDefined) throw UpdateNotPerformedException( - s"The $permissionIri is not correctly updated. Please report this as a bug.") + s"The $permissionIri is not correctly updated. Please report this as a bug." + ) } } } yield updatedPermission /** - * The actual task run with an IRI lock. - */ - def permissionGroupChangeTask(permissioniri: IRI, - changePermissionGroupRequest: ChangePermissionGroupApiRequestADM, - requestingUser: UserADM): Future[PermissionGetResponseADM] = + * The actual task run with an IRI lock. + */ + def permissionGroupChangeTask( + permissioniri: IRI, + changePermissionGroupRequest: ChangePermissionGroupApiRequestADM, + requestingUser: UserADM + ): Future[PermissionGetResponseADM] = for { // get permission permission <- permissionGetADM(permissioniri, requestingUser) @@ -1658,9 +1815,9 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // if a doap permission has a group defined, it cannot have either resourceClass or property _ <- updatePermission(permissionIri = doap.iri, maybeGroup = Some(changePermissionGroupRequest.forGroup)) updatedPermission <- verifyPermissionGroupUpdate - } yield - DefaultObjectAccessPermissionGetResponseADM( - updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM]) + } yield DefaultObjectAccessPermissionGetResponseADM( + updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM] + ) } } yield response @@ -1675,20 +1832,21 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Update a permission's set of hasPermissions. - * - * @param permissionIri the IRI of the permission. - * @param changeHasPermissionsRequest the request to change hasPermissions. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return [[PermissionGetResponseADM]]. - * @throws UpdateNotPerformedException if something has gone wrong. - */ + * Update a permission's set of hasPermissions. + * + * @param permissionIri the IRI of the permission. + * @param changeHasPermissionsRequest the request to change hasPermissions. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return [[PermissionGetResponseADM]]. + * @throws UpdateNotPerformedException if something has gone wrong. + */ private def permissionHasPermissionsChangeRequestADM( - permissionIri: IRI, - changeHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID): Future[PermissionGetResponseADM] = { + permissionIri: IRI, + changeHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[PermissionGetResponseADM] = { /*Verify that hasPermissions is updated successfully*/ def verifyUpdateOfHasPermissions(expectedPermissions: Set[PermissionADM]): Future[PermissionItemADM] = @@ -1700,22 +1858,26 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case ap: AdministrativePermissionADM => if (!ap.hasPermissions.equals(expectedPermissions)) throw UpdateNotPerformedException( - s"The hasPermissions set of permission $permissionIri was not updated. Please report this as a bug.") + s"The hasPermissions set of permission $permissionIri was not updated. Please report this as a bug." + ) case doap: DefaultObjectAccessPermissionADM => if (!doap.hasPermissions.equals(expectedPermissions)) { throw UpdateNotPerformedException( - s"The hasPermissions set of permission $permissionIri was not updated. Please report this as a bug.") + s"The hasPermissions set of permission $permissionIri was not updated. Please report this as a bug." + ) } case _ => None } } yield updatedPermission /** - * The actual task run with an IRI lock. - */ - def permissionHasPermissionsChangeTask(permissionIri: IRI, - changeHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, - requestingUser: UserADM): Future[PermissionGetResponseADM] = + * The actual task run with an IRI lock. + */ + def permissionHasPermissionsChangeTask( + permissionIri: IRI, + changeHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, + requestingUser: UserADM + ): Future[PermissionGetResponseADM] = for { // get permission permission <- permissionGetADM(permissionIri, requestingUser) @@ -1727,7 +1889,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re PermissionsMessagesUtilADM.verifyHasPermissionsAP(changeHasPermissionsRequest.hasPermissions) for { formattedPermissions <- Future( - PermissionUtilADM.formatPermissionADMs(verifiedPermissions, PermissionType.AP)) + PermissionUtilADM.formatPermissionADMs(verifiedPermissions, PermissionType.AP) + ) _ <- updatePermission(permissionIri = ap.iri, maybeHasPermissions = Some(formattedPermissions)) updatedPermission <- verifyUpdateOfHasPermissions(verifiedPermissions) } yield AdministrativePermissionGetResponseADM(updatedPermission.asInstanceOf[AdministrativePermissionADM]) @@ -1737,15 +1900,17 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re PermissionsMessagesUtilADM.verifyHasPermissionsDOAP(changeHasPermissionsRequest.hasPermissions) for { formattedPermissions <- Future( - PermissionUtilADM.formatPermissionADMs(verifiedPermissions, PermissionType.OAP)) + PermissionUtilADM.formatPermissionADMs(verifiedPermissions, PermissionType.OAP) + ) _ <- updatePermission(permissionIri = doap.iri, maybeHasPermissions = Some(formattedPermissions)) updatedPermission <- verifyUpdateOfHasPermissions(verifiedPermissions) - } yield - DefaultObjectAccessPermissionGetResponseADM( - updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM]) + } yield DefaultObjectAccessPermissionGetResponseADM( + updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM] + ) case _ => throw UpdateNotPerformedException( - s"Permission $permissionIri was not updated. Please report this as a bug.") + s"Permission $permissionIri was not updated. Please report this as a bug." + ) } } yield response @@ -1760,20 +1925,21 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Update a doap permission's resource class. - * - * @param permissionIri the IRI of the permission. - * @param changePermissionResourceClass the request to change hasPermissions. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return [[PermissionGetResponseADM]]. - * @throws UpdateNotPerformedException if something has gone wrong. - */ + * Update a doap permission's resource class. + * + * @param permissionIri the IRI of the permission. + * @param changePermissionResourceClass the request to change hasPermissions. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return [[PermissionGetResponseADM]]. + * @throws UpdateNotPerformedException if something has gone wrong. + */ private def permissionResourceClassChangeRequestADM( - permissionIri: IRI, - changePermissionResourceClass: ChangePermissionResourceClassApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID): Future[PermissionGetResponseADM] = { + permissionIri: IRI, + changePermissionResourceClass: ChangePermissionResourceClassApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[PermissionGetResponseADM] = { /*Verify that resource class of doap is updated successfully*/ def verifyUpdateOfResourceClass: Future[PermissionItemADM] = @@ -1785,24 +1951,29 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case doap: DefaultObjectAccessPermissionADM => if (doap.forResourceClass.get != changePermissionResourceClass.forResourceClass) throw UpdateNotPerformedException( - s"The resource class of ${doap.iri} was not updated. Please report this as a bug.") + s"The resource class of ${doap.iri} was not updated. Please report this as a bug." + ) if (doap.forGroup.isDefined) throw UpdateNotPerformedException( - s"The $permissionIri is not correctly updated. Please report this as a bug.") + s"The $permissionIri is not correctly updated. Please report this as a bug." + ) case _ => throw UpdateNotPerformedException( - s"Incorrect permission type returned for $permissionIri. Please report this as a bug.") + s"Incorrect permission type returned for $permissionIri. Please report this as a bug." + ) } } yield updatedPermission /** - * The actual task run with an IRI lock. - */ - def permissionResourceClassChangeTask(permissionIri: IRI, - changePermissionResourceClass: ChangePermissionResourceClassApiRequestADM, - requestingUser: UserADM): Future[PermissionGetResponseADM] = + * The actual task run with an IRI lock. + */ + def permissionResourceClassChangeTask( + permissionIri: IRI, + changePermissionResourceClass: ChangePermissionResourceClassApiRequestADM, + requestingUser: UserADM + ): Future[PermissionGetResponseADM] = for { // get permission permission <- permissionGetADM(permissionIri, requestingUser) @@ -1812,19 +1983,23 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // Yes. throw BadRequestException( s"Permission ${ap.iri} is of type administrative permission. " + - s"Only a default object access permission defined for a resource class can be updated.") + s"Only a default object access permission defined for a resource class can be updated." + ) case doap: DefaultObjectAccessPermissionADM => //No. It is a default object access permission. for { - _ <- updatePermission(permissionIri = doap.iri, - maybeResourceClass = Some(changePermissionResourceClass.forResourceClass)) + _ <- updatePermission( + permissionIri = doap.iri, + maybeResourceClass = Some(changePermissionResourceClass.forResourceClass) + ) updatedPermission <- verifyUpdateOfResourceClass - } yield - DefaultObjectAccessPermissionGetResponseADM( - updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM]) + } yield DefaultObjectAccessPermissionGetResponseADM( + updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM] + ) case _ => throw UpdateNotPerformedException( - s"Permission $permissionIri was not updated. Please report this as a bug.") + s"Permission $permissionIri was not updated. Please report this as a bug." + ) } } yield response @@ -1839,19 +2014,21 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Update a doap permission's property. - * - * @param permissionIri the IRI of the permission. - * @param changePermissionPropertyRequest the request to change hasPermissions. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return [[PermissionGetResponseADM]]. - * @throws UpdateNotPerformedException if something has gone wrong. - */ - private def permissionPropertyChangeRequestADM(permissionIri: IRI, - changePermissionPropertyRequest: ChangePermissionPropertyApiRequestADM, - requestingUser: UserADM, - apiRequestID: UUID): Future[PermissionGetResponseADM] = { + * Update a doap permission's property. + * + * @param permissionIri the IRI of the permission. + * @param changePermissionPropertyRequest the request to change hasPermissions. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return [[PermissionGetResponseADM]]. + * @throws UpdateNotPerformedException if something has gone wrong. + */ + private def permissionPropertyChangeRequestADM( + permissionIri: IRI, + changePermissionPropertyRequest: ChangePermissionPropertyApiRequestADM, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[PermissionGetResponseADM] = { /*Verify that property of doap is updated successfully*/ def verifyUpdateOfProperty: Future[PermissionItemADM] = @@ -1863,24 +2040,29 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case doap: DefaultObjectAccessPermissionADM => if (doap.forProperty.get != changePermissionPropertyRequest.forProperty) throw UpdateNotPerformedException( - s"The property of ${doap.iri} was not updated. Please report this as a bug.") + s"The property of ${doap.iri} was not updated. Please report this as a bug." + ) if (doap.forGroup.isDefined) throw UpdateNotPerformedException( - s"The $permissionIri is not correctly updated. Please report this as a bug.") + s"The $permissionIri is not correctly updated. Please report this as a bug." + ) case _ => throw UpdateNotPerformedException( - s"Incorrect permission type returned for $permissionIri. Please report this as a bug.") + s"Incorrect permission type returned for $permissionIri. Please report this as a bug." + ) } } yield updatedPermission /** - * The actual task run with an IRI lock. - */ - def permissionPropertyChangeTask(permissionIri: IRI, - changePermissionPropertyRequest: ChangePermissionPropertyApiRequestADM, - requestingUser: UserADM): Future[PermissionGetResponseADM] = + * The actual task run with an IRI lock. + */ + def permissionPropertyChangeTask( + permissionIri: IRI, + changePermissionPropertyRequest: ChangePermissionPropertyApiRequestADM, + requestingUser: UserADM + ): Future[PermissionGetResponseADM] = for { // get permission permission <- permissionGetADM(permissionIri, requestingUser) @@ -1890,19 +2072,23 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // Yes. throw BadRequestException( s"Permission ${ap.iri} is of type administrative permission. " + - s"Only a default object access permission defined for a property can be updated.") + s"Only a default object access permission defined for a property can be updated." + ) case doap: DefaultObjectAccessPermissionADM => //No. It is a default object access permission. for { - _ <- updatePermission(permissionIri = doap.iri, - maybeProperty = Some(changePermissionPropertyRequest.forProperty)) + _ <- updatePermission( + permissionIri = doap.iri, + maybeProperty = Some(changePermissionPropertyRequest.forProperty) + ) updatedPermission <- verifyUpdateOfProperty - } yield - DefaultObjectAccessPermissionGetResponseADM( - updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM]) + } yield DefaultObjectAccessPermissionGetResponseADM( + updatedPermission.asInstanceOf[DefaultObjectAccessPermissionADM] + ) case _ => throw UpdateNotPerformedException( - s"Permission $permissionIri was not updated. Please report this as a bug.") + s"Permission $permissionIri was not updated. Please report this as a bug." + ) } } yield response @@ -1917,27 +2103,31 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } /** - * Delete a permission. - * - * @param permissionIri the IRI of the permission. - * @param requestingUser the [[UserADM]] of the requesting user. - * @param apiRequestID the API request ID. - * @return [[PermissionDeleteResponseADM]]. - * @throws UpdateNotPerformedException if permission was in use and could not be deleted or something else went wrong. - * @throws NotFoundException if no permission is found for the given IRI. - */ - private def permissionDeleteRequestADM(permissionIri: IRI, - requestingUser: UserADM, - apiRequestID: UUID): Future[PermissionDeleteResponseADM] = { + * Delete a permission. + * + * @param permissionIri the IRI of the permission. + * @param requestingUser the [[UserADM]] of the requesting user. + * @param apiRequestID the API request ID. + * @return [[PermissionDeleteResponseADM]]. + * @throws UpdateNotPerformedException if permission was in use and could not be deleted or something else went wrong. + * @throws NotFoundException if no permission is found for the given IRI. + */ + private def permissionDeleteRequestADM( + permissionIri: IRI, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[PermissionDeleteResponseADM] = { def permissionDeleteTask(): Future[PermissionDeleteResponseADM] = for { // check that there is a permission with a given IRI _ <- permissionGetADM(permissionIri, requestingUser) // Is permission in use? - _ <- isPermissionUsed(permissionIri = permissionIri, - errorFun = throw UpdateNotPerformedException( - s"Permission $permissionIri cannot be deleted, because it is in use.")) + _ <- isPermissionUsed( + permissionIri = permissionIri, + errorFun = + throw UpdateNotPerformedException(s"Permission $permissionIri cannot be deleted, because it is in use.") + ) _ <- deletePermission(permissionIri) @@ -1953,32 +2143,36 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } yield taskResult } - /** **************/ + /** + * ************* + */ /*Helper Methods*/ - /** *************/ /** - * Checks that requesting user has right for the permission operation - * - * @param requestingUser the [[UserADM]] of the requesting user. - * @param projectIri the IRI of the project the permission is attached to. - * @param permissionIri the IRI of the permission. - * @throw ForbiddenException if the user is not a project or system admin - */ - def verifyUsersRightForOperation(requestingUser: UserADM, projectIri: IRI, permissionIri: IRI): Unit = { + * ************ + */ + /** + * Checks that requesting user has right for the permission operation + * + * @param requestingUser the [[UserADM]] of the requesting user. + * @param projectIri the IRI of the project the permission is attached to. + * @param permissionIri the IRI of the permission. + * @throw ForbiddenException if the user is not a project or system admin + */ + def verifyUsersRightForOperation(requestingUser: UserADM, projectIri: IRI, permissionIri: IRI): Unit = if (!requestingUser.isSystemAdmin && !requestingUser.permissions.isProjectAdmin(projectIri)) { throw ForbiddenException( - s"Permission $permissionIri can only be queried/updated/deleted by system or project admin.") + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) } - } /** - * Get a permission. - * - * @param permissionIri the IRI of the permission. - * @param requestingUser the [[UserADM]] of the requesting user. - * @return [[PermissionItemADM]]. - */ + * Get a permission. + * + * @param permissionIri the IRI of the permission. + * @param requestingUser the [[UserADM]] of the requesting user. + * @return [[PermissionItemADM]]. + */ private def permissionGetADM(permissionIri: IRI, requestingUser: UserADM): Future[PermissionItemADM] = for { // SPARQL query statement to get permission by IRI. @@ -1997,8 +2191,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re groupedPermissionsQueryResponse: Map[String, Seq[String]] = permissionQueryResponseRows .groupBy(_.rowMap("p")) - .map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) + .map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) } /* check if we have found something */ @@ -2006,14 +2200,18 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re throw NotFoundException(s"Permission with given IRI: $permissionIri not found.") projectIri = groupedPermissionsQueryResponse - .getOrElse(OntologyConstants.KnoraAdmin.ForProject, - throw InconsistentRepositoryDataException(s"Permission $permissionIri has no project attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.ForProject, + throw InconsistentRepositoryDataException(s"Permission $permissionIri has no project attached") + ) .head // Before returning the permission check that the requesting user has permission to see it - _ = verifyUsersRightForOperation(requestingUser = requestingUser, - projectIri = projectIri, - permissionIri = permissionIri) + _ = verifyUsersRightForOperation( + requestingUser = requestingUser, + projectIri = projectIri, + permissionIri = permissionIri + ) permissionType: Option[String] = groupedPermissionsQueryResponse .getOrElse(OntologyConstants.Rdf.Type, throw InconsistentRepositoryDataException(s"RDF type is not returned.")) @@ -2022,48 +2220,58 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case Some(OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission) => val hasPermissions = PermissionUtilADM.parsePermissionsWithType( groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), - PermissionType.OAP) + PermissionType.OAP + ) val forGroup = groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForGroup).map(_.head) val forResourceClass = groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForResourceClass).map(_.head) val forProperty = groupedPermissionsQueryResponse.get(OntologyConstants.KnoraAdmin.ForProperty).map(_.head) - DefaultObjectAccessPermissionADM(iri = permissionIri, - forProject = projectIri, - forGroup = forGroup, - forResourceClass = forResourceClass, - forProperty = forProperty, - hasPermissions = hasPermissions) + DefaultObjectAccessPermissionADM( + iri = permissionIri, + forProject = projectIri, + forGroup = forGroup, + forResourceClass = forResourceClass, + forProperty = forProperty, + hasPermissions = hasPermissions + ) case Some(OntologyConstants.KnoraAdmin.AdministrativePermission) => val forGroup = groupedPermissionsQueryResponse - .getOrElse(OntologyConstants.KnoraAdmin.ForGroup, - throw InconsistentRepositoryDataException(s"Permission $permissionIri has no group attached")) + .getOrElse( + OntologyConstants.KnoraAdmin.ForGroup, + throw InconsistentRepositoryDataException(s"Permission $permissionIri has no group attached") + ) .head val hasPermissions = PermissionUtilADM.parsePermissionsWithType( groupedPermissionsQueryResponse.get(OntologyConstants.KnoraBase.HasPermissions).map(_.head), - PermissionType.AP) + PermissionType.AP + ) - AdministrativePermissionADM(iri = permissionIri, - forProject = projectIri, - forGroup = forGroup, - hasPermissions = hasPermissions) + AdministrativePermissionADM( + iri = permissionIri, + forProject = projectIri, + forGroup = forGroup, + hasPermissions = hasPermissions + ) case _ => throw BadRequestException(s"Invalid permission type returned, please report this as a bug.") } } yield permission /** - * Update an existing permission with a given parameter. - * - * @param permissionIri the IRI of the permission. - * @param maybeGroup the IRI of the new group. - * @param maybeHasPermissions the new set of permissions formatted according to permission type as string. - * @param maybeResourceClass the new resource class IRI of a doap permission. - * @param maybeProperty the new property IRI of a doap permission. - */ - def updatePermission(permissionIri: IRI, - maybeGroup: Option[IRI] = None, - maybeHasPermissions: Option[String] = None, - maybeResourceClass: Option[IRI] = None, - maybeProperty: Option[IRI] = None): Future[Unit] = + * Update an existing permission with a given parameter. + * + * @param permissionIri the IRI of the permission. + * @param maybeGroup the IRI of the new group. + * @param maybeHasPermissions the new set of permissions formatted according to permission type as string. + * @param maybeResourceClass the new resource class IRI of a doap permission. + * @param maybeProperty the new property IRI of a doap permission. + */ + def updatePermission( + permissionIri: IRI, + maybeGroup: Option[IRI] = None, + maybeHasPermissions: Option[String] = None, + maybeResourceClass: Option[IRI] = None, + maybeProperty: Option[IRI] = None + ): Future[Unit] = for { // Generate SPARQL for changing the permission. @@ -2085,10 +2293,10 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } yield () /** - * Delete an existing permission with a given IRI. - * - * @param permissionIri the IRI of the permission. - */ + * Delete an existing permission with a given IRI. + * + * @param permissionIri the IRI of the permission. + */ def deletePermission(permissionIri: IRI): Future[Unit] = for { // Generate SPARQL for erasing a permission. @@ -2114,17 +2322,18 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re _ = if (permissionStillExists) { throw UpdateNotPerformedException( - s"Permission <$permissionIri> was not erased. Please report this as a possible bug.") + s"Permission <$permissionIri> was not erased. Please report this as a possible bug." + ) } } yield () /** - * Helper method to check if a permission is in use. - * - * @param permissionIri the IRI of the permission. - * @param errorFun a function that throws an exception. It will be called if the permission is used. - * @return a [[Boolean]]. - */ + * Helper method to check if a permission is in use. + * + * @param permissionIri the IRI of the permission. + * @param errorFun a function that throws an exception. It will be called if the permission is used. + * @return a [[Boolean]]. + */ protected def isPermissionUsed(permissionIri: IRI, errorFun: => Nothing): Future[Unit] = for { isPermissionUsedSparql <- Future( @@ -2152,16 +2361,19 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re triplestore = settings.triplestoreType, entityIri = entityIri ) - .toString()) + .toString() + ) response <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] rows: Seq[VariableResultsRow] = response.results.bindings - projectIri = if (rows.size == 0) { - throw BadRequestException( - s"<$entityIri> is not attached to a project, please verify that IRI is of a knora entity.") - } else { - val projectOption = rows.head.rowMap.get("projectIri") - projectOption.getOrElse(throw BadRequestException(s"No Project found for the given <$entityIri>")) - } + projectIri = + if (rows.size == 0) { + throw BadRequestException( + s"<$entityIri> is not attached to a project, please verify that IRI is of a knora entity." + ) + } else { + val projectOption = rows.head.rowMap.get("projectIri") + projectOption.getOrElse(throw BadRequestException(s"No Project found for the given <$entityIri>")) + } } yield projectIri diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala index df80f95896..9458dd34bd 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala @@ -61,8 +61,8 @@ import scala.concurrent.Future import scala.util.{Failure, Success, Try} /** - * Returns information about Knora projects. - */ + * Returns information about Knora projects. + */ class ProjectsResponderADM(responderData: ResponderData) extends Responder(responderData) with InstrumentationSupport { // Global lock IRI used for project creation and update @@ -72,8 +72,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo private val PERMISSIONS_DATA_GRAPH = "http://www.knora.org/data/permissions" /** - * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: ProjectsResponderRequestADM) = msg match { case ProjectsGetADM(featureFactoryConfig, requestingUser) => projectsGetADM(featureFactoryConfig, requestingUser) case ProjectsGetRequestADM(featureFactoryConfig, requestingUser) => @@ -96,31 +96,36 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo projectRestrictedViewSettingsGetRequestADM(identifier, featureFactoryConfig, requestingUser) case ProjectCreateRequestADM(createRequest, featureFactoryConfig, requestingUser, apiRequestID) => projectCreateRequestADM(createRequest, featureFactoryConfig, requestingUser, apiRequestID) - case ProjectChangeRequestADM(projectIri, - changeProjectRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) => - changeBasicInformationRequestADM(projectIri, - changeProjectRequest, - featureFactoryConfig, - requestingUser, - apiRequestID) + case ProjectChangeRequestADM( + projectIri, + changeProjectRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) => + changeBasicInformationRequestADM( + projectIri, + changeProjectRequest, + featureFactoryConfig, + requestingUser, + apiRequestID + ) case ProjectDataGetRequestADM(projectIdentifier, featureFactoryConfig, requestingUser) => projectDataGetRequestADM(projectIdentifier, featureFactoryConfig, requestingUser) case other => handleUnexpectedMessage(other, log, this.getClass.getName) } /** - * Gets all the projects and returns them as a sequence containing [[ProjectADM]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return all the projects as a sequence containing [[ProjectADM]]. - */ - private def projectsGetADM(featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Seq[ProjectADM]] = { - + * Gets all the projects and returns them as a sequence containing [[ProjectADM]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return all the projects as a sequence containing [[ProjectADM]]. + */ + private def projectsGetADM( + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Seq[ProjectADM]] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -130,7 +135,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo maybeShortname = None, maybeShortcode = None ) - .toString()) + .toString() + ) // _ = log.debug(s"getProjectsResponseV1 - query: $sparqlQueryString") projectsResponse <- (storeManager ? SparqlExtendedConstructRequest( @@ -142,8 +148,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo statements: List[(SubjectV2, Map[SmartIri, Seq[LiteralV2]])] = projectsResponse.statements.toList // _ = log.debug(s"projectsGetADM - statements: $statements") - projectIris = statements.map { - case (projectIri: SubjectV2, _) => projectIri.toString + projectIris = statements.map { case (projectIri: SubjectV2, _) => + projectIri.toString }.toSet ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects(projectIris, requestingUser) @@ -155,123 +161,125 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } } yield projects.sorted - } /** - * Given a set of project IRIs, gets the ontologies that belong to each project. - * - * @param projectIris a set of project IRIs. If empty, returns the ontologies for all projects. - * @param requestingUser the requesting user. - * @return a map of project IRIs to sequences of ontology IRIs. - */ - private def getOntologiesForProjects(projectIris: Set[IRI], requestingUser: UserADM): Future[Map[IRI, Seq[IRI]]] = { + * Given a set of project IRIs, gets the ontologies that belong to each project. + * + * @param projectIris a set of project IRIs. If empty, returns the ontologies for all projects. + * @param requestingUser the requesting user. + * @return a map of project IRIs to sequences of ontology IRIs. + */ + private def getOntologiesForProjects(projectIris: Set[IRI], requestingUser: UserADM): Future[Map[IRI, Seq[IRI]]] = for { ontologyMetadataResponse: ReadOntologyMetadataV2 <- (responderManager ? OntologyMetadataGetByProjectRequestV2( projectIris = projectIris.map(_.toSmartIri), - requestingUser = requestingUser)).mapTo[ReadOntologyMetadataV2] - } yield - ontologyMetadataResponse.ontologies - .map { ontology => - val ontologyIri: IRI = ontology.ontologyIri.toString - val projectIri: IRI = ontology.projectIri - .getOrElse(throw InconsistentRepositoryDataException(s"Ontology $ontologyIri has no project")) - .toString - projectIri -> ontologyIri - } - .groupBy(_._1) - .map { - case (projectIri, projectIriAndOntologies: Set[(IRI, IRI)]) => - projectIri -> projectIriAndOntologies.map(_._2).toSeq - } - } + requestingUser = requestingUser + )).mapTo[ReadOntologyMetadataV2] + } yield ontologyMetadataResponse.ontologies.map { ontology => + val ontologyIri: IRI = ontology.ontologyIri.toString + val projectIri: IRI = ontology.projectIri + .getOrElse(throw InconsistentRepositoryDataException(s"Ontology $ontologyIri has no project")) + .toString + projectIri -> ontologyIri + } + .groupBy(_._1) + .map { case (projectIri, projectIriAndOntologies: Set[(IRI, IRI)]) => + projectIri -> projectIriAndOntologies.map(_._2).toSeq + } /** - * Gets all the projects and returns them as a [[ProjectsResponseV1]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user that is making the request. - * @return all the projects as a [[ProjectsResponseV1]]. - * @throws NotFoundException if no projects are found. - */ - private def projectsGetRequestADM(featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectsGetResponseADM] = { - + * Gets all the projects and returns them as a [[ProjectsResponseV1]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user that is making the request. + * @return all the projects as a [[ProjectsResponseV1]]. + * @throws NotFoundException if no projects are found. + */ + private def projectsGetRequestADM( + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectsGetResponseADM] = // log.debug("projectsGetRequestADM") // ToDo: What permissions should be required, if any? - for { projects <- projectsGetADM( featureFactoryConfig = featureFactoryConfig, requestingUser = requestingUser ) - result = if (projects.nonEmpty) { - ProjectsGetResponseADM( - projects = projects - ) - } else { - throw NotFoundException(s"No projects found") - } + result = + if (projects.nonEmpty) { + ProjectsGetResponseADM( + projects = projects + ) + } else { + throw NotFoundException(s"No projects found") + } } yield result - } /** - * Gets the project with the given project IRI, shortname, or shortcode and returns the information as a [[ProjectADM]]. - * - * @param identifier the IRI, shortname, or shortcode of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return information about the project as a [[ProjectInfoV1]]. - */ - private def getSingleProjectADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - skipCache: Boolean = false): Future[Option[ProjectADM]] = + * Gets the project with the given project IRI, shortname, or shortcode and returns the information as a [[ProjectADM]]. + * + * @param identifier the IRI, shortname, or shortcode of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return information about the project as a [[ProjectInfoV1]]. + */ + private def getSingleProjectADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + skipCache: Boolean = false + ): Future[Option[ProjectADM]] = tracedFuture("admin-get-project") { // log.debug("getSingleProjectADM - projectIRI: {}", projectIri) - log.debug(s"getSingleProjectADM - id: {}, requester: {}, skipCache: {}", - identifier.value, - requestingUser.username, - skipCache) + log.debug( + s"getSingleProjectADM - id: {}, requester: {}, skipCache: {}", + identifier.value, + requestingUser.username, + skipCache + ) for { - maybeProjectADM <- if (skipCache) { - // getting directly from triplestore - getProjectFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) - } else { - // getting from cache or triplestore - getProjectFromCacheOrTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) - } + maybeProjectADM <- + if (skipCache) { + // getting directly from triplestore + getProjectFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) + } else { + // getting from cache or triplestore + getProjectFromCacheOrTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) + } - _ = if (maybeProjectADM.nonEmpty) { - log.debug("getSingleProjectADM - successfully retrieved project: {}", identifier.value) - } else { - log.debug("getSingleProjectADM - could not retrieve project: {}", identifier.value) - } + _ = + if (maybeProjectADM.nonEmpty) { + log.debug("getSingleProjectADM - successfully retrieved project: {}", identifier.value) + } else { + log.debug("getSingleProjectADM - could not retrieve project: {}", identifier.value) + } } yield maybeProjectADM } /** - * Gets the project with the given project IRI, shortname, or shortcode and returns the information - * as a [[ProjectGetResponseADM]]. - * - * @param identifier the IRI, shortname, or shortcode of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return information about the project as a [[ProjectInfoResponseV1]]. - * @throws NotFoundException when no project for the given IRI can be found - */ - private def getSingleProjectADMRequest(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectGetResponseADM] = { - + * Gets the project with the given project IRI, shortname, or shortcode and returns the information + * as a [[ProjectGetResponseADM]]. + * + * @param identifier the IRI, shortname, or shortcode of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return information about the project as a [[ProjectInfoResponseV1]]. + * @throws NotFoundException when no project for the given IRI can be found + */ + private def getSingleProjectADMRequest( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectGetResponseADM] = // log.debug("getSingleProjectADMRequest - maybeIri: {}, maybeShortname: {}, maybeShortcode: {}", maybeIri, maybeShortname, maybeShortcode) - for { maybeProject: Option[ProjectADM] <- getSingleProjectADM( identifier = identifier, @@ -283,27 +291,25 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo case Some(p) => p case None => throw NotFoundException(s"Project '${identifier.value}' not found") } - } yield - ProjectGetResponseADM( - project = project - ) - } + } yield ProjectGetResponseADM( + project = project + ) /** - * Gets the members of a project with the given IRI, shortname, oder shortcode. Returns an empty list - * if none are found. - * - * @param identifier the IRI, shortname, or shortcode of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the members of a project as a [[ProjectMembersGetResponseADM]] - */ - private def projectMembersGetRequestADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectMembersGetResponseADM] = { - + * Gets the members of a project with the given IRI, shortname, oder shortcode. Returns an empty list + * if none are found. + * + * @param identifier the IRI, shortname, or shortcode of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the members of a project as a [[ProjectMembersGetResponseADM]] + */ + private def projectMembersGetRequestADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectMembersGetResponseADM] = // log.debug("projectMembersGetRequestADM - maybeIri: {}, maybeShortname: {}, maybeShortcode: {}", maybeIri, maybeShortname, maybeShortcode) - for { /* Get project and verify permissions. */ @@ -313,13 +319,18 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo requestingUser = KnoraSystemInstances.Users.SystemUser ) - _ = if (project.isEmpty) { - throw NotFoundException(s"Project '${identifier.value}' not found.") - } else { - if (!requestingUser.permissions.isSystemAdmin && !requestingUser.permissions.isProjectAdmin(project.get.id) && !requestingUser.isSystemUser) { - throw ForbiddenException("SystemAdmin or ProjectAdmin permissions are required.") + _ = + if (project.isEmpty) { + throw NotFoundException(s"Project '${identifier.value}' not found.") + } else { + if ( + !requestingUser.permissions.isSystemAdmin && !requestingUser.permissions.isProjectAdmin( + project.get.id + ) && !requestingUser.isSystemUser + ) { + throw ForbiddenException("SystemAdmin or ProjectAdmin permissions are required.") + } } - } sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -329,7 +340,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo maybeShortname = identifier.toShortnameOption, maybeShortcode = identifier.toShortcodeOption ) - .toString()) + .toString() + ) //_ = log.debug(s"projectMembersGetRequestADM - query: $sparqlQueryString") projectMembersResponse <- (storeManager ? SparqlExtendedConstructRequest( @@ -342,11 +354,12 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // _ = log.debug(s"projectMembersGetRequestADM - statements: {}", MessageUtil.toSource(statements)) // get project member IRI from results rows - userIris: Seq[IRI] = if (statements.nonEmpty) { - statements.map(_._1.toString) - } else { - Seq.empty[IRI] - } + userIris: Seq[IRI] = + if (statements.nonEmpty) { + statements.map(_._1.toString) + } else { + Seq.empty[IRI] + } maybeUserFutures: Seq[Future[Option[UserADM]]] = userIris.map { userIri => (responderManager ? UserGetADM( @@ -362,23 +375,22 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // _ = log.debug(s"projectMembersGetRequestADM - users: {}", users) } yield ProjectMembersGetResponseADM(members = users) - } /** - * Gets the admin members of a project with the given IRI, shortname, or shortcode. Returns an empty list - * if none are found - * - * @param identifier the IRI, shortname, or shortcode of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the members of a project as a [[ProjectMembersGetResponseADM]] - */ - private def projectAdminMembersGetRequestADM(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectAdminMembersGetResponseADM] = { - + * Gets the admin members of a project with the given IRI, shortname, or shortcode. Returns an empty list + * if none are found + * + * @param identifier the IRI, shortname, or shortcode of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the members of a project as a [[ProjectMembersGetResponseADM]] + */ + private def projectAdminMembersGetRequestADM( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectAdminMembersGetResponseADM] = // log.debug("projectAdminMembersGetRequestADM - maybeIri: {}, maybeShortname: {}, maybeShortcode: {}", maybeIri, maybeShortname, maybeShortcode) - for { /* Get project and verify permissions. */ project <- getSingleProjectADM( @@ -387,13 +399,14 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo requestingUser = KnoraSystemInstances.Users.SystemUser ) - _ = if (project.isEmpty) { - throw NotFoundException(s"Project '${identifier.value}' not found.") - } else { - if (!requestingUser.permissions.isSystemAdmin && !requestingUser.permissions.isProjectAdmin(project.get.id)) { - throw ForbiddenException("SystemAdmin or ProjectAdmin permissions are required.") + _ = + if (project.isEmpty) { + throw NotFoundException(s"Project '${identifier.value}' not found.") + } else { + if (!requestingUser.permissions.isSystemAdmin && !requestingUser.permissions.isProjectAdmin(project.get.id)) { + throw ForbiddenException("SystemAdmin or ProjectAdmin permissions are required.") + } } - } sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -403,7 +416,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo maybeShortname = identifier.toShortnameOption, maybeShortcode = identifier.toShortcodeOption ) - .toString()) + .toString() + ) //_ = log.debug(s"projectAdminMembersByIRIGetRequestV1 - query: $sparqlQueryString") projectAdminMembersResponse <- (storeManager ? SparqlExtendedConstructRequest( @@ -415,11 +429,12 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo statements = projectAdminMembersResponse.statements.toList // get project member IRI from results rows - userIris: Seq[IRI] = if (statements.nonEmpty) { - statements.map(_._1.toString) - } else { - Seq.empty[IRI] - } + userIris: Seq[IRI] = + if (statements.nonEmpty) { + statements.map(_._1.toString) + } else { + Seq.empty[IRI] + } maybeUserFutures: Seq[Future[Option[UserADM]]] = userIris.map { userIri => (responderManager ? UserGetADM( @@ -435,18 +450,18 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo //_ = log.debug(s"projectMembersGetRequestADM - users: $users") } yield ProjectAdminMembersGetResponseADM(members = users) - } /** - * Gets all unique keywords for all projects and returns them. Returns an empty list if none are found. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return all keywords for all projects as [[ProjectsKeywordsGetResponseADM]] - */ - private def projectsKeywordsGetRequestADM(featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectsKeywordsGetResponseADM] = { - + * Gets all unique keywords for all projects and returns them. Returns an empty list if none are found. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return all keywords for all projects as [[ProjectsKeywordsGetResponseADM]] + */ + private def projectsKeywordsGetRequestADM( + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectsKeywordsGetResponseADM] = for { projects <- projectsGetADM( featureFactoryConfig = featureFactoryConfig, @@ -456,20 +471,20 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo keywords: Seq[String] = projects.flatMap(_.keywords).distinct.sorted } yield ProjectsKeywordsGetResponseADM(keywords = keywords) - } /** - * Gets all keywords for a single project and returns them. Returns an empty list if none are found. - * - * @param projectIri the IRI of the project. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return keywords for a projects as [[ProjectKeywordsGetResponseADM]] - */ - private def projectKeywordsGetRequestADM(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectKeywordsGetResponseADM] = { - + * Gets all keywords for a single project and returns them. Returns an empty list if none are found. + * + * @param projectIri the IRI of the project. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return keywords for a projects as [[ProjectKeywordsGetResponseADM]] + */ + private def projectKeywordsGetRequestADM( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectKeywordsGetResponseADM] = for { maybeProject <- getSingleProjectADM( identifier = ProjectIdentifierADM(maybeIri = Some(projectIri)), @@ -483,18 +498,19 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } } yield ProjectKeywordsGetResponseADM(keywords = keywords) - } - private def projectDataGetRequestADM(projectIdentifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectDataGetResponseADM] = { + private def projectDataGetRequestADM( + projectIdentifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectDataGetResponseADM] = { /** - * Represents a named graph to be saved to a TriG file. - * - * @param graphIri the IRI of the named graph. - * @param tempDir the directory in which the file is to be saved. - */ + * Represents a named graph to be saved to a TriG file. + * + * @param graphIri the IRI of the named graph. + * @param tempDir the directory in which the file is to be saved. + */ case class NamedGraphTrigFile(graphIri: IRI, tempDir: Path) { lazy val dataFile: Path = { val filename = graphIri.replaceAll(":", "_").replaceAll("/", "_").replaceAll("""\.""", "_") + @@ -505,10 +521,10 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * An [[RdfStreamProcessor]] for combining several named graphs into one. - * - * @param formattingStreamProcessor an [[RdfStreamProcessor]] for writing the combined result. - */ + * An [[RdfStreamProcessor]] for combining several named graphs into one. + * + * @param formattingStreamProcessor an [[RdfStreamProcessor]] for writing the combined result. + */ class CombiningRdfProcessor(formattingStreamProcessor: RdfStreamProcessor) extends RdfStreamProcessor { private var startedStatements = false @@ -518,12 +534,11 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // Ignore this, since it will be done after the last file is written. override def finish(): Unit = {} - override def processNamespace(prefix: IRI, namespace: IRI): Unit = { + override def processNamespace(prefix: IRI, namespace: IRI): Unit = // Only accept namespaces from the first graph, to prevent conflicts. if (!startedStatements) { formattingStreamProcessor.processNamespace(prefix, namespace) } - } override def processStatement(statement: Statement): Unit = { startedStatements = true @@ -532,11 +547,11 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Combines several TriG files into one. - * - * @param namedGraphTrigFiles the TriG files to combine. - * @param resultFile the output file. - */ + * Combines several TriG files into one. + * + * @param namedGraphTrigFiles the TriG files to combine. + * @param resultFile the output file. + */ def combineGraphs(namedGraphTrigFiles: Seq[NamedGraphTrigFile], resultFile: Path): Unit = { val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) var maybeBufferedFileOutputStream: Option[BufferedOutputStream] = None @@ -590,12 +605,14 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo ) project: ProjectADM = maybeProject.getOrElse( - throw NotFoundException(s"Project '${projectIdentifier.value}' not found.")) + throw NotFoundException(s"Project '${projectIdentifier.value}' not found.") + ) // Check that the user has permission to download the data. _ = if (!(requestingUser.permissions.isSystemAdmin || requestingUser.permissions.isProjectAdmin(project.id))) { throw ForbiddenException( - s"You are logged in as ${requestingUser.username}, but only a system administrator or project administrator can request a project's data") + s"You are logged in as ${requestingUser.username}, but only a system administrator or project administrator can request a project's data" + ) } // Make a temporary directory for the downloaded data. @@ -607,10 +624,11 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo projectDataNamedGraph: IRI = stringFormatter.projectDataNamedGraphV2(project) graphsToDownload: Seq[IRI] = project.ontologies :+ projectDataNamedGraph projectSpecificNamedGraphTrigFiles: Seq[NamedGraphTrigFile] = graphsToDownload.map(graphIri => - NamedGraphTrigFile(graphIri = graphIri, tempDir = tempDir)) + NamedGraphTrigFile(graphIri = graphIri, tempDir = tempDir) + ) - projectSpecificNamedGraphTrigFileWriteFutures: Seq[Future[FileWrittenResponse]] = projectSpecificNamedGraphTrigFiles - .map { trigFile => + projectSpecificNamedGraphTrigFileWriteFutures: Seq[Future[FileWrittenResponse]] = + projectSpecificNamedGraphTrigFiles.map { trigFile => for { fileWrittenResponse: FileWrittenResponse <- ( storeManager ? @@ -666,28 +684,28 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // Stream the combined results into the output file. - namedGraphTrigFiles: Seq[NamedGraphTrigFile] = projectSpecificNamedGraphTrigFiles :+ adminDataNamedGraphTrigFile :+ permissionDataNamedGraphTrigFile + namedGraphTrigFiles: Seq[NamedGraphTrigFile] = + projectSpecificNamedGraphTrigFiles :+ adminDataNamedGraphTrigFile :+ permissionDataNamedGraphTrigFile resultFile: Path = tempDir.resolve(project.shortname + ".trig") _ = combineGraphs(namedGraphTrigFiles = namedGraphTrigFiles, resultFile = resultFile) } yield ProjectDataGetResponseADM(resultFile) } /** - * Get project's restricted view settings. - * - * @param identifier the project's identifier (IRI / shortcode / shortname) - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return [[ProjectRestrictedViewSettingsADM]] - */ + * Get project's restricted view settings. + * + * @param identifier the project's identifier (IRI / shortcode / shortname) + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return [[ProjectRestrictedViewSettingsADM]] + */ @ApiMayChange private def projectRestrictedViewSettingsGetADM( - identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Option[ProjectRestrictedViewSettingsADM]] = { - + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Option[ProjectRestrictedViewSettingsADM]] = // ToDo: We have two possible NotFound scenarios: 1. Project, 2. ProjectRestrictedViewSettings resource. How to send the client the correct NotFound reply? - for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt @@ -697,46 +715,47 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo maybeShortname = identifier.toShortnameOption, maybeShortcode = identifier.toShortcodeOption ) - .toString()) + .toString() + ) projectResponse <- (storeManager ? SparqlExtendedConstructRequest( sparql = sparqlQuery, featureFactoryConfig = featureFactoryConfig )).mapTo[SparqlExtendedConstructResponse] - restrictedViewSettings = if (projectResponse.statements.nonEmpty) { + restrictedViewSettings = + if (projectResponse.statements.nonEmpty) { - val (_, propsMap): (SubjectV2, Map[SmartIri, Seq[LiteralV2]]) = projectResponse.statements.head + val (_, propsMap): (SubjectV2, Map[SmartIri, Seq[LiteralV2]]) = projectResponse.statements.head - val size = propsMap - .get(OntologyConstants.KnoraAdmin.ProjectRestrictedViewSize.toSmartIri) - .map(_.head.asInstanceOf[StringLiteralV2].value) - val watermark = propsMap - .get(OntologyConstants.KnoraAdmin.ProjectRestrictedViewWatermark.toSmartIri) - .map(_.head.asInstanceOf[StringLiteralV2].value) + val size = propsMap + .get(OntologyConstants.KnoraAdmin.ProjectRestrictedViewSize.toSmartIri) + .map(_.head.asInstanceOf[StringLiteralV2].value) + val watermark = propsMap + .get(OntologyConstants.KnoraAdmin.ProjectRestrictedViewWatermark.toSmartIri) + .map(_.head.asInstanceOf[StringLiteralV2].value) - Some(ProjectRestrictedViewSettingsADM(size, watermark)) - } else { - None - } + Some(ProjectRestrictedViewSettingsADM(size, watermark)) + } else { + None + } } yield restrictedViewSettings - } - /** - * Get project's restricted view settings. - * - * @param identifier the project's identifier (IRI / shortcode / shortname) - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return [[ProjectRestrictedViewSettingsGetResponseADM]] - */ + * Get project's restricted view settings. + * + * @param identifier the project's identifier (IRI / shortcode / shortname) + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return [[ProjectRestrictedViewSettingsGetResponseADM]] + */ @ApiMayChange private def projectRestrictedViewSettingsGetRequestADM( - identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectRestrictedViewSettingsGetResponseADM] = { + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectRestrictedViewSettingsGetResponseADM] = { val maybeIri = identifier.toIriOption val maybeShortname = identifier.toShortnameOption @@ -760,30 +779,34 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Changes project's basic information. - * - * @param projectIri the IRI of the project. - * @param changeProjectRequest the change payload. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the unique api request ID. - * @return a [[ProjectOperationResponseADM]]. - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - */ - private def changeBasicInformationRequestADM(projectIri: IRI, - changeProjectRequest: ChangeProjectApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[ProjectOperationResponseADM] = { + * Changes project's basic information. + * + * @param projectIri the IRI of the project. + * @param changeProjectRequest the change payload. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the unique api request ID. + * @return a [[ProjectOperationResponseADM]]. + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + */ + private def changeBasicInformationRequestADM( + projectIri: IRI, + changeProjectRequest: ChangeProjectApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[ProjectOperationResponseADM] = { //log.debug(s"changeBasicInformationRequestV1: changeProjectRequest: {}", changeProjectRequest) /** - * The actual change project task run with an IRI lock. - */ - def changeProjectTask(projectIri: IRI, - changeProjectRequest: ChangeProjectApiRequestADM, - requestingUser: UserADM): Future[ProjectOperationResponseADM] = + * The actual change project task run with an IRI lock. + */ + def changeProjectTask( + projectIri: IRI, + changeProjectRequest: ChangeProjectApiRequestADM, + requestingUser: UserADM + ): Future[ProjectOperationResponseADM] = for { _ <- Future( @@ -828,20 +851,22 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Main project update method. - * - * @param projectIri the IRI of the project. - * @param projectUpdatePayload the data to be updated. Update means exchanging what is in the triplestore with - * this data. If only some parts of the data need to be changed, then this needs to - * be prepared in the step before this one. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[ProjectOperationResponseADM]]. - * @throws NotFoundException in the case that the project's IRI is not found. - */ - private def updateProjectADM(projectIri: IRI, - projectUpdatePayload: ProjectUpdatePayloadADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectOperationResponseADM] = { + * Main project update method. + * + * @param projectIri the IRI of the project. + * @param projectUpdatePayload the data to be updated. Update means exchanging what is in the triplestore with + * this data. If only some parts of the data need to be changed, then this needs to + * be prepared in the step before this one. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[ProjectOperationResponseADM]]. + * @throws NotFoundException in the case that the project's IRI is not found. + */ + private def updateProjectADM( + projectIri: IRI, + projectUpdatePayload: ProjectUpdatePayloadADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectOperationResponseADM] = { // log.debug("updateProjectADM - projectIri: {}, projectUpdatePayload: {}", projectIri, projectUpdatePayload) @@ -887,7 +912,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo maybeStatus = projectUpdatePayload.status, maybeSelfjoin = projectUpdatePayload.selfjoin ) - .toString) + .toString + ) // _ = log.debug(s"updateProjectADM - update query: {}", updateProjectSparqlString) @@ -903,30 +929,37 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo ) updatedProject: ProjectADM = maybeUpdatedProject.getOrElse( - throw UpdateNotPerformedException("Project was not updated. Please report this as a possible bug.")) + throw UpdateNotPerformedException("Project was not updated. Please report this as a possible bug.") + ) - _ = log.debug("updateProjectADM - projectUpdatePayload: {} / updatedProject: {}", - projectUpdatePayload, - updatedProject) + _ = log.debug( + "updateProjectADM - projectUpdatePayload: {} / updatedProject: {}", + projectUpdatePayload, + updatedProject + ) _ = if (projectUpdatePayload.shortname.isDefined) { val unescapedShortName: String = stringFormatter.fromSparqlEncodedString(projectUpdatePayload.shortname.get) if (updatedProject.shortname != unescapedShortName) throw UpdateNotPerformedException( - "Project's 'shortname' was not updated. Please report this as a possible bug.") + "Project's 'shortname' was not updated. Please report this as a possible bug." + ) } _ = if (projectUpdatePayload.longname.isDefined) { val unescapedLongname: Option[String] = stringFormatter.unescapeOptionalString(projectUpdatePayload.longname) if (updatedProject.longname != unescapedLongname) throw UpdateNotPerformedException( - s"Project's 'longname' was not updated. Please report this as a possible bug.") + s"Project's 'longname' was not updated. Please report this as a possible bug." + ) } _ = if (projectUpdatePayload.description.isDefined) { val unescapedDescriptions: Seq[StringLiteralV2] = projectUpdatePayload.description.get.map(desc => - StringLiteralV2(stringFormatter.fromSparqlEncodedString(desc.value), desc.language)) + StringLiteralV2(stringFormatter.fromSparqlEncodedString(desc.value), desc.language) + ) if (updatedProject.description.diff(unescapedDescriptions).nonEmpty) throw UpdateNotPerformedException( - "Project's 'description' was not updated. Please report this as a possible bug.") + "Project's 'description' was not updated. Please report this as a possible bug." + ) } _ = if (projectUpdatePayload.keywords.isDefined) { @@ -934,7 +967,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo projectUpdatePayload.keywords.get.map(key => stringFormatter.fromSparqlEncodedString(key)) if (updatedProject.keywords.sorted != unescapedKeywords.sorted) throw UpdateNotPerformedException( - "Project's 'keywords' was not updated. Please report this as a possible bug.") + "Project's 'keywords' was not updated. Please report this as a possible bug." + ) } _ = if (projectUpdatePayload.logo.isDefined) { val unescapedLogo: Option[String] = stringFormatter.unescapeOptionalString(projectUpdatePayload.logo) @@ -949,7 +983,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo _ = if (projectUpdatePayload.selfjoin.isDefined) { if (updatedProject.selfjoin != projectUpdatePayload.selfjoin.get) throw UpdateNotPerformedException( - "Project's 'selfjoin' status was not updated. Please report this as a possible bug.") + "Project's 'selfjoin' status was not updated. Please report this as a possible bug." + ) } } yield ProjectOperationResponseADM(project = updatedProject) @@ -957,31 +992,33 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Creates a project. - * - * @param createProjectRequest the new project's information. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user that is making the request. - * @param apiRequestID the unique api request ID. - * @return a [[ProjectOperationResponseADM]]. - * @throws ForbiddenException in the case that the user is not allowed to perform the operation. - * @throws DuplicateValueException in the case when either the shortname or shortcode are not unique. - * @throws BadRequestException in the case when the shortcode is invalid. - */ - private def projectCreateRequestADM(createProjectRequest: CreateProjectApiRequestADM, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[ProjectOperationResponseADM] = { + * Creates a project. + * + * @param createProjectRequest the new project's information. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user that is making the request. + * @param apiRequestID the unique api request ID. + * @return a [[ProjectOperationResponseADM]]. + * @throws ForbiddenException in the case that the user is not allowed to perform the operation. + * @throws DuplicateValueException in the case when either the shortname or shortcode are not unique. + * @throws BadRequestException in the case when the shortcode is invalid. + */ + private def projectCreateRequestADM( + createProjectRequest: CreateProjectApiRequestADM, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[ProjectOperationResponseADM] = { /** - * Creates following permissions for the new project - * 1. Permissions for project admins to do all operations on project level and to create, modify, delete, change rights, - * view, and restricted view of all new resources and values that belong to this project. - * 2. Permissions for project members to create, modify, view and restricted view of all new resources and values that belong to this project. - * - * @param projectIri the IRI of the new project. - * @throws BadRequestException if a permission is not created. - */ + * Creates following permissions for the new project + * 1. Permissions for project admins to do all operations on project level and to create, modify, delete, change rights, + * view, and restricted view of all new resources and values that belong to this project. + * 2. Permissions for project members to create, modify, view and restricted view of all new resources and values that belong to this project. + * + * @param projectIri the IRI of the new project. + * @throws BadRequestException if a permission is not created. + */ def createPermissionsForAdminsAndMembersOfNewProject(projectIri: IRI, projectShortCode: String): Future[Unit] = for { // Give the admins of the new project rights for any operation in project level, and rights to create resources. @@ -1046,15 +1083,18 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo )).mapTo[DefaultObjectAccessPermissionCreateResponseADM] } yield () - def projectCreateTask(createProjectRequest: CreateProjectApiRequestADM, - requestingUser: UserADM): Future[ProjectOperationResponseADM] = + def projectCreateTask( + createProjectRequest: CreateProjectApiRequestADM, + requestingUser: UserADM + ): Future[ProjectOperationResponseADM] = for { // check if the supplied shortname is unique shortnameExists <- projectByShortnameExists(createProjectRequest.shortname) _ = if (shortnameExists) { throw DuplicateValueException( - s"Project with the shortname: '${createProjectRequest.shortname}' already exists") + s"Project with the shortname: '${createProjectRequest.shortname}' already exists" + ) } // check if the optionally supplied shortcode is valid and unique @@ -1062,7 +1102,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo _ = if (shortcodeExists) { throw DuplicateValueException( - s"Project with the shortcode: '${createProjectRequest.shortcode}' already exists") + s"Project with the shortcode: '${createProjectRequest.shortcode}' already exists" + ) } // check if the requesting user is allowed to create project @@ -1075,7 +1116,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo customProjectIri: Option[SmartIri] = createProjectRequest.id.map(iri => iri.toSmartIri) newProjectIRI: IRI <- checkOrCreateEntityIri( customProjectIri, - stringFormatter.makeRandomProjectIri(createProjectRequest.shortcode)) + stringFormatter.makeRandomProjectIri(createProjectRequest.shortcode) + ) createNewProjectSparqlString = org.knora.webapi.messages.twirl.queries.sparql.admin.txt .createNewProject( @@ -1112,7 +1154,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // check to see if we could retrieve the new project newProjectADM = maybeNewProjectADM.getOrElse( throw UpdateNotPerformedException( - s"Project $newProjectIRI was not created. Please report this as a possible bug.") + s"Project $newProjectIRI was not created. Please report this as a possible bug." + ) ) // create permissions for admins and members of the new group _ <- createPermissionsForAdminsAndMembersOfNewProject(newProjectIRI, newProjectADM.shortcode) @@ -1134,51 +1177,52 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo //////////////////// /** - * Tries to retrieve a [[ProjectADM]] either from triplestore or cache if caching is enabled. - * If project is not found in cache but in triplestore, then project is written to cache. - * - * @param featureFactoryConfig the feature factory configuration. - */ + * Tries to retrieve a [[ProjectADM]] either from triplestore or cache if caching is enabled. + * If project is not found in cache but in triplestore, then project is written to cache. + * + * @param featureFactoryConfig the feature factory configuration. + */ private def getProjectFromCacheOrTriplestore( - identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig): Future[Option[ProjectADM]] = { + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Option[ProjectADM]] = if (cacheServiceSettings.cacheServiceEnabled) { // caching enabled - getProjectFromCache(identifier) - .flatMap { - case None => - // none found in cache. getting from triplestore. - getProjectFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) - .flatMap { - case None => - // also none found in triplestore. finally returning none. - log.debug("getProjectFromCacheOrTriplestore - not found in cache and in triplestore") - FastFuture.successful(None) - case Some(project) => - // found a project in the triplestore. need to write to cache. - log.debug( - "getProjectFromCacheOrTriplestore - not found in cache but found in triplestore. need to write to cache.") - // writing project to cache and afterwards returning the project found in the triplestore - writeProjectADMToCache(project).map(res => Some(project)) - } - case Some(user) => - log.debug("getProjectFromCacheOrTriplestore - found in cache. returning user.") - FastFuture.successful(Some(user)) - } + getProjectFromCache(identifier).flatMap { + case None => + // none found in cache. getting from triplestore. + getProjectFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig).flatMap { + case None => + // also none found in triplestore. finally returning none. + log.debug("getProjectFromCacheOrTriplestore - not found in cache and in triplestore") + FastFuture.successful(None) + case Some(project) => + // found a project in the triplestore. need to write to cache. + log.debug( + "getProjectFromCacheOrTriplestore - not found in cache but found in triplestore. need to write to cache." + ) + // writing project to cache and afterwards returning the project found in the triplestore + writeProjectADMToCache(project).map(res => Some(project)) + } + case Some(user) => + log.debug("getProjectFromCacheOrTriplestore - found in cache. returning user.") + FastFuture.successful(Some(user)) + } } else { // caching disabled log.debug("getProjectFromCacheOrTriplestore - caching disabled. getting from triplestore.") getProjectFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) } - } /** - * Tries to retrieve a [[ProjectADM]] from the triplestore. - * - * @param featureFactoryConfig the feature factory configuration. - */ - private def getProjectFromTriplestore(identifier: ProjectIdentifierADM, - featureFactoryConfig: FeatureFactoryConfig): Future[Option[ProjectADM]] = + * Tries to retrieve a [[ProjectADM]] from the triplestore. + * + * @param featureFactoryConfig the feature factory configuration. + */ + private def getProjectFromTriplestore( + identifier: ProjectIdentifierADM, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Option[ProjectADM]] = for { sparqlQuery <- Future( @@ -1189,7 +1233,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo maybeShortname = identifier.toShortnameOption, maybeShortcode = identifier.toShortcodeOption ) - .toString()) + .toString() + ) projectResponse <- (storeManager ? SparqlExtendedConstructRequest( sparql = sparqlQuery, @@ -1198,32 +1243,36 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo projectIris = projectResponse.statements.keySet.map(_.toString) - ontologies <- if (projectResponse.statements.nonEmpty) { - getOntologiesForProjects(projectIris, KnoraSystemInstances.Users.SystemUser) - } else { - FastFuture.successful(Map.empty[IRI, Seq[IRI]]) - } + ontologies <- + if (projectResponse.statements.nonEmpty) { + getOntologiesForProjects(projectIris, KnoraSystemInstances.Users.SystemUser) + } else { + FastFuture.successful(Map.empty[IRI, Seq[IRI]]) + } - maybeProjectADM: Option[ProjectADM] = if (projectResponse.statements.nonEmpty) { - log.debug("getProjectFromTriplestore - triplestore hit for: {}", identifier) - val projectOntologies = ontologies.getOrElse(projectIris.head, Seq.empty[IRI]) - Some(statements2ProjectADM(statements = projectResponse.statements.head, ontologies = projectOntologies)) - } else { - log.debug("getProjectFromTriplestore - no triplestore hit for: {}", identifier) - None - } + maybeProjectADM: Option[ProjectADM] = + if (projectResponse.statements.nonEmpty) { + log.debug("getProjectFromTriplestore - triplestore hit for: {}", identifier) + val projectOntologies = ontologies.getOrElse(projectIris.head, Seq.empty[IRI]) + Some(statements2ProjectADM(statements = projectResponse.statements.head, ontologies = projectOntologies)) + } else { + log.debug("getProjectFromTriplestore - no triplestore hit for: {}", identifier) + None + } } yield maybeProjectADM /** - * Helper method that turns SPARQL result rows into a [[ProjectInfoV1]]. - * - * @param statements results from the SPARQL query representing information about the project. - * @param ontologies the ontologies in the project. - * @return a [[ProjectADM]] representing information about project. - */ - private def statements2ProjectADM(statements: (SubjectV2, Map[SmartIri, Seq[LiteralV2]]), - ontologies: Seq[IRI]): ProjectADM = { + * Helper method that turns SPARQL result rows into a [[ProjectInfoV1]]. + * + * @param statements results from the SPARQL query representing information about the project. + * @param ontologies the ontologies in the project. + * @return a [[ProjectADM]] representing information about project. + */ + private def statements2ProjectADM( + statements: (SubjectV2, Map[SmartIri, Seq[LiteralV2]]), + ontologies: Seq[IRI] + ): ProjectADM = { // log.debug("statements2ProjectADM - statements: {}", statements) @@ -1233,14 +1282,18 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo ProjectADM( id = projectIri, shortname = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.ProjectShortname.toSmartIri, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortname defined.")) + .getOrElse( + OntologyConstants.KnoraAdmin.ProjectShortname.toSmartIri, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortname defined.") + ) .head .asInstanceOf[StringLiteralV2] .value, shortcode = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.ProjectShortcode.toSmartIri, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortcode defined.")) + .getOrElse( + OntologyConstants.KnoraAdmin.ProjectShortcode.toSmartIri, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortcode defined.") + ) .head .asInstanceOf[StringLiteralV2] .value, @@ -1262,8 +1315,10 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo .map(_.head.asInstanceOf[StringLiteralV2].value), ontologies = ontologies, status = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.Status.toSmartIri, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no status defined.")) + .getOrElse( + OntologyConstants.KnoraAdmin.Status.toSmartIri, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no status defined.") + ) .head .asInstanceOf[BooleanLiteralV2] .value, @@ -1279,17 +1334,18 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Helper method for checking if a project exists. - * - * @param maybeIri the IRI of the project. - * @param maybeShortname the shortname of the project. - * @param maybeShortcode the shortcode of the project. - * @return a [[Boolean]]. - */ - private def projectExists(maybeIri: Option[IRI], - maybeShortname: Option[String], - maybeShortcode: Option[String]): Future[Boolean] = { - + * Helper method for checking if a project exists. + * + * @param maybeIri the IRI of the project. + * @param maybeShortname the shortname of the project. + * @param maybeShortcode the shortcode of the project. + * @return a [[Boolean]]. + */ + private def projectExists( + maybeIri: Option[IRI], + maybeShortname: Option[String], + maybeShortcode: Option[String] + ): Future[Boolean] = if (maybeIri.nonEmpty) { projectByIriExists(maybeIri.get) } else if (maybeShortname.nonEmpty) { @@ -1299,71 +1355,70 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } else { FastFuture.successful(false) } - } /** - * Helper method for checking if a project identified by IRI exists. - * - * @param projectIri the IRI of the project. - * @return a [[Boolean]]. - */ - private def projectByIriExists(projectIri: IRI): Future[Boolean] = { + * Helper method for checking if a project identified by IRI exists. + * + * @param projectIri the IRI of the project. + * @return a [[Boolean]]. + */ + private def projectByIriExists(projectIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByIri(projectIri = projectIri) - .toString) + .toString + ) //_ = log.debug("projectExists - query: {}", askString) checkProjectExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkProjectExistsResponse.result } yield result - } /** - * Helper method for checking if a project identified by shortname exists. - * - * @param shortname the shortname of the project. - * @return a [[Boolean]]. - */ - private def projectByShortnameExists(shortname: String): Future[Boolean] = { + * Helper method for checking if a project identified by shortname exists. + * + * @param shortname the shortname of the project. + * @return a [[Boolean]]. + */ + private def projectByShortnameExists(shortname: String): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByShortname(shortname = shortname) - .toString) + .toString + ) //_ = log.debug("projectExists - query: {}", askString) checkProjectExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkProjectExistsResponse.result } yield result - } /** - * Helper method for checking if a project identified by shortcode exists. - * - * @param shortcode the shortcode of the project. - * @return a [[Boolean]]. - */ - private def projectByShortcodeExists(shortcode: String): Future[Boolean] = { + * Helper method for checking if a project identified by shortcode exists. + * + * @param shortcode the shortcode of the project. + * @return a [[Boolean]]. + */ + private def projectByShortcodeExists(shortcode: String): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByShortcode(shortcode = shortcode) - .toString) + .toString + ) //_ = log.debug("projectExists - query: {}", askString) checkProjectExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkProjectExistsResponse.result } yield result - } /** - * Tries to retrieve a [[ProjectADM]] from the cache. - */ + * Tries to retrieve a [[ProjectADM]] from the cache. + */ private def getProjectFromCache(identifier: ProjectIdentifierADM): Future[Option[ProjectADM]] = { val result = (storeManager ? CacheServiceGetProjectADM(identifier)).mapTo[Option[ProjectADM]] result.map { @@ -1377,11 +1432,11 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Writes the project to cache. - * - * @param project a [[ProjectADM]]. - * @return true if writing was successful. - */ + * Writes the project to cache. + * + * @param project a [[ProjectADM]]. + * @return true if writing was successful. + */ private def writeProjectADMToCache(project: ProjectADM): Future[Boolean] = { val result = (storeManager ? CacheServicePutProjectADM(project)).mapTo[Boolean] result.map { res => @@ -1391,9 +1446,9 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } /** - * Removes the project from cache. - */ - private def invalidateCachedProjectADM(maybeProject: Option[ProjectADM]): Future[Boolean] = { + * Removes the project from cache. + */ + private def invalidateCachedProjectADM(maybeProject: Option[ProjectADM]): Future[Boolean] = if (cacheServiceSettings.cacheServiceEnabled) { val keys: Set[String] = Seq(maybeProject.map(_.id), maybeProject.map(_.shortname), maybeProject.map(_.shortcode)).flatten.toSet @@ -1413,5 +1468,4 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo FastFuture.successful(true) } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/SipiResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/SipiResponderADM.scala index c531077500..e036c680e9 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/SipiResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/SipiResponderADM.scala @@ -48,16 +48,16 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import scala.concurrent.Future /** - * Responds to requests for information about binary representations of resources, and returns responses in Knora API - * ADM format. - */ + * Responds to requests for information about binary representations of resources, and returns responses in Knora API + * ADM format. + */ class SipiResponderADM(responderData: ResponderData) extends Responder(responderData) { /** - * Receives a message of type [[SipiResponderRequestADM]], and returns an appropriate response message, or - * [[Status.Failure]]. If a serious error occurs (i.e. an error that isn't the client's fault), this - * method first returns `Failure` to the sender, then throws an exception. - */ + * Receives a message of type [[SipiResponderRequestADM]], and returns an appropriate response message, or + * [[Status.Failure]]. If a serious error occurs (i.e. an error that isn't the client's fault), this + * method first returns `Failure` to the sender, then throws an exception. + */ def receive(msg: SipiResponderRequestADM) = msg match { case sipiFileInfoGetRequestADM: SipiFileInfoGetRequestADM => getFileInfoForSipiADM(sipiFileInfoGetRequestADM) case other => handleUnexpectedMessage(other, log, this.getClass.getName) @@ -67,15 +67,16 @@ class SipiResponderADM(responderData: ResponderData) extends Responder(responder // Methods for generating complete responses. /** - * Returns a [[SipiFileInfoGetResponseADM]] containing the permissions and path for a file. - * - * @param request the request. - * @return a [[SipiFileInfoGetResponseADM]]. - */ + * Returns a [[SipiFileInfoGetResponseADM]] containing the permissions and path for a file. + * + * @param request the request. + * @return a [[SipiFileInfoGetResponseADM]]. + */ private def getFileInfoForSipiADM(request: SipiFileInfoGetRequestADM): Future[SipiFileInfoGetResponseADM] = { log.debug( - s"SipiResponderADM - getFileInfoForSipiADM: projectID: ${request.projectID}, filename: ${request.filename}, user: ${request.requestingUser.username}") + s"SipiResponderADM - getFileInfoForSipiADM: projectID: ${request.projectID}, filename: ${request.filename}, user: ${request.requestingUser.username}" + ) for { sparqlQuery <- Future( @@ -84,7 +85,8 @@ class SipiResponderADM(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, filename = request.filename ) - .toString()) + .toString() + ) queryResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( sparql = sparqlQuery, @@ -100,7 +102,8 @@ class SipiResponderADM(responderData: ResponderData) extends Responder(responder case iriSubject: IriSubjectV2 => iriSubject case _ => throw InconsistentRepositoryDataException( - s"The subject of the file value with filename ${request.filename} is not an IRI") + s"The subject of the file value with filename ${request.filename} is not an IRI" + ) } assertions: Seq[(String, String)] = queryResponse.statements(fileValueIriSubject).toSeq.flatMap { @@ -117,7 +120,8 @@ class SipiResponderADM(responderData: ResponderData) extends Responder(responder ) _ = log.debug( - s"SipiResponderADM - getFileInfoForSipiADM - maybePermissionCode: $maybeEntityPermission, requestingUser: ${request.requestingUser.username}") + s"SipiResponderADM - getFileInfoForSipiADM - maybePermissionCode: $maybeEntityPermission, requestingUser: ${request.requestingUser.username}" + ) permissionCode: Int = maybeEntityPermission .map(_.toInt) @@ -137,7 +141,8 @@ class SipiResponderADM(responderData: ResponderData) extends Responder(responder case _ => FastFuture.successful( - SipiFileInfoGetResponseADM(permissionCode = permissionCode, restrictedViewSettings = None)) + SipiFileInfoGetResponseADM(permissionCode = permissionCode, restrictedViewSettings = None) + ) } _ = println(s"SipiResponderADM returning permission code $permissionCode") diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala index aef6dbbb59..d8bee7b218 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala @@ -43,37 +43,40 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import scala.concurrent.Future /** - * This responder is used by [[org.knora.webapi.routing.admin.StoreRouteADM]], for piping through HTTP requests to the - * 'Store Module' - */ + * This responder is used by [[org.knora.webapi.routing.admin.StoreRouteADM]], for piping through HTTP requests to the + * 'Store Module' + */ class StoresResponderADM(responderData: ResponderData) extends Responder(responderData) { /** - * A user representing the Knora API server, used in those cases where a user is required. - */ + * A user representing the Knora API server, used in those cases where a user is required. + */ private val systemUser = KnoraSystemInstances.Users.SystemUser /** - * Receives a message extending [[StoreResponderRequestADM]], and returns an appropriate response message. - */ + * Receives a message extending [[StoreResponderRequestADM]], and returns an appropriate response message. + */ def receive(msg: StoreResponderRequestADM) = msg match { - case ResetTriplestoreContentRequestADM(rdfDataObjects: Seq[RdfDataObject], - prependDefaults: Boolean, - featureFactoryConfig: FeatureFactoryConfig) => + case ResetTriplestoreContentRequestADM( + rdfDataObjects: Seq[RdfDataObject], + prependDefaults: Boolean, + featureFactoryConfig: FeatureFactoryConfig + ) => resetTriplestoreContent(rdfDataObjects, prependDefaults, featureFactoryConfig) case other => handleUnexpectedMessage(other, log, this.getClass.getName) } /** - * This method send a [[ResetRepositoryContent]] message to the [[org.knora.webapi.store.triplestore.TriplestoreManager]]. - * - * @param rdfDataObjects the payload consisting of a list of [[RdfDataObject]] send inside the message. - * @return a future containing a [[ResetTriplestoreContentResponseADM]]. - */ + * This method send a [[ResetRepositoryContent]] message to the [[org.knora.webapi.store.triplestore.TriplestoreManager]]. + * + * @param rdfDataObjects the payload consisting of a list of [[RdfDataObject]] send inside the message. + * @return a future containing a [[ResetTriplestoreContentResponseADM]]. + */ private def resetTriplestoreContent( - rdfDataObjects: Seq[RdfDataObject], - prependDefaults: Boolean = true, - featureFactoryConfig: FeatureFactoryConfig): Future[ResetTriplestoreContentResponseADM] = { + rdfDataObjects: Seq[RdfDataObject], + prependDefaults: Boolean = true, + featureFactoryConfig: FeatureFactoryConfig + ): Future[ResetTriplestoreContentResponseADM] = { log.debug(s"resetTriplestoreContent - called") @@ -81,7 +84,8 @@ class StoresResponderADM(responderData: ResponderData) extends Responder(respond value: Boolean <- (appActor ? GetAllowReloadOverHTTPState()).mapTo[Boolean] _ = if (!value) { throw ForbiddenException( - "The ResetTriplestoreContent operation is not allowed. Did you start the server with the right flag?") + "The ResetTriplestoreContent operation is not allowed. Did you start the server with the right flag?" + ) } resetResponse <- (storeManager ? ResetRepositoryContent(rdfDataObjects, prependDefaults)) diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala index ba2cc76954..f76ff199ad 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala @@ -168,95 +168,95 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[Seq[UserADM]] = for { _ <- Future( - if ( - !requestingUser.permissions.isSystemAdmin && !requestingUser.permissions - .isProjectAdminInAnyProject() && !requestingUser.isSystemUser - ) { - throw ForbiddenException("ProjectAdmin or SystemAdmin permissions are required.") - } - ) + if ( + !requestingUser.permissions.isSystemAdmin && !requestingUser.permissions + .isProjectAdminInAnyProject() && !requestingUser.isSystemUser + ) { + throw ForbiddenException("ProjectAdmin or SystemAdmin permissions are required.") + } + ) sparqlQueryString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .getUsers( - triplestore = settings.triplestoreType, - maybeIri = None, - maybeUsername = None, - maybeEmail = None - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .getUsers( + triplestore = settings.triplestoreType, + maybeIri = None, + maybeUsername = None, + maybeEmail = None + ) + .toString() + ) usersResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparqlQueryString, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] + sparql = sparqlQueryString, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] statements = usersResponse.statements.toList users: Seq[UserADM] = statements.map { case (userIri: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => - UserADM( - id = userIri.toString, - username = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.Username.toSmartIri, - throw InconsistentRepositoryDataException( - s"User: $userIri has no 'username' defined." - ) - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - email = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.Email.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'email' defined.") - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - givenName = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.GivenName.toSmartIri, - throw InconsistentRepositoryDataException( - s"User: $userIri has no 'givenName' defined." - ) - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - familyName = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.FamilyName.toSmartIri, - throw InconsistentRepositoryDataException( - s"User: $userIri has no 'familyName' defined." - ) - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - status = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.Status.toSmartIri, - throw InconsistentRepositoryDataException( - s"User: $userIri has no 'status' defined." - ) - ) - .head - .asInstanceOf[BooleanLiteralV2] - .value, - lang = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.PreferredLanguage.toSmartIri, - throw InconsistentRepositoryDataException( - s"User: $userIri has no 'preferedLanguage' defined." - ) - ) - .head - .asInstanceOf[StringLiteralV2] - .value - ) - } + UserADM( + id = userIri.toString, + username = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Username.toSmartIri, + throw InconsistentRepositoryDataException( + s"User: $userIri has no 'username' defined." + ) + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + email = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Email.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'email' defined.") + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + givenName = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.GivenName.toSmartIri, + throw InconsistentRepositoryDataException( + s"User: $userIri has no 'givenName' defined." + ) + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + familyName = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.FamilyName.toSmartIri, + throw InconsistentRepositoryDataException( + s"User: $userIri has no 'familyName' defined." + ) + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + status = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Status.toSmartIri, + throw InconsistentRepositoryDataException( + s"User: $userIri has no 'status' defined." + ) + ) + .head + .asInstanceOf[BooleanLiteralV2] + .value, + lang = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.PreferredLanguage.toSmartIri, + throw InconsistentRepositoryDataException( + s"User: $userIri has no 'preferedLanguage' defined." + ) + ) + .head + .asInstanceOf[StringLiteralV2] + .value + ) + } } yield users.sorted @@ -275,17 +275,17 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[UsersGetResponseADM] = for { maybeUsersListToReturn <- getAllUserADM( - userInformationType = userInformationType, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + userInformationType = userInformationType, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) result = maybeUsersListToReturn match { - case users: Seq[UserADM] if users.nonEmpty => - UsersGetResponseADM(users = users) - case _ => - throw NotFoundException(s"No users found") - } + case users: Seq[UserADM] if users.nonEmpty => + UsersGetResponseADM(users = users) + case _ => + throw NotFoundException(s"No users found") + } } yield result /** @@ -321,31 +321,34 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ) for { - maybeUserADM <- if (skipCache) { - // getting directly from triplestore - getUserFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) - } else { - // getting from cache or triplestore - getUserFromCacheOrTriplestore(identifier, featureFactoryConfig) - } + maybeUserADM <- + if (skipCache) { + // getting directly from triplestore + getUserFromTriplestore(identifier = identifier, featureFactoryConfig = featureFactoryConfig) + } else { + // getting from cache or triplestore + getUserFromCacheOrTriplestore(identifier, featureFactoryConfig) + } // return the correct amount of information depending on either the request or user permission - finalResponse: Option[UserADM] = if ( - requestingUser.permissions.isSystemAdmin || requestingUser - .isSelf(identifier) || requestingUser.isSystemUser - ) { - // return everything or what was requested - maybeUserADM.map(user => user.ofType(userInformationType)) - } else { - // return only public information - maybeUserADM.map(user => user.ofType(UserInformationTypeADM.PUBLIC)) - } - - _ = if (finalResponse.nonEmpty) { - log.debug("getSingleUserADM - successfully retrieved user: {}", identifier.value) - } else { - log.debug("getSingleUserADM - could not retrieve user: {}", identifier.value) - } + finalResponse: Option[UserADM] = + if ( + requestingUser.permissions.isSystemAdmin || requestingUser + .isSelf(identifier) || requestingUser.isSystemUser + ) { + // return everything or what was requested + maybeUserADM.map(user => user.ofType(userInformationType)) + } else { + // return only public information + maybeUserADM.map(user => user.ofType(UserInformationTypeADM.PUBLIC)) + } + + _ = + if (finalResponse.nonEmpty) { + log.debug("getSingleUserADM - successfully retrieved user: {}", identifier.value) + } else { + log.debug("getSingleUserADM - could not retrieve user: {}", identifier.value) + } } yield finalResponse } @@ -366,16 +369,16 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[UserResponseADM] = for { maybeUserADM <- getSingleUserADM( - identifier = identifier, - userInformationType = userInformationType, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + identifier = identifier, + userInformationType = userInformationType, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) result = maybeUserADM match { - case Some(user) => UserResponseADM(user = user) - case None => throw NotFoundException(s"User '${identifier.value}' not found") - } + case Some(user) => UserResponseADM(user = user) + case None => throw NotFoundException(s"User '${identifier.value}' not found") + } } yield result /** @@ -413,53 +416,53 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { // check if the requesting user is allowed to perform updates (i.e. requesting updates own information or is system admin) _ <- Future( - if (!requestingUser.id.equalsIgnoreCase(userIri) && !requestingUser.permissions.isSystemAdmin) { - throw ForbiddenException( - "User information can only be changed by the user itself or a system administrator" - ) - } - ) + if (!requestingUser.id.equalsIgnoreCase(userIri) && !requestingUser.permissions.isSystemAdmin) { + throw ForbiddenException( + "User information can only be changed by the user itself or a system administrator" + ) + } + ) // get current user information currentUserInformation: Option[UserADM] <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - userInformationType = UserInformationTypeADM.FULL, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + userInformationType = UserInformationTypeADM.FULL, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) // check if email is unique in case of a change email request emailTaken: Boolean <- userByEmailExists(userUpdateBasicInformationPayload.email, Some(currentUserInformation.get.email)) _ = if (emailTaken) { - throw DuplicateValueException( - s"User with the email '${userUpdateBasicInformationPayload.email.get.value}' already exists" - ) - } + throw DuplicateValueException( + s"User with the email '${userUpdateBasicInformationPayload.email.get.value}' already exists" + ) + } // check if username is unique in case of a change username request usernameTaken: Boolean <- userByUsernameExists(userUpdateBasicInformationPayload.username, Some(currentUserInformation.get.username)) _ = if (usernameTaken) { - throw DuplicateValueException( - s"User with the username '${userUpdateBasicInformationPayload.username.get.value}' already exists" - ) - } + throw DuplicateValueException( + s"User with the username '${userUpdateBasicInformationPayload.username.get.value}' already exists" + ) + } // send change request as SystemUser result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM( - username = userUpdateBasicInformationPayload.username, - email = userUpdateBasicInformationPayload.email, - givenName = userUpdateBasicInformationPayload.givenName, - familyName = userUpdateBasicInformationPayload.familyName, - lang = userUpdateBasicInformationPayload.lang - ), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM( + username = userUpdateBasicInformationPayload.username, + email = userUpdateBasicInformationPayload.email, + givenName = userUpdateBasicInformationPayload.givenName, + familyName = userUpdateBasicInformationPayload.familyName, + lang = userUpdateBasicInformationPayload.lang + ), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { @@ -507,42 +510,42 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { // check if the requesting user is allowed to perform updates (i.e. requesting updates own information or is system admin) _ <- Future( - if (!requestingUser.id.equalsIgnoreCase(userIri) && !requestingUser.permissions.isSystemAdmin) { - throw ForbiddenException( - "User's password can only be changed by the user itself or a system administrator" - ) - } - ) + if (!requestingUser.id.equalsIgnoreCase(userIri) && !requestingUser.permissions.isSystemAdmin) { + throw ForbiddenException( + "User's password can only be changed by the user itself or a system administrator" + ) + } + ) // check if supplied password matches requesting user's password _ = if (!requestingUser.passwordMatch(userUpdatePasswordPayload.requesterPassword.value)) { - throw ForbiddenException("The supplied password does not match the requesting user's password.") - } + throw ForbiddenException("The supplied password does not match the requesting user's password.") + } // hash the new password encoder = new BCryptPasswordEncoder(settings.bcryptPasswordStrength) newHashedPassword = Password - .create(encoder.encode(userUpdatePasswordPayload.newPassword.value)) - .fold(error => throw error, value => value) + .create(encoder.encode(userUpdatePasswordPayload.newPassword.value)) + .fold(error => throw error, value => value) // update the users password as SystemUser result <- updateUserPasswordADM( - userIri = userIri, - password = newHashedPassword, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + password = newHashedPassword, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { // run the change password task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => changePasswordTask(userIri, userUpdatePasswordPayload, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => changePasswordTask(userIri, userUpdatePasswordPayload, requestingUser, apiRequestID) + ) } yield taskResult } @@ -588,22 +591,22 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(status = Some(status)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(status = Some(status)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { // run the change status task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => changeUserStatusTask(userIri, status, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => changeUserStatusTask(userIri, status, requestingUser, apiRequestID) + ) } yield taskResult } @@ -647,12 +650,12 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(systemAdmin = Some(systemAdmin)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(systemAdmin = Some(systemAdmin)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result @@ -681,16 +684,16 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[Seq[ProjectADM]] = for { maybeUser <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - userInformationType = UserInformationTypeADM.FULL, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + userInformationType = UserInformationTypeADM.FULL, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) result = maybeUser match { - case Some(userADM) => userADM.projects - case None => Seq.empty[ProjectADM] - } + case Some(userADM) => userADM.projects + case None => Seq.empty[ProjectADM] + } } yield result @@ -709,14 +712,14 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { userExists <- userExists(userIri) _ = if (!userExists) { - throw BadRequestException(s"User $userIri does not exist.") - } + throw BadRequestException(s"User $userIri does not exist.") + } projects: Seq[ProjectADM] <- userProjectMembershipsGetADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) result = UserProjectMembershipsGetResponseADM(projects = projects) } yield result @@ -763,47 +766,48 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // check if user exists userExists <- userExists(userIri) - _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") + _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") // check if project exists projectExists <- projectExists(projectIri) - _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") + _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") // get users current project membership list currentProjectMemberships <- userProjectMembershipsGetRequestADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) currentProjectMembershipIris: Seq[IRI] = currentProjectMemberships.projects.map(_.id) // check if user is already member and if not then append to list - updatedProjectMembershipIris = if (!currentProjectMembershipIris.contains(projectIri)) { - currentProjectMembershipIris :+ projectIri - } else { - throw BadRequestException( - s"User $userIri is already member of project $projectIri." - ) - } + updatedProjectMembershipIris = + if (!currentProjectMembershipIris.contains(projectIri)) { + currentProjectMembershipIris :+ projectIri + } else { + throw BadRequestException( + s"User $userIri is already member of project $projectIri." + ) + } // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(projects = Some(updatedProjectMembershipIris)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(projects = Some(updatedProjectMembershipIris)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = apiRequestID + ) } yield result for { // run the task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => userProjectMembershipAddRequestTask(userIri, projectIri, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => userProjectMembershipAddRequestTask(userIri, projectIri, requestingUser, apiRequestID) + ) } yield taskResult } @@ -848,46 +852,47 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // check if user exists userExists <- userExists(userIri) - _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") + _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") // check if project exists projectExists <- projectExists(projectIri) - _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") + _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") // get users current project membership list currentProjectMemberships <- userProjectMembershipsGetADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) currentProjectMembershipIris = currentProjectMemberships.map(_.id) // check if user is not already a member and if he is then remove the project from to list - updatedProjectMembershipIris = if (currentProjectMembershipIris.contains(projectIri)) { - currentProjectMembershipIris diff Seq(projectIri) - } else { - throw BadRequestException( - s"User $userIri is not member of project $projectIri." - ) - } + updatedProjectMembershipIris = + if (currentProjectMembershipIris.contains(projectIri)) { + currentProjectMembershipIris diff Seq(projectIri) + } else { + throw BadRequestException( + s"User $userIri is not member of project $projectIri." + ) + } // create the update request by using the SystemUser result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(projects = Some(updatedProjectMembershipIris)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(projects = Some(updatedProjectMembershipIris)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { // run the task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => userProjectMembershipRemoveRequestTask(userIri, projectIri, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => userProjectMembershipRemoveRequestTask(userIri, projectIri, requestingUser, apiRequestID) + ) } yield taskResult } @@ -910,36 +915,35 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // ToDo: this is a bit of a hack since the ProjectAdmin group doesn't really exist. for { sparqlQueryString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v1.txt - .getUserByIri( - triplestore = settings.triplestoreType, - userIri = userIri - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.v1.txt + .getUserByIri( + triplestore = settings.triplestoreType, + userIri = userIri + ) + .toString() + ) userDataQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] groupedUserData: Map[String, Seq[String]] = userDataQueryResponse.results.bindings.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) - } + case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) + } /* the projects the user is member of */ projectIris: Seq[IRI] = groupedUserData.get(OntologyConstants.KnoraAdmin.IsInProjectAdminGroup) match { - case Some(projects) => projects - case None => Seq.empty[IRI] - } + case Some(projects) => projects + case None => Seq.empty[IRI] + } maybeProjectFutures: Seq[Future[Option[ProjectADM]]] = projectIris.map { projectIri => - (responderManager ? ProjectGetADM( - identifier = - ProjectIdentifierADM(maybeIri = Some(projectIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[ProjectADM]] - } + (responderManager ? ProjectGetADM( + identifier = ProjectIdentifierADM(maybeIri = Some(projectIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[ProjectADM]] + } maybeProjects: Seq[Option[ProjectADM]] <- Future.sequence(maybeProjectFutures) - projects: Seq[ProjectADM] = maybeProjects.flatten + projects: Seq[ProjectADM] = maybeProjects.flatten } yield projects @@ -964,15 +968,15 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { userExists <- userExists(userIri) _ = if (!userExists) { - throw BadRequestException(s"User $userIri does not exist.") - } + throw BadRequestException(s"User $userIri does not exist.") + } projects: Seq[ProjectADM] <- userProjectAdminMembershipsGetADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield UserProjectAdminMembershipsGetResponseADM(projects = projects) /** @@ -1015,48 +1019,49 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // check if user exists userExists <- userExists(userIri) - _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") + _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") // check if project exists projectExists <- projectExists(projectIri) - _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") + _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") // get users current project membership list currentProjectAdminMemberships <- userProjectAdminMembershipsGetADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) currentProjectAdminMembershipIris: Seq[IRI] = currentProjectAdminMemberships.map(_.id) // check if user is already member and if not then append to list - updatedProjectAdminMembershipIris = if (!currentProjectAdminMembershipIris.contains(projectIri)) { - currentProjectAdminMembershipIris :+ projectIri - } else { - throw BadRequestException( - s"User $userIri is already a project admin for project $projectIri." - ) - } + updatedProjectAdminMembershipIris = + if (!currentProjectAdminMembershipIris.contains(projectIri)) { + currentProjectAdminMembershipIris :+ projectIri + } else { + throw BadRequestException( + s"User $userIri is already a project admin for project $projectIri." + ) + } // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(projectsAdmin = Some(updatedProjectAdminMembershipIris)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(projectsAdmin = Some(updatedProjectAdminMembershipIris)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { // run the task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => userProjectAdminMembershipAddRequestTask(userIri, projectIri, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => userProjectAdminMembershipAddRequestTask(userIri, projectIri, requestingUser, apiRequestID) + ) } yield taskResult } @@ -1101,39 +1106,40 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // check if user exists userExists <- userExists(userIri) - _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") + _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") // check if project exists projectExists <- projectExists(projectIri) - _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") + _ = if (!projectExists) throw NotFoundException(s"The project $projectIri does not exist.") // get users current project membership list currentProjectAdminMemberships <- userProjectAdminMembershipsGetADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) currentProjectAdminMembershipIris: Seq[IRI] = currentProjectAdminMemberships.map(_.id) // check if user is not already a member and if he is then remove the project from to list - updatedProjectAdminMembershipIris = if (currentProjectAdminMembershipIris.contains(projectIri)) { - currentProjectAdminMembershipIris diff Seq(projectIri) - } else { - throw BadRequestException( - s"User $userIri is not a project admin of project $projectIri." - ) - } + updatedProjectAdminMembershipIris = + if (currentProjectAdminMembershipIris.contains(projectIri)) { + currentProjectAdminMembershipIris diff Seq(projectIri) + } else { + throw BadRequestException( + s"User $userIri is not a project admin of project $projectIri." + ) + } // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(projectsAdmin = Some(updatedProjectAdminMembershipIris)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(projectsAdmin = Some(updatedProjectAdminMembershipIris)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { @@ -1162,23 +1168,23 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[Seq[GroupADM]] = for { maybeUserADM: Option[UserADM] <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - userInformationType = UserInformationTypeADM.FULL, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + userInformationType = UserInformationTypeADM.FULL, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) groups: Seq[GroupADM] = maybeUserADM match { - case Some(user) => - log.debug( - "userGroupMembershipsGetADM - user found. Returning his groups: {}.", - user.groups - ) - user.groups - case None => - log.debug("userGroupMembershipsGetADM - user not found. Returning empty seq.") - Seq.empty[GroupADM] - } + case Some(user) => + log.debug( + "userGroupMembershipsGetADM - user found. Returning his groups: {}.", + user.groups + ) + user.groups + case None => + log.debug("userGroupMembershipsGetADM - user not found. Returning empty seq.") + Seq.empty[GroupADM] + } } yield groups @@ -1197,10 +1203,10 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[UserGroupMembershipsGetResponseADM] = for { groups: Seq[GroupADM] <- userGroupMembershipsGetADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) } yield UserGroupMembershipsGetResponseADM(groups = groups) /** @@ -1233,40 +1239,40 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { // check if user exists maybeUser <- getSingleUserADM( - UserIdentifierADM(maybeIri = Some(userIri)), - UserInformationTypeADM.FULL, - featureFactoryConfig = featureFactoryConfig, - KnoraSystemInstances.Users.SystemUser, - skipCache = true - ) + UserIdentifierADM(maybeIri = Some(userIri)), + UserInformationTypeADM.FULL, + featureFactoryConfig = featureFactoryConfig, + KnoraSystemInstances.Users.SystemUser, + skipCache = true + ) userToChange: UserADM = maybeUser match { - case Some(user) => user - case None => throw NotFoundException(s"The user $userIri does not exist.") - } + case Some(user) => user + case None => throw NotFoundException(s"The user $userIri does not exist.") + } // check if group exists groupExists <- groupExists(groupIri) - _ = if (!groupExists) throw NotFoundException(s"The group $groupIri does not exist.") + _ = if (!groupExists) throw NotFoundException(s"The group $groupIri does not exist.") // get group's info. we need the project IRI. maybeGroupADM <- (responderManager ? GroupGetADM( - groupIri = groupIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[GroupADM]] + groupIri = groupIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[GroupADM]] projectIri = maybeGroupADM - .getOrElse(throw InconsistentRepositoryDataException(s"Group $groupIri does not exist")) - .project - .id + .getOrElse(throw InconsistentRepositoryDataException(s"Group $groupIri does not exist")) + .project + .id // check if the requesting user is allowed to perform updates (i.e. project or system administrator) _ = if (!requestingUser.permissions.isProjectAdmin(projectIri) && !requestingUser.permissions.isSystemAdmin) { - throw ForbiddenException( - "User's group membership can only be changed by a project or system administrator" - ) - } + throw ForbiddenException( + "User's group membership can only be changed by a project or system administrator" + ) + } // get users current group membership list currentGroupMemberships = userToChange.groups @@ -1274,29 +1280,30 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde currentGroupMembershipIris: Seq[IRI] = currentGroupMemberships.map(_.id) // check if user is already member and if not then append to list - updatedGroupMembershipIris = if (!currentGroupMembershipIris.contains(groupIri)) { - currentGroupMembershipIris :+ groupIri - } else { - throw BadRequestException(s"User $userIri is already member of group $groupIri.") - } + updatedGroupMembershipIris = + if (!currentGroupMembershipIris.contains(groupIri)) { + currentGroupMembershipIris :+ groupIri + } else { + throw BadRequestException(s"User $userIri is already member of group $groupIri.") + } // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(groups = Some(updatedGroupMembershipIris)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(groups = Some(updatedGroupMembershipIris)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + apiRequestID = apiRequestID + ) } yield result for { // run the task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => userGroupMembershipAddRequestTask(userIri, groupIri, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => userGroupMembershipAddRequestTask(userIri, groupIri, requestingUser, apiRequestID) + ) } yield taskResult } @@ -1321,23 +1328,23 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { // check if user exists userExists <- userExists(userIri) - _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") + _ = if (!userExists) throw NotFoundException(s"The user $userIri does not exist.") // check if group exists projectExists <- groupExists(groupIri) - _ = if (!projectExists) throw NotFoundException(s"The group $groupIri does not exist.") + _ = if (!projectExists) throw NotFoundException(s"The group $groupIri does not exist.") // get group's info. we need the project IRI. maybeGroupADM <- (responderManager ? GroupGetADM( - groupIri = groupIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[GroupADM]] + groupIri = groupIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[GroupADM]] projectIri = maybeGroupADM - .getOrElse(throw InconsistentRepositoryDataException(s"Group $groupIri does not exist")) - .project - .id + .getOrElse(throw InconsistentRepositoryDataException(s"Group $groupIri does not exist")) + .project + .id // check if the requesting user is allowed to perform updates (i.e. is project or system admin) _ = @@ -1350,37 +1357,38 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // get users current project membership list currentGroupMemberships <- userGroupMembershipsGetRequestADM( - userIri = userIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + userIri = userIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) currentGroupMembershipIris: Seq[IRI] = currentGroupMemberships.groups.map(_.id) // check if user is not already a member and if he is then remove the project from to list - updatedGroupMembershipIris = if (currentGroupMembershipIris.contains(groupIri)) { - currentGroupMembershipIris diff Seq(groupIri) - } else { - throw BadRequestException(s"User $userIri is not member of group $groupIri.") - } + updatedGroupMembershipIris = + if (currentGroupMembershipIris.contains(groupIri)) { + currentGroupMembershipIris diff Seq(groupIri) + } else { + throw BadRequestException(s"User $userIri is not member of group $groupIri.") + } // create the update request result <- updateUserADM( - userIri = userIri, - userUpdatePayload = UserChangeRequestADM(groups = Some(updatedGroupMembershipIris)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = apiRequestID - ) + userIri = userIri, + userUpdatePayload = UserChangeRequestADM(groups = Some(updatedGroupMembershipIris)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = apiRequestID + ) } yield result for { // run the task with an IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - userIri, - () => userGroupMembershipRemoveRequestTask(userIri, groupIri, requestingUser, apiRequestID) - ) + apiRequestID, + userIri, + () => userGroupMembershipRemoveRequestTask(userIri, groupIri, requestingUser, apiRequestID) + ) } yield taskResult } @@ -1417,107 +1425,107 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { maybeCurrentUser <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - userInformationType = UserInformationTypeADM.FULL, - skipCache = true - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + userInformationType = UserInformationTypeADM.FULL, + skipCache = true + ) _ = if (maybeCurrentUser.isEmpty) { - throw NotFoundException(s"User '$userIri' not found. Aborting update request.") - } + throw NotFoundException(s"User '$userIri' not found. Aborting update request.") + } // we are changing the user, so lets get rid of the cached copy _ = invalidateCachedUserADM(maybeCurrentUser) /* Update the user */ maybeChangedUsername = userUpdatePayload.username match { - case Some(username) => Some(username.value) - case None => None - } + case Some(username) => Some(username.value) + case None => None + } maybeChangedEmail = userUpdatePayload.email match { - case Some(email) => Some(email.value) - case None => None - } + case Some(email) => Some(email.value) + case None => None + } maybeChangedGivenName = userUpdatePayload.givenName match { - case Some(givenName) => - Some( - stringFormatter.toSparqlEncodedString( - givenName.value, - throw BadRequestException( - s"The supplied given name: '${givenName.value}' is not valid." - ) - ) - ) - case None => None - } + case Some(givenName) => + Some( + stringFormatter.toSparqlEncodedString( + givenName.value, + throw BadRequestException( + s"The supplied given name: '${givenName.value}' is not valid." + ) + ) + ) + case None => None + } maybeChangedFamilyName = userUpdatePayload.familyName match { - case Some(familyName) => - Some( - stringFormatter.toSparqlEncodedString( - familyName.value, - throw BadRequestException( - s"The supplied family name: '${familyName.value}' is not valid." - ) - ) - ) - case None => None - } + case Some(familyName) => + Some( + stringFormatter.toSparqlEncodedString( + familyName.value, + throw BadRequestException( + s"The supplied family name: '${familyName.value}' is not valid." + ) + ) + ) + case None => None + } maybeChangedStatus = userUpdatePayload.status match { - case Some(status) => Some(status.value) - case None => None - } + case Some(status) => Some(status.value) + case None => None + } maybeChangedLang = userUpdatePayload.lang match { - case Some(lang) => Some(lang.value) - case None => None - } + case Some(lang) => Some(lang.value) + case None => None + } maybeChangedProjects = userUpdatePayload.projects match { - case Some(projects) => Some(projects) - case None => None - } + case Some(projects) => Some(projects) + case None => None + } maybeChangedProjectsAdmin = userUpdatePayload.projectsAdmin match { - case Some(projectsAdmin) => Some(projectsAdmin) - case None => None - } + case Some(projectsAdmin) => Some(projectsAdmin) + case None => None + } maybeChangedGroups = userUpdatePayload.groups match { - case Some(groups) => Some(groups) - case None => None - } + case Some(groups) => Some(groups) + case None => None + } maybeChangedSystemAdmin = userUpdatePayload.systemAdmin match { - case Some(systemAdmin) => Some(systemAdmin.value) - case None => None - } + case Some(systemAdmin) => Some(systemAdmin.value) + case None => None + } updateUserSparqlString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .updateUser( - adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, - triplestore = settings.triplestoreType, - userIri = userIri, - maybeUsername = maybeChangedUsername, - maybeEmail = maybeChangedEmail, - maybeGivenName = maybeChangedGivenName, - maybeFamilyName = maybeChangedFamilyName, - maybeStatus = maybeChangedStatus, - maybeLang = maybeChangedLang, - maybeProjects = maybeChangedProjects, - maybeProjectsAdmin = maybeChangedProjectsAdmin, - maybeGroups = maybeChangedGroups, - maybeSystemAdmin = maybeChangedSystemAdmin - ) - .toString - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .updateUser( + adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, + triplestore = settings.triplestoreType, + userIri = userIri, + maybeUsername = maybeChangedUsername, + maybeEmail = maybeChangedEmail, + maybeGivenName = maybeChangedGivenName, + maybeFamilyName = maybeChangedFamilyName, + maybeStatus = maybeChangedStatus, + maybeLang = maybeChangedLang, + maybeProjects = maybeChangedProjects, + maybeProjectsAdmin = maybeChangedProjectsAdmin, + maybeGroups = maybeChangedGroups, + maybeSystemAdmin = maybeChangedSystemAdmin + ) + .toString + ) updateResult <- (storeManager ? SparqlUpdateRequest(updateUserSparqlString)).mapTo[SparqlUpdateResponse] /* Verify that the user was updated. */ maybeUpdatedUserADM <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - userInformationType = UserInformationTypeADM.FULL, - skipCache = true - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + userInformationType = UserInformationTypeADM.FULL, + skipCache = true + ) updatedUserADM: UserADM = maybeUpdatedUserADM.getOrElse( @@ -1525,63 +1533,63 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ) _ = if (userUpdatePayload.username.isDefined) { - if (updatedUserADM.username != userUpdatePayload.username.get.value) - throw UpdateNotPerformedException( - "User's 'username' was not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.username != userUpdatePayload.username.get.value) + throw UpdateNotPerformedException( + "User's 'username' was not updated. Please report this as a possible bug." + ) + } _ = if (userUpdatePayload.email.isDefined) { - if (updatedUserADM.email != userUpdatePayload.email.get.value) - throw UpdateNotPerformedException("User's 'email' was not updated. Please report this as a possible bug.") - } + if (updatedUserADM.email != userUpdatePayload.email.get.value) + throw UpdateNotPerformedException("User's 'email' was not updated. Please report this as a possible bug.") + } _ = if (userUpdatePayload.givenName.isDefined) { - if (updatedUserADM.givenName != userUpdatePayload.givenName.get.value) - throw UpdateNotPerformedException( - "User's 'givenName' was not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.givenName != userUpdatePayload.givenName.get.value) + throw UpdateNotPerformedException( + "User's 'givenName' was not updated. Please report this as a possible bug." + ) + } _ = if (userUpdatePayload.familyName.isDefined) { - if (updatedUserADM.familyName != userUpdatePayload.familyName.get.value) - throw UpdateNotPerformedException( - "User's 'familyName' was not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.familyName != userUpdatePayload.familyName.get.value) + throw UpdateNotPerformedException( + "User's 'familyName' was not updated. Please report this as a possible bug." + ) + } _ = if (userUpdatePayload.status.isDefined) { - if (updatedUserADM.status != userUpdatePayload.status.get.value) - throw UpdateNotPerformedException( - "User's 'status' was not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.status != userUpdatePayload.status.get.value) + throw UpdateNotPerformedException( + "User's 'status' was not updated. Please report this as a possible bug." + ) + } _ = if (userUpdatePayload.lang.isDefined) { - if (updatedUserADM.lang != userUpdatePayload.lang.get.value) - throw UpdateNotPerformedException("User's 'lang' was not updated. Please report this as a possible bug.") - } + if (updatedUserADM.lang != userUpdatePayload.lang.get.value) + throw UpdateNotPerformedException("User's 'lang' was not updated. Please report this as a possible bug.") + } _ = if (userUpdatePayload.projects.isDefined) { - if (updatedUserADM.projects.map(_.id) != userUpdatePayload.projects.get) - throw UpdateNotPerformedException( - "User's 'project' memberships where not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.projects.map(_.id) != userUpdatePayload.projects.get) + throw UpdateNotPerformedException( + "User's 'project' memberships where not updated. Please report this as a possible bug." + ) + } _ = if (userUpdatePayload.systemAdmin.isDefined) { - if (updatedUserADM.permissions.isSystemAdmin != userUpdatePayload.systemAdmin.get.value) - throw UpdateNotPerformedException( - "User's 'isInSystemAdminGroup' status was not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.permissions.isSystemAdmin != userUpdatePayload.systemAdmin.get.value) + throw UpdateNotPerformedException( + "User's 'isInSystemAdminGroup' status was not updated. Please report this as a possible bug." + ) + } _ = if (userUpdatePayload.groups.isDefined) { - if (updatedUserADM.groups.map(_.id) != userUpdatePayload.groups.get) - throw UpdateNotPerformedException( - "User's 'group' memberships where not updated. Please report this as a possible bug." - ) - } + if (updatedUserADM.groups.map(_.id) != userUpdatePayload.groups.get) + throw UpdateNotPerformedException( + "User's 'group' memberships where not updated. Please report this as a possible bug." + ) + } } yield UserOperationResponseADM(updatedUserADM.ofType(UserInformationTypeADM.RESTRICTED)) } @@ -1617,41 +1625,41 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { maybeCurrentUser <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - userInformationType = UserInformationTypeADM.FULL, - skipCache = true - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + userInformationType = UserInformationTypeADM.FULL, + skipCache = true + ) _ = if (maybeCurrentUser.isEmpty) { - throw NotFoundException(s"User '$userIri' not found. Aborting update request.") - } + throw NotFoundException(s"User '$userIri' not found. Aborting update request.") + } // we are changing the user, so lets get rid of the cached copy _ = invalidateCachedUserADM(maybeCurrentUser) // update the password updateUserSparqlString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .updateUserPassword( - adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, - triplestore = settings.triplestoreType, - userIri = userIri, - newPassword = password.value - ) - .toString - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .updateUserPassword( + adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, + triplestore = settings.triplestoreType, + userIri = userIri, + newPassword = password.value + ) + .toString + ) updateResult <- (storeManager ? SparqlUpdateRequest(updateUserSparqlString)).mapTo[SparqlUpdateResponse] /* Verify that the password was updated. */ maybeUpdatedUserADM <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - userInformationType = UserInformationTypeADM.FULL, - skipCache = true - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + userInformationType = UserInformationTypeADM.FULL, + skipCache = true + ) updatedUserADM: UserADM = maybeUpdatedUserADM.getOrElse( @@ -1659,7 +1667,7 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ) _ = if (updatedUserADM.password.get != password.value) - throw UpdateNotPerformedException("User's password was not updated. Please report this as a possible bug.") + throw UpdateNotPerformedException("User's password was not updated. Please report this as a possible bug.") } yield UserOperationResponseADM(updatedUserADM.ofType(UserInformationTypeADM.RESTRICTED)) } @@ -1694,83 +1702,83 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // check if username is unique usernameTaken: Boolean <- userByUsernameExists(userCreatePayloadADM.username) _ = if (usernameTaken) { - throw DuplicateValueException( - s"User with the username '${userCreatePayloadADM.username.get.value}' already exists" - ) - } + throw DuplicateValueException( + s"User with the username '${userCreatePayloadADM.username.get.value}' already exists" + ) + } // check if email is unique emailTaken: Boolean <- userByEmailExists(userCreatePayloadADM.email) _ = if (emailTaken) { - throw DuplicateValueException( - s"User with the email '${userCreatePayloadADM.email.get.value}' already exists" - ) - } + throw DuplicateValueException( + s"User with the email '${userCreatePayloadADM.email.get.value}' already exists" + ) + } // check the custom IRI; if not given, create an unused IRI customUserIri: Option[SmartIri] = userCreatePayloadADM.id.map(iri => iri.toSmartIri) - userIri: IRI <- checkOrCreateEntityIri(customUserIri, stringFormatter.makeRandomPersonIri) + userIri: IRI <- checkOrCreateEntityIri(customUserIri, stringFormatter.makeRandomPersonIri) // hash password - encoder = new BCryptPasswordEncoder(settings.bcryptPasswordStrength) + encoder = new BCryptPasswordEncoder(settings.bcryptPasswordStrength) hashedPassword = encoder.encode(userCreatePayloadADM.password.get.value) // Create the new user. createNewUserSparqlString = org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .createNewUser( - adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, - triplestore = settings.triplestoreType, - userIri = userIri, - userClassIri = OntologyConstants.KnoraAdmin.User, - username = stringFormatter.toSparqlEncodedString( - userCreatePayloadADM.username.get.value, - errorFun = throw BadRequestException( - s"The supplied username: '${userCreatePayloadADM.username.get.value}' is not valid." - ) - ), - email = stringFormatter.toSparqlEncodedString( - userCreatePayloadADM.email.get.value, - errorFun = throw BadRequestException( - s"The supplied email: '${userCreatePayloadADM.email.get.value}' is not valid." - ) - ), - password = hashedPassword, - givenName = stringFormatter.toSparqlEncodedString( - userCreatePayloadADM.givenName.get.value, - errorFun = throw BadRequestException( - s"The supplied given name: '${userCreatePayloadADM.givenName.get.value}' is not valid." - ) - ), - familyName = stringFormatter.toSparqlEncodedString( - userCreatePayloadADM.familyName.get.value, - errorFun = throw BadRequestException( - s"The supplied family name: '${userCreatePayloadADM.familyName.get.value}' is not valid." - ) - ), - status = userCreatePayloadADM.status.get.value, - preferredLanguage = stringFormatter.toSparqlEncodedString( - userCreatePayloadADM.lang.get.value, - errorFun = throw BadRequestException( - s"The supplied language: '${userCreatePayloadADM.lang.get.value}' is not valid." - ) - ), - systemAdmin = userCreatePayloadADM.systemAdmin.get.value - ) - .toString + .createNewUser( + adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, + triplestore = settings.triplestoreType, + userIri = userIri, + userClassIri = OntologyConstants.KnoraAdmin.User, + username = stringFormatter.toSparqlEncodedString( + userCreatePayloadADM.username.get.value, + errorFun = throw BadRequestException( + s"The supplied username: '${userCreatePayloadADM.username.get.value}' is not valid." + ) + ), + email = stringFormatter.toSparqlEncodedString( + userCreatePayloadADM.email.get.value, + errorFun = throw BadRequestException( + s"The supplied email: '${userCreatePayloadADM.email.get.value}' is not valid." + ) + ), + password = hashedPassword, + givenName = stringFormatter.toSparqlEncodedString( + userCreatePayloadADM.givenName.get.value, + errorFun = throw BadRequestException( + s"The supplied given name: '${userCreatePayloadADM.givenName.get.value}' is not valid." + ) + ), + familyName = stringFormatter.toSparqlEncodedString( + userCreatePayloadADM.familyName.get.value, + errorFun = throw BadRequestException( + s"The supplied family name: '${userCreatePayloadADM.familyName.get.value}' is not valid." + ) + ), + status = userCreatePayloadADM.status.get.value, + preferredLanguage = stringFormatter.toSparqlEncodedString( + userCreatePayloadADM.lang.get.value, + errorFun = throw BadRequestException( + s"The supplied language: '${userCreatePayloadADM.lang.get.value}' is not valid." + ) + ), + systemAdmin = userCreatePayloadADM.systemAdmin.get.value + ) + .toString _ = log.debug(s"createNewUser: $createNewUserSparqlString") createNewUserResponse <- (storeManager ? SparqlUpdateRequest(createNewUserSparqlString)) - .mapTo[SparqlUpdateResponse] + .mapTo[SparqlUpdateResponse] // try to retrieve newly created user (will also add to cache) maybeNewUserADM: Option[UserADM] <- getSingleUserADM( - identifier = UserIdentifierADM(maybeIri = Some(userIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser, - userInformationType = UserInformationTypeADM.FULL, - skipCache = true - ) + identifier = UserIdentifierADM(maybeIri = Some(userIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser, + userInformationType = UserInformationTypeADM.FULL, + skipCache = true + ) // check to see if we could retrieve the new user newUserADM = @@ -1779,17 +1787,17 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ) // create the user operation response - _ = log.debug("createNewUserADM - created new user: {}", newUserADM) + _ = log.debug("createNewUserADM - created new user: {}", newUserADM) userOperationResponseADM = UserOperationResponseADM(newUserADM.ofType(UserInformationTypeADM.RESTRICTED)) } yield userOperationResponseADM for { // run user creation with an global IRI lock taskResult <- IriLocker.runWithIriLock( - apiRequestID, - USERS_GLOBAL_LOCK_IRI, - () => createNewUserTask(userCreatePayloadADM) - ) + apiRequestID, + USERS_GLOBAL_LOCK_IRI, + () => createNewUserTask(userCreatePayloadADM) + ) } yield taskResult } @@ -1842,31 +1850,32 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde ): Future[Option[UserADM]] = for { sparqlQueryString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .getUsers( - triplestore = settings.triplestoreType, - maybeIri = identifier.toIriOption, - maybeUsername = identifier.toUsernameOption, - maybeEmail = identifier.toEmailOption - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .getUsers( + triplestore = settings.triplestoreType, + maybeIri = identifier.toIriOption, + maybeUsername = identifier.toUsernameOption, + maybeEmail = identifier.toEmailOption + ) + .toString() + ) userQueryResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparqlQueryString, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] - - maybeUserADM: Option[UserADM] <- if (userQueryResponse.statements.nonEmpty) { - log.debug("getUserFromTriplestore - triplestore hit for: {}", identifier) - statements2UserADM( - statements = userQueryResponse.statements.head, - featureFactoryConfig = featureFactoryConfig - ) - } else { - log.debug("getUserFromTriplestore - no triplestore hit for: {}", identifier) - FastFuture.successful(None) - } + sparql = sparqlQueryString, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] + + maybeUserADM: Option[UserADM] <- + if (userQueryResponse.statements.nonEmpty) { + log.debug("getUserFromTriplestore - triplestore hit for: {}", identifier) + statements2UserADM( + statements = userQueryResponse.statements.head, + featureFactoryConfig = featureFactoryConfig + ) + } else { + log.debug("getUserFromTriplestore - no triplestore hit for: {}", identifier) + FastFuture.successful(None) + } } yield maybeUserADM /** @@ -1883,7 +1892,7 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // log.debug("statements2UserADM - statements: {}", statements) - val userIri: IRI = statements._1.toString + val userIri: IRI = statements._1.toString val propsMap: Map[SmartIri, Seq[LiteralV2]] = statements._2 // log.debug("statements2UserADM - userIri: {}", userIri) @@ -1919,99 +1928,98 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { /* get the user's permission profile from the permissions responder */ permissionData <- (responderManager ? PermissionDataGetADM( - projectIris = projectIris, - groupIris = groupIris, - isInProjectAdminGroups = isInProjectAdminGroups, - isInSystemAdminGroup = isInSystemAdminGroup, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[PermissionsDataADM] + projectIris = projectIris, + groupIris = groupIris, + isInProjectAdminGroups = isInProjectAdminGroups, + isInSystemAdminGroup = isInSystemAdminGroup, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[PermissionsDataADM] maybeGroupFutures: Seq[Future[Option[GroupADM]]] = groupIris.map { groupIri => - (responderManager ? GroupGetADM( - groupIri = groupIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[GroupADM]] - } + (responderManager ? GroupGetADM( + groupIri = groupIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[GroupADM]] + } maybeGroups: Seq[Option[GroupADM]] <- Future.sequence(maybeGroupFutures) - groups: Seq[GroupADM] = maybeGroups.flatten + groups: Seq[GroupADM] = maybeGroups.flatten // _ = log.debug("statements2UserADM - groups: {}", MessageUtil.toSource(groups)) maybeProjectFutures: Seq[Future[Option[ProjectADM]]] = projectIris.map { projectIri => - (responderManager ? ProjectGetADM( - ProjectIdentifierADM(maybeIri = Some(projectIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = - KnoraSystemInstances.Users.SystemUser - )).mapTo[Option[ProjectADM]] - } + (responderManager ? ProjectGetADM( + ProjectIdentifierADM(maybeIri = Some(projectIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = KnoraSystemInstances.Users.SystemUser + )).mapTo[Option[ProjectADM]] + } maybeProjects: Seq[Option[ProjectADM]] <- Future.sequence(maybeProjectFutures) - projects: Seq[ProjectADM] = maybeProjects.flatten + projects: Seq[ProjectADM] = maybeProjects.flatten // _ = log.debug("statements2UserADM - projects: {}", MessageUtil.toSource(projects)) /* construct the user profile from the different parts */ user = UserADM( - id = userIri, - username = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.Username.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'username' defined.") - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - email = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.Email.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'email' defined.") - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - password = propsMap - .get(OntologyConstants.KnoraAdmin.Password.toSmartIri) - .map(_.head.asInstanceOf[StringLiteralV2].value), - token = None, - givenName = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.GivenName.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'givenName' defined.") - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - familyName = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.FamilyName.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'familyName' defined.") - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - status = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.Status.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'status' defined.") - ) - .head - .asInstanceOf[BooleanLiteralV2] - .value, - lang = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.PreferredLanguage.toSmartIri, - throw InconsistentRepositoryDataException(s"User: $userIri has no 'preferredLanguage' defined.") - ) - .head - .asInstanceOf[StringLiteralV2] - .value, - groups = groups, - projects = projects, - sessionId = None, - permissions = permissionData - ) + id = userIri, + username = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Username.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'username' defined.") + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + email = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Email.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'email' defined.") + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + password = propsMap + .get(OntologyConstants.KnoraAdmin.Password.toSmartIri) + .map(_.head.asInstanceOf[StringLiteralV2].value), + token = None, + givenName = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.GivenName.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'givenName' defined.") + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + familyName = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.FamilyName.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'familyName' defined.") + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + status = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Status.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'status' defined.") + ) + .head + .asInstanceOf[BooleanLiteralV2] + .value, + lang = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.PreferredLanguage.toSmartIri, + throw InconsistentRepositoryDataException(s"User: $userIri has no 'preferredLanguage' defined.") + ) + .head + .asInstanceOf[StringLiteralV2] + .value, + groups = groups, + projects = projects, + sessionId = None, + permissions = permissionData + ) // _ = log.debug(s"statements2UserADM - user: {}", user.toString) result: Option[UserADM] = Some(user) @@ -2035,7 +2043,7 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // _ = log.debug("userExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] - result = checkUserExistsResponse.result + result = checkUserExistsResponse.result } yield result @@ -2062,10 +2070,10 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { askString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .checkUserExistsByUsername(username = username.value) - .toString - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .checkUserExistsByUsername(username = username.value) + .toString + ) // _ = log.debug("userExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] @@ -2095,10 +2103,10 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde for { askString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .checkUserExistsByEmail(email = email.value) - .toString - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .checkUserExistsByEmail(email = email.value) + .toString + ) // _ = log.debug("userExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] @@ -2117,14 +2125,14 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde private def projectExists(projectIri: IRI): Future[Boolean] = for { askString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .checkProjectExistsByIri(projectIri = projectIri) - .toString - ) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .checkProjectExistsByIri(projectIri = projectIri) + .toString + ) // _ = log.debug("projectExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] - result = checkUserExistsResponse.result + result = checkUserExistsResponse.result } yield result @@ -2143,7 +2151,7 @@ class UsersResponderADM(responderData: ResponderData) extends Responder(responde // _ = log.debug("groupExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] - result = checkUserExistsResponse.result + result = checkUserExistsResponse.result } yield result diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/CkanResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/CkanResponderV1.scala index 7732a3f7fb..3659128d67 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/CkanResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/CkanResponderV1.scala @@ -48,30 +48,32 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} /** - * This responder is used by the Ckan route, for serving data to the Ckan harverster, which is published - * under http://data.humanities.ch - */ + * This responder is used by the Ckan route, for serving data to the Ckan harverster, which is published + * under http://data.humanities.ch + */ class CkanResponderV1(responderData: ResponderData) extends Responder(responderData) { /** - * A user representing the Knora API server, used in those cases where a user is required. - */ + * A user representing the Knora API server, used in those cases where a user is required. + */ private val systemUser = KnoraSystemInstances.Users.SystemUser.asUserProfileV1 /** - * Receives a message extending [[CkanResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message extending [[CkanResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: CkanResponderRequestV1) = msg match { case CkanRequestV1(projects, limit, info, featureFactoryConfig, userProfile) => getCkanResponseV1(projects, limit, info, featureFactoryConfig, userProfile) case other => handleUnexpectedMessage(other, log, this.getClass.getName) } - private def getCkanResponseV1(project: Option[Seq[String]], - limit: Option[Int], - info: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[CkanResponseV1] = { + private def getCkanResponseV1( + project: Option[Seq[String]], + limit: Option[Int], + info: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[CkanResponseV1] = { log.debug("Ckan Endpoint:") log.debug(s"Project: $project") @@ -134,17 +136,19 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD /////////////////////////////////////////////////////////////////////////////////////////// /** - * Dokubib specific Ckan export stuff - * - * @param pinfo - * @param limit - * @param userProfile - * @return - */ - private def getDokubibCkanProject(pinfo: ProjectInfoV1, - limit: Option[Int], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[CkanProjectV1] = { + * Dokubib specific Ckan export stuff + * + * @param pinfo + * @param limit + * @param userProfile + * @return + */ + private def getDokubibCkanProject( + pinfo: ProjectInfoV1, + limit: Option[Int], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[CkanProjectV1] = { /* - datasets @@ -175,23 +179,23 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD ) bilderMitProps <- bilderMitPropsFuture - dataset = bilderMitProps.map { - case (iri, info, props) => - val infoMap = flattenInfo(info) - val propsMap = flattenProps(props) - CkanProjectDatasetV1( - ckan_title = propsMap.getOrElse("Description", ""), - ckan_tags = propsMap.getOrElse("Title", "").split("/").map(_.trim), - files = Vector( - CkanProjectDatasetFileV1( - ckan_title = propsMap.getOrElse("preview_loc_origname", ""), - data_url = "http://localhost:3333/v1/assets/" + URLEncoder.encode(iri, "UTF-8"), - data_mimetype = "", - source_url = "http://salsah.org/resources/" + URLEncoder.encode(iri, "UTF-8"), - source_mimetype = "" - )), - other_props = propsMap - ) + dataset = bilderMitProps.map { case (iri, info, props) => + val infoMap = flattenInfo(info) + val propsMap = flattenProps(props) + CkanProjectDatasetV1( + ckan_title = propsMap.getOrElse("Description", ""), + ckan_tags = propsMap.getOrElse("Title", "").split("/").map(_.trim), + files = Vector( + CkanProjectDatasetFileV1( + ckan_title = propsMap.getOrElse("preview_loc_origname", ""), + data_url = "http://localhost:3333/v1/assets/" + URLEncoder.encode(iri, "UTF-8"), + data_mimetype = "", + source_url = "http://salsah.org/resources/" + URLEncoder.encode(iri, "UTF-8"), + source_mimetype = "" + ) + ), + other_props = propsMap + ) } } yield dataset @@ -203,12 +207,12 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD } /** - * Get all Bilder IRIs for Dokubib - * - * @param projectIri - * @param limit - * @return - */ + * Get all Bilder IRIs for Dokubib + * + * @param projectIri + * @param limit + * @return + */ private def getDokubibBilderIRIs(projectIri: IRI, limit: Option[Int]): Future[Seq[IRI]] = { implicit val timeout = Timeout(180.seconds) @@ -217,7 +221,8 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt .ckanDokubib(settings.triplestoreType, projectIri, limit) - .toString()) + .toString() + ) response <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] responseRows: Seq[VariableResultsRow] = response.results.bindings @@ -237,18 +242,20 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD /////////////////////////////////////////////////////////////////////////////////////////// /** - * Incunabula specific Ckan stuff - * - * @param pinfo - * @param limit - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile - * @return - */ - private def getIncunabulaCkanProject(pinfo: ProjectInfoV1, - limit: Option[Int], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[CkanProjectV1] = { + * Incunabula specific Ckan stuff + * + * @param pinfo + * @param limit + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile + * @return + */ + private def getIncunabulaCkanProject( + pinfo: ProjectInfoV1, + limit: Option[Int], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[CkanProjectV1] = { /* - datasets @@ -263,56 +270,55 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD val pIri = pinfo.id val ckanPInfo = - CkanProjectInfoV1(shortname = pinfo.shortname, - longname = pinfo.longname.getOrElse(pinfo.shortname), - ckan_tags = Vector("Kunstgeschichte"), - ckan_license_id = "CC-BY-4.0") + CkanProjectInfoV1( + shortname = pinfo.shortname, + longname = pinfo.longname.getOrElse(pinfo.shortname), + ckan_tags = Vector("Kunstgeschichte"), + ckan_license_id = "CC-BY-4.0" + ) // get book and page IRIs in project val booksWithPagesFuture = getIncunabulaBooksWithPagesIRIs(pIri, limit) val bookDatasetsFuture = booksWithPagesFuture.flatMap { singleBook => - val bookDataset = singleBook map { - case (bookIri: IRI, pageIris: Seq[IRI]) => - val bookResourceFuture = getResource( - iri = bookIri, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile - ) + val bookDataset = singleBook map { case (bookIri: IRI, pageIris: Seq[IRI]) => + val bookResourceFuture = getResource( + iri = bookIri, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile + ) - bookResourceFuture flatMap { - case (bIri, bInfo, bProps) => - val bInfoMap = flattenInfo(bInfo) - val bPropsMap = flattenProps(bProps) - val files = pageIris map { pageIri => - getResource( - iri = pageIri, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile - ) map { - case (pIri, pInfo, pProps) => - val pInfoMap = flattenInfo(pInfo) - val pPropsMap = flattenProps(pProps) - CkanProjectDatasetFileV1( - ckan_title = pPropsMap.getOrElse("Page identifier", ""), - ckan_description = Some(pPropsMap.getOrElse("Beschreibung (Richtext)", "")), - data_url = "http://localhost:3333/v1/assets/" + URLEncoder.encode(pIri, "UTF-8"), - data_mimetype = "", - source_url = "http://salsah.org/resources/" + URLEncoder.encode(pIri, "UTF-8"), - source_mimetype = "", - other_props = Some(pPropsMap) - ) - } - } - Future.sequence(files) map { filesList => - CkanProjectDatasetV1( - ckan_title = bPropsMap.getOrElse("Title", ""), - ckan_tags = Vector("Kunstgeschichte"), - files = filesList, - other_props = bPropsMap - ) - } + bookResourceFuture flatMap { case (bIri, bInfo, bProps) => + val bInfoMap = flattenInfo(bInfo) + val bPropsMap = flattenProps(bProps) + val files = pageIris map { pageIri => + getResource( + iri = pageIri, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile + ) map { case (pIri, pInfo, pProps) => + val pInfoMap = flattenInfo(pInfo) + val pPropsMap = flattenProps(pProps) + CkanProjectDatasetFileV1( + ckan_title = pPropsMap.getOrElse("Page identifier", ""), + ckan_description = Some(pPropsMap.getOrElse("Beschreibung (Richtext)", "")), + data_url = "http://localhost:3333/v1/assets/" + URLEncoder.encode(pIri, "UTF-8"), + data_mimetype = "", + source_url = "http://salsah.org/resources/" + URLEncoder.encode(pIri, "UTF-8"), + source_mimetype = "", + other_props = Some(pPropsMap) + ) + } + } + Future.sequence(files) map { filesList => + CkanProjectDatasetV1( + ckan_title = bPropsMap.getOrElse("Title", ""), + ckan_tags = Vector("Kunstgeschichte"), + files = filesList, + other_props = bPropsMap + ) } + } } Future.sequence(bookDataset.toVector) } @@ -324,27 +330,30 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD } /** - * Get all book IRIs for Incunabula - * - * @param projectIri - * @param limit - * @return - */ - private def getIncunabulaBooksWithPagesIRIs(projectIri: IRI, limit: Option[Int]): Future[Map[IRI, Seq[IRI]]] = { - + * Get all book IRIs for Incunabula + * + * @param projectIri + * @param limit + * @return + */ + private def getIncunabulaBooksWithPagesIRIs(projectIri: IRI, limit: Option[Int]): Future[Map[IRI, Seq[IRI]]] = for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt .ckanIncunabula(settings.triplestoreType, projectIri, limit) - .toString()) + .toString() + ) response <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] responseRows: Seq[VariableResultsRow] = response.results.bindings booksWithPages: Map[String, Seq[String]] = responseRows.groupBy(_.rowMap("book")).map { case (bookIri: String, rows: Seq[VariableResultsRow]) => - (bookIri, rows.map { - case row => row.rowMap("page") - }) + ( + bookIri, + rows.map { case row => + row.rowMap("page") + } + ) } result = limit match { @@ -353,23 +362,24 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD } } yield result - } /////////////////////////////////////////////////////////////////////////////////////////// // GENERAL /////////////////////////////////////////////////////////////////////////////////////////// /** - * Get detailed information about the projects - * - * @param projectNames - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile - * @return - */ - private def getProjectInfos(projectNames: Seq[String], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Seq[(String, ProjectInfoV1)]] = { + * Get detailed information about the projects + * + * @param projectNames + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile + * @return + */ + private def getProjectInfos( + projectNames: Seq[String], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Seq[(String, ProjectInfoV1)]] = Future.sequence { for { pName <- projectNames @@ -385,22 +395,22 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD } } yield result } - } /** - * Get IRIs of a certain type inside a certain project - * - * @param projectIri - * @param resType - * @param limit - * @param userProfile - * @return - */ - private def getIris(projectIri: IRI, - resType: String, - limit: Option[Int], - userProfile: UserProfileV1): Future[Seq[IRI]] = { - + * Get IRIs of a certain type inside a certain project + * + * @param projectIri + * @param resType + * @param limit + * @param userProfile + * @return + */ + private def getIris( + projectIri: IRI, + resType: String, + limit: Option[Int], + userProfile: UserProfileV1 + ): Future[Seq[IRI]] = for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -409,7 +419,8 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD projectIri = projectIri, resType = resType ) - .toString()) + .toString() + ) resourcesResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] resourcesResponseRows: Seq[VariableResultsRow] = resourcesResponse.results.bindings resIri = resourcesResponseRows.groupBy(_.rowMap("s")).keys.toVector @@ -418,20 +429,20 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD case None => resIri } } yield result - } /** - * Get all information there is about these resources - * - * @param iris - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile - * @return - */ - private def getResources(iris: Seq[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Seq[(String, Option[ResourceInfoV1], Option[PropsV1])]] = { - + * Get all information there is about these resources + * + * @param iris + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile + * @return + */ + private def getResources( + iris: Seq[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Seq[(String, Option[ResourceInfoV1], Option[PropsV1])]] = Future.sequence { for { iri <- iris @@ -443,19 +454,20 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD ) } yield resource } - } /** - * Get all information there is about this one resource - * - * @param iri - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile - * @return - */ - private def getResource(iri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[(String, Option[ResourceInfoV1], Option[PropsV1])] = { + * Get all information there is about this one resource + * + * @param iri + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile + * @return + */ + private def getResource( + iri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[(String, Option[ResourceInfoV1], Option[PropsV1])] = { val resourceFullResponseFuture = (responderManager ? ResourceFullGetRequestV1( iri = iri, @@ -463,16 +475,15 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD userADM = userProfile )).mapTo[ResourceFullResponseV1] - resourceFullResponseFuture map { - case ResourceFullResponseV1(resInfo, _, props, _, _) => (iri, resInfo, props) + resourceFullResponseFuture map { case ResourceFullResponseV1(resInfo, _, props, _, _) => + (iri, resInfo, props) } } private def flattenInfo(maybeInfo: Option[ResourceInfoV1]): Map[String, String] = { - def maybeTuple(key: String, maybeValue: Option[String]): Option[(String, String)] = { + def maybeTuple(key: String, maybeValue: Option[String]): Option[(String, String)] = maybeValue.map(value => (key, value)) - } maybeInfo match { case None => Map() @@ -491,45 +502,43 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD } - private def flattenLocation(location: Option[LocationV1]): Seq[Option[(String, String)]] = { + private def flattenLocation(location: Option[LocationV1]): Seq[Option[(String, String)]] = location match { case None => Vector(None) case Some(loc) => Vector(Some(("preview_loc_origname", loc.origname.getOrElse("")))) } - } - - private def flattenProps(props: Option[PropsV1]): Map[String, String] = { + private def flattenProps(props: Option[PropsV1]): Map[String, String] = if (props.nonEmpty) { val properties = props.get.properties - val propMap = properties.foldLeft(Map.empty[String, String]) { - case (acc, propertyV1: PropertyV1) => - val label = propertyV1.label.getOrElse("") + val propMap = properties.foldLeft(Map.empty[String, String]) { case (acc, propertyV1: PropertyV1) => + val label = propertyV1.label.getOrElse("") - val values: Seq[String] = propertyV1.valuetype_id.get match { - case OntologyConstants.KnoraBase.TextValue => - propertyV1.values.map(literal => textValue2String(literal.asInstanceOf[TextValueV1])) + val values: Seq[String] = propertyV1.valuetype_id.get match { + case OntologyConstants.KnoraBase.TextValue => + propertyV1.values.map(literal => textValue2String(literal.asInstanceOf[TextValueV1])) - case OntologyConstants.KnoraBase.DateValue => - propertyV1.values.map(literal => dateValue2String(literal.asInstanceOf[DateValueV1])) + case OntologyConstants.KnoraBase.DateValue => + propertyV1.values.map(literal => dateValue2String(literal.asInstanceOf[DateValueV1])) - case OntologyConstants.KnoraBase.ListValue => - propertyV1.values.map(literal => - listValue2String(literal.asInstanceOf[HierarchicalListValueV1], responderManager)) + case OntologyConstants.KnoraBase.ListValue => + propertyV1.values.map(literal => + listValue2String(literal.asInstanceOf[HierarchicalListValueV1], responderManager) + ) - case OntologyConstants.KnoraBase.Resource => // TODO: this could actually be a subclass of knora-base:Resource. - propertyV1.values.map(literal => resourceValue2String(literal.asInstanceOf[LinkV1], responderManager)) + case OntologyConstants.KnoraBase.Resource => // TODO: this could actually be a subclass of knora-base:Resource. + propertyV1.values.map(literal => resourceValue2String(literal.asInstanceOf[LinkV1], responderManager)) - case _ => Vector() - } + case _ => Vector() + } - if (label.nonEmpty && values.nonEmpty) { - acc + (label -> values.mkString(",")) - } else { - acc - } + if (label.nonEmpty && values.nonEmpty) { + acc + (label -> values.mkString(",")) + } else { + acc + } } propMap @@ -537,35 +546,29 @@ class CkanResponderV1(responderData: ResponderData) extends Responder(responderD Map.empty[String, String] } - } - - private def textValue2String(text: TextValueV1): String = { + private def textValue2String(text: TextValueV1): String = text.utf8str - } - - private def dateValue2String(date: DateValueV1): String = { + private def dateValue2String(date: DateValueV1): String = if (date.dateval1 == date.dateval2) { date.dateval1.toString + " " + date.era1 + ", " + date.calendar.toString + " " + date.era2 } else { date.dateval1.toString + " " + date.era1 + ", " + date.dateval2 + ", " + date.calendar.toString + " " + date.era2 } - } private def listValue2String(list: HierarchicalListValueV1, responderManager: ActorRef): String = { val resultFuture = responderManager ? NodePathGetRequestV1(list.hierarchicalListIri, systemUser) val nodePath = Await.result(resultFuture, Duration(3, SECONDS)).asInstanceOf[NodePathGetResponseV1] - val labels = nodePath.nodelist map { - case element => element.label.getOrElse("") + val labels = nodePath.nodelist map { case element => + element.label.getOrElse("") } labels.mkString(" / ") } - private def resourceValue2String(resource: LinkV1, responderManager: ActorRef): String = { + private def resourceValue2String(resource: LinkV1, responderManager: ActorRef): String = resource.valueLabel.get - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/ListsResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/ListsResponderV1.scala index b4a8169ed8..74297a2206 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/ListsResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/ListsResponderV1.scala @@ -34,13 +34,13 @@ import scala.annotation.tailrec import scala.concurrent.Future /** - * A responder that returns information about hierarchical lists. - */ + * A responder that returns information about hierarchical lists. + */ class ListsResponderV1(responderData: ResponderData) extends Responder(responderData) { /** - * Receives a message of type [[ListsResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message of type [[ListsResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: ListsResponderRequestV1) = msg match { case HListGetRequestV1(listIri, userProfile) => listGetRequestV1(listIri, userProfile, PathType.HList) case SelectionGetRequestV1(listIri, userProfile) => listGetRequestV1(listIri, userProfile, PathType.Selection) @@ -49,19 +49,20 @@ class ListsResponderV1(responderData: ResponderData) extends Responder(responder } /** - * Retrieves a list from the triplestore and returns it as a [[ListGetResponseV1]]. - * Due to compatibility with the old, crappy SALSAH-API, "hlists" and "selection" have to be differentiated in the response - * [[ListGetResponseV1]] is the abstract super class of [[HListGetResponseV1]] and [[SelectionGetResponseV1]] - * - * @param rootNodeIri the Iri if the root node of the list to be queried. - * @param userProfile the profile of the user making the request. - * @param pathType the type of the list (HList or Selection). - * @return a [[ListGetResponseV1]]. - */ - def listGetRequestV1(rootNodeIri: IRI, - userProfile: UserProfileV1, - pathType: PathType.Value): Future[ListGetResponseV1] = { - + * Retrieves a list from the triplestore and returns it as a [[ListGetResponseV1]]. + * Due to compatibility with the old, crappy SALSAH-API, "hlists" and "selection" have to be differentiated in the response + * [[ListGetResponseV1]] is the abstract super class of [[HListGetResponseV1]] and [[SelectionGetResponseV1]] + * + * @param rootNodeIri the Iri if the root node of the list to be queried. + * @param userProfile the profile of the user making the request. + * @param pathType the type of the list (HList or Selection). + * @return a [[ListGetResponseV1]]. + */ + def listGetRequestV1( + rootNodeIri: IRI, + userProfile: UserProfileV1, + pathType: PathType.Value + ): Future[ListGetResponseV1] = for { maybeChildren <- listGetV1(rootNodeIri, userProfile) @@ -77,39 +78,39 @@ class ListsResponderV1(responderData: ResponderData) extends Responder(responder } } yield result - } /** - * Retrieves a list from the triplestore and returns it as a sequence of child nodes. - * - * @param rootNodeIri the Iri of the root node of the list to be queried. - * @param userProfile the profile of the user making the request. - * @return a sequence of [[ListNodeV1]]. - */ + * Retrieves a list from the triplestore and returns it as a sequence of child nodes. + * + * @param rootNodeIri the Iri of the root node of the list to be queried. + * @param userProfile the profile of the user making the request. + * @return a sequence of [[ListNodeV1]]. + */ private def listGetV1(rootNodeIri: IRI, userProfile: UserProfileV1): Future[Seq[ListNodeV1]] = { /** - * Compares the `position`-values of two nodes - * - * @param list1 node in a list - * @param list2 node in the same list - * @return true if the `position` of list1 is lower than the one of list2 - */ - def orderNodes(list1: ListNodeV1, list2: ListNodeV1): Boolean = { + * Compares the `position`-values of two nodes + * + * @param list1 node in a list + * @param list2 node in the same list + * @return true if the `position` of list1 is lower than the one of list2 + */ + def orderNodes(list1: ListNodeV1, list2: ListNodeV1): Boolean = list1.position < list2.position - } /** - * This function recursively transforms SPARQL query results representing a hierarchical list into a [[ListNodeV1]]. - * - * @param nodeIri the IRI of the node to be created. - * @param groupedByNodeIri a [[Map]] in which each key is the IRI of a node in the hierarchical list, and each value is a [[Seq]] - * of SPARQL query results representing that node's children. - * @return a [[ListNodeV1]]. - */ - def createHierarchicalListV1(nodeIri: IRI, - groupedByNodeIri: Map[IRI, Seq[VariableResultsRow]], - level: Int): ListNodeV1 = { + * This function recursively transforms SPARQL query results representing a hierarchical list into a [[ListNodeV1]]. + * + * @param nodeIri the IRI of the node to be created. + * @param groupedByNodeIri a [[Map]] in which each key is the IRI of a node in the hierarchical list, and each value is a [[Seq]] + * of SPARQL query results representing that node's children. + * @return a [[ListNodeV1]]. + */ + def createHierarchicalListV1( + nodeIri: IRI, + groupedByNodeIri: Map[IRI, Seq[VariableResultsRow]], + level: Int + ): ListNodeV1 = { val childRows = groupedByNodeIri(nodeIri) /* @@ -167,47 +168,50 @@ class ListsResponderV1(responderData: ResponderData) extends Responder(responder // Group the results to map each node to the SPARQL query results representing its children. groupedByNodeIri: Map[IRI, Seq[VariableResultsRow]] = listQueryResponse.results.bindings.groupBy(row => - row.rowMap("node")) + row.rowMap("node") + ) rootNodeChildren = groupedByNodeIri.getOrElse(rootNodeIri, Seq.empty[VariableResultsRow]) - children: Seq[ListNodeV1] = if (!rootNodeChildren.head.rowMap.contains("child")) { - // The root node has no children, so we return an empty list. - Seq.empty[ListNodeV1] - } else { - // Process each child of the root node. - rootNodeChildren - .map { childRow => + children: Seq[ListNodeV1] = + if (!rootNodeChildren.head.rowMap.contains("child")) { + // The root node has no children, so we return an empty list. + Seq.empty[ListNodeV1] + } else { + // Process each child of the root node. + rootNodeChildren.map { childRow => createHierarchicalListV1(childRow.rowMap("child"), groupedByNodeIri, 0) } - .sortWith(orderNodes) - } + .sortWith(orderNodes) + } } yield children } /** - * Provides the path to a particular hierarchical list node. - * - * @param queryNodeIri the IRI of the node whose path is to be queried. - * @param userProfile the profile of the user making the request. - */ + * Provides the path to a particular hierarchical list node. + * + * @param queryNodeIri the IRI of the node whose path is to be queried. + * @param userProfile the profile of the user making the request. + */ private def getNodePathResponseV1(queryNodeIri: IRI, userProfile: UserProfileV1): Future[NodePathGetResponseV1] = { /** - * Recursively constructs the path to a node. - * - * @param node the IRI of the node whose path is to be constructed. - * @param nodeMap a [[Map]] of node IRIs to query result row data, in the format described below. - * @param parentMap a [[Map]] of child node IRIs to parent node IRIs. - * @param path the path constructed so far. - * @return the complete path to `node`. - */ + * Recursively constructs the path to a node. + * + * @param node the IRI of the node whose path is to be constructed. + * @param nodeMap a [[Map]] of node IRIs to query result row data, in the format described below. + * @param parentMap a [[Map]] of child node IRIs to parent node IRIs. + * @param path the path constructed so far. + * @return the complete path to `node`. + */ @tailrec - def makePath(node: IRI, - nodeMap: Map[IRI, Map[String, String]], - parentMap: Map[IRI, IRI], - path: Seq[NodePathElementV1]): Seq[NodePathElementV1] = { + def makePath( + node: IRI, + nodeMap: Map[IRI, Map[String, String]], + parentMap: Map[IRI, IRI], + path: Seq[NodePathElementV1] + ): Seq[NodePathElementV1] = { // Get the details of the node. val nodeData = nodeMap(node) @@ -267,16 +271,14 @@ class ListsResponderV1(responderData: ResponderData) extends Responder(responder }.toMap // A Map of child node IRIs to parent node IRIs. - parentMap: Map[IRI, IRI] = nodePathResponse.results.bindings.foldLeft(Map.empty[IRI, IRI]) { - case (acc, row) => - row.rowMap.get("child") match { - case Some(child) => acc + (child -> row.rowMap("node")) - case None => acc - } + parentMap: Map[IRI, IRI] = nodePathResponse.results.bindings.foldLeft(Map.empty[IRI, IRI]) { case (acc, row) => + row.rowMap.get("child") match { + case Some(child) => acc + (child -> row.rowMap("node")) + case None => acc + } } - } yield - NodePathGetResponseV1( - nodelist = makePath(queryNodeIri, nodeMap, parentMap, Nil) - ) + } yield NodePathGetResponseV1( + nodelist = makePath(queryNodeIri, nodeMap, parentMap, Nil) + ) } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala index df4480896d..253a0ea11c 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala @@ -43,18 +43,18 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import scala.concurrent.Future /** - * Handles requests for information about ontology entities. - * - * All ontology data is loaded and cached when the application starts. To refresh the cache, you currently have to restart - * the application. - */ + * Handles requests for information about ontology entities. + * + * All ontology data is loaded and cached when the application starts. To refresh the cache, you currently have to restart + * the application. + */ class OntologyResponderV1(responderData: ResponderData) extends Responder(responderData) { private val valueUtilV1 = new ValueUtilV1(settings) /** - * Receives a message extending [[OntologyResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message extending [[OntologyResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: OntologyResponderRequestV1) = msg match { case LoadOntologiesRequestV1(featureFactoryConfig, userProfile) => loadOntologies(featureFactoryConfig, userProfile) case EntityInfoGetRequestV1(resourceIris, propertyIris, userProfile) => @@ -81,15 +81,16 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon } /** - * Loads and caches all ontology information. - * - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[LoadOntologiesResponse]]. - */ - private def loadOntologies(featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[LoadOntologiesResponse] = { - + * Loads and caches all ontology information. + * + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[LoadOntologiesResponse]]. + */ + private def loadOntologies( + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[LoadOntologiesResponse] = for { // forward the request to the v2 ontologies responder _: SuccessResponseV2 <- (responderManager ? LoadOntologiesRequestV2( @@ -97,196 +98,216 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon requestingUser = userProfile )).mapTo[SuccessResponseV2] } yield LoadOntologiesResponse() - } /** - * Given a list of resource IRIs and a list of property IRIs (ontology entities), returns an [[EntityInfoGetResponseV1]] describing both resource and property entities. - * - * @param resourceClassIris the IRIs of the resource entities to be queried. - * @param propertyIris the IRIs of the property entities to be queried. - * @param userProfile the profile of the user making the request. - * @return an [[EntityInfoGetResponseV1]]. - */ - private def getEntityInfoResponseV1(resourceClassIris: Set[IRI] = Set.empty[IRI], - propertyIris: Set[IRI] = Set.empty[IRI], - userProfile: UserADM): Future[EntityInfoGetResponseV1] = { + * Given a list of resource IRIs and a list of property IRIs (ontology entities), returns an [[EntityInfoGetResponseV1]] describing both resource and property entities. + * + * @param resourceClassIris the IRIs of the resource entities to be queried. + * @param propertyIris the IRIs of the property entities to be queried. + * @param userProfile the profile of the user making the request. + * @return an [[EntityInfoGetResponseV1]]. + */ + private def getEntityInfoResponseV1( + resourceClassIris: Set[IRI] = Set.empty[IRI], + propertyIris: Set[IRI] = Set.empty[IRI], + userProfile: UserADM + ): Future[EntityInfoGetResponseV1] = for { response: EntityInfoGetResponseV2 <- (responderManager ? EntityInfoGetRequestV2( resourceClassIris.map(_.toSmartIri), propertyIris.map(_.toSmartIri), - userProfile)).mapTo[EntityInfoGetResponseV2] - } yield - EntityInfoGetResponseV1( - resourceClassInfoMap = ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(response.classInfoMap), - propertyInfoMap = ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(response.propertyInfoMap) - ) - } + userProfile + )).mapTo[EntityInfoGetResponseV2] + } yield EntityInfoGetResponseV1( + resourceClassInfoMap = ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(response.classInfoMap), + propertyInfoMap = ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(response.propertyInfoMap) + ) /** - * Given a list of standoff class IRIs and a list of property IRIs (ontology entities), returns an [[StandoffEntityInfoGetResponseV1]] describing both resource and property entities. - * - * @param standoffClassIris the IRIs of the resource entities to be queried. - * @param standoffPropertyIris the IRIs of the property entities to be queried. - * @param userProfile the profile of the user making the request. - * @return an [[EntityInfoGetResponseV1]]. - */ - private def getStandoffEntityInfoResponseV1(standoffClassIris: Set[IRI] = Set.empty[IRI], - standoffPropertyIris: Set[IRI] = Set.empty[IRI], - userProfile: UserADM): Future[StandoffEntityInfoGetResponseV1] = { + * Given a list of standoff class IRIs and a list of property IRIs (ontology entities), returns an [[StandoffEntityInfoGetResponseV1]] describing both resource and property entities. + * + * @param standoffClassIris the IRIs of the resource entities to be queried. + * @param standoffPropertyIris the IRIs of the property entities to be queried. + * @param userProfile the profile of the user making the request. + * @return an [[EntityInfoGetResponseV1]]. + */ + private def getStandoffEntityInfoResponseV1( + standoffClassIris: Set[IRI] = Set.empty[IRI], + standoffPropertyIris: Set[IRI] = Set.empty[IRI], + userProfile: UserADM + ): Future[StandoffEntityInfoGetResponseV1] = for { response: StandoffEntityInfoGetResponseV2 <- (responderManager ? StandoffEntityInfoGetRequestV2( standoffClassIris.map(_.toSmartIri), standoffPropertyIris.map(_.toSmartIri), - userProfile)).mapTo[StandoffEntityInfoGetResponseV2] + userProfile + )).mapTo[StandoffEntityInfoGetResponseV2] - } yield - StandoffEntityInfoGetResponseV1( - standoffClassInfoMap = ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(response.standoffClassInfoMap), - standoffPropertyInfoMap = ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(response.standoffPropertyInfoMap) - ) - } + } yield StandoffEntityInfoGetResponseV1( + standoffClassInfoMap = ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(response.standoffClassInfoMap), + standoffPropertyInfoMap = ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(response.standoffPropertyInfoMap) + ) /** - * Gets information about all standoff classes that are a subclass of a data type standoff class. - * - * @param userProfile the profile of the user making the request. - * @return a [[StandoffClassesWithDataTypeGetResponseV1]] - */ + * Gets information about all standoff classes that are a subclass of a data type standoff class. + * + * @param userProfile the profile of the user making the request. + * @return a [[StandoffClassesWithDataTypeGetResponseV1]] + */ private def getStandoffStandoffClassesWithDataTypeV1( - userProfile: UserADM): Future[StandoffClassesWithDataTypeGetResponseV1] = { + userProfile: UserADM + ): Future[StandoffClassesWithDataTypeGetResponseV1] = for { response: StandoffClassesWithDataTypeGetResponseV2 <- (responderManager ? StandoffClassesWithDataTypeGetRequestV2( - userProfile)).mapTo[StandoffClassesWithDataTypeGetResponseV2] - } yield - StandoffClassesWithDataTypeGetResponseV1( - standoffClassInfoMap = ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(response.standoffClassInfoMap)) - } + userProfile + )).mapTo[StandoffClassesWithDataTypeGetResponseV2] + } yield StandoffClassesWithDataTypeGetResponseV1( + standoffClassInfoMap = ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(response.standoffClassInfoMap) + ) /** - * Gets all standoff property entities. - * - * @param userProfile the profile of the user making the request. - * @return a [[StandoffAllPropertiesGetResponseV1]]. - */ - private def getAllStandoffPropertyEntities(userProfile: UserADM): Future[StandoffAllPropertiesGetResponseV1] = { + * Gets all standoff property entities. + * + * @param userProfile the profile of the user making the request. + * @return a [[StandoffAllPropertiesGetResponseV1]]. + */ + private def getAllStandoffPropertyEntities(userProfile: UserADM): Future[StandoffAllPropertiesGetResponseV1] = for { response: StandoffAllPropertyEntitiesGetResponseV2 <- (responderManager ? StandoffAllPropertyEntitiesGetRequestV2( - userProfile)).mapTo[StandoffAllPropertyEntitiesGetResponseV2] - } yield - StandoffAllPropertiesGetResponseV1( - standoffAllPropertiesInfoMap = - ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(response.standoffAllPropertiesEntityInfoMap)) - } + userProfile + )).mapTo[StandoffAllPropertyEntitiesGetResponseV2] + } yield StandoffAllPropertiesGetResponseV1( + standoffAllPropertiesInfoMap = + ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(response.standoffAllPropertiesEntityInfoMap) + ) /** - * Given the IRI of a resource type, returns a [[ResourceTypeResponseV1]] describing the resource type and its possible - * properties. - * - * @param resourceTypeIri the IRI of the resource type to be queried. - * @param userProfile the profile of the user making the request. - * @return a [[ResourceTypeResponseV1]]. - */ - private def getResourceTypeResponseV1(resourceTypeIri: String, - userProfile: UserADM): Future[ResourceTypeResponseV1] = { + * Given the IRI of a resource type, returns a [[ResourceTypeResponseV1]] describing the resource type and its possible + * properties. + * + * @param resourceTypeIri the IRI of the resource type to be queried. + * @param userProfile the profile of the user making the request. + * @return a [[ResourceTypeResponseV1]]. + */ + private def getResourceTypeResponseV1( + resourceTypeIri: String, + userProfile: UserADM + ): Future[ResourceTypeResponseV1] = { for { // Get all information about the resource type, including its property cardinalities. - resourceClassInfoResponse: EntityInfoGetResponseV1 <- getEntityInfoResponseV1(resourceClassIris = - Set(resourceTypeIri), - userProfile = userProfile) + resourceClassInfoResponse: EntityInfoGetResponseV1 <- getEntityInfoResponseV1( + resourceClassIris = Set(resourceTypeIri), + userProfile = userProfile + ) resourceClassInfo: ClassInfoV1 = resourceClassInfoResponse.resourceClassInfoMap .getOrElse(resourceTypeIri, throw NotFoundException(s"Resource class $resourceTypeIri not found")) // Get all information about those properties. propertyInfo: EntityInfoGetResponseV1 <- getEntityInfoResponseV1( propertyIris = resourceClassInfo.knoraResourceCardinalities.keySet, - userProfile = userProfile) + userProfile = userProfile + ) // Build the property definitions. - propertyDefinitions: Vector[PropertyDefinitionV1] = resourceClassInfo.knoraResourceCardinalities - .filterNot { - // filter out the properties that point to LinkValue objects - case (propertyIri, _) => - resourceClassInfo.linkValueProperties(propertyIri) || propertyIri == OntologyConstants.KnoraBase.HasStandoffLinkTo - } - .map { - case (propertyIri: IRI, cardinalityInfo: KnoraCardinalityInfo) => - propertyInfo.propertyInfoMap.get(propertyIri) match { - case Some(entityInfo: PropertyInfoV1) => - if (entityInfo.isLinkProp) { - // It is a linking prop: its valuetype_id is knora-base:LinkValue. - // It is restricted to the resource class that is given for knora-base:objectClassConstraint - // for the given property which goes in the attributes that will be read by the GUI. - - PropertyDefinitionV1( - id = propertyIri, - name = propertyIri, - label = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), - description = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), - vocabulary = entityInfo.ontologyIri, - occurrence = cardinalityInfo.cardinality.toString, - valuetype_id = OntologyConstants.KnoraBase.LinkValue, - attributes = valueUtilV1.makeAttributeString( + propertyDefinitions: Vector[PropertyDefinitionV1] = resourceClassInfo.knoraResourceCardinalities.filterNot { + // filter out the properties that point to LinkValue objects + case (propertyIri, _) => + resourceClassInfo.linkValueProperties( + propertyIri + ) || propertyIri == OntologyConstants.KnoraBase.HasStandoffLinkTo + }.map { case (propertyIri: IRI, cardinalityInfo: KnoraCardinalityInfo) => + propertyInfo.propertyInfoMap.get(propertyIri) match { + case Some(entityInfo: PropertyInfoV1) => + if (entityInfo.isLinkProp) { + // It is a linking prop: its valuetype_id is knora-base:LinkValue. + // It is restricted to the resource class that is given for knora-base:objectClassConstraint + // for the given property which goes in the attributes that will be read by the GUI. + + PropertyDefinitionV1( + id = propertyIri, + name = propertyIri, + label = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + description = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + vocabulary = entityInfo.ontologyIri, + occurrence = cardinalityInfo.cardinality.toString, + valuetype_id = OntologyConstants.KnoraBase.LinkValue, + attributes = valueUtilV1.makeAttributeString( + entityInfo + .getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute) + valueUtilV1 + .makeAttributeRestype( entityInfo - .getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute) + valueUtilV1 - .makeAttributeRestype( - entityInfo - .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse(throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint")))), - gui_name = entityInfo - .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) - .map(iri => SalsahGuiConversions.iri2SalsahGuiElement(iri)), - guiorder = cardinalityInfo.guiOrder - ) - - } else { - - PropertyDefinitionV1( - id = propertyIri, - name = propertyIri, - label = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), - description = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), - vocabulary = entityInfo.ontologyIri, - occurrence = cardinalityInfo.cardinality.toString, - valuetype_id = entityInfo - .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse(throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint")), - attributes = valueUtilV1.makeAttributeString( - entityInfo.getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute)), - gui_name = entityInfo - .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) - .map(iri => SalsahGuiConversions.iri2SalsahGuiElement(iri)), - guiorder = cardinalityInfo.guiOrder - ) - } - case None => - throw new InconsistentRepositoryDataException( - s"Resource type $resourceTypeIri is defined as having property $propertyIri, which doesn't exist") + .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) + .getOrElse( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + ) + ) + ), + gui_name = entityInfo + .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) + .map(iri => SalsahGuiConversions.iri2SalsahGuiElement(iri)), + guiorder = cardinalityInfo.guiOrder + ) + + } else { + + PropertyDefinitionV1( + id = propertyIri, + name = propertyIri, + label = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + description = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + vocabulary = entityInfo.ontologyIri, + occurrence = cardinalityInfo.cardinality.toString, + valuetype_id = entityInfo + .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) + .getOrElse( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + ), + attributes = valueUtilV1.makeAttributeString( + entityInfo.getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute) + ), + gui_name = entityInfo + .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) + .map(iri => SalsahGuiConversions.iri2SalsahGuiElement(iri)), + guiorder = cardinalityInfo.guiOrder + ) } + case None => + throw new InconsistentRepositoryDataException( + s"Resource type $resourceTypeIri is defined as having property $propertyIri, which doesn't exist" + ) } - .toVector + }.toVector .sortBy(_.guiorder) // Build the API response. resourceTypeResponse = ResourceTypeResponseV1( restype_info = ResTypeInfoV1( name = resourceTypeIri, - label = resourceClassInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), - description = resourceClassInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), + label = resourceClassInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + description = resourceClassInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), iconsrc = resourceClassInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon), properties = propertyDefinitions ) @@ -295,32 +316,33 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon } /** - * Checks whether a certain Knora resource or value class is a subclass of another class. - * - * @param checkSubClassRequest a [[CheckSubClassRequestV1]] - * @return a [[CheckSubClassResponseV1]]. - */ - private def checkSubClass(checkSubClassRequest: CheckSubClassRequestV1): Future[CheckSubClassResponseV1] = { + * Checks whether a certain Knora resource or value class is a subclass of another class. + * + * @param checkSubClassRequest a [[CheckSubClassRequestV1]] + * @return a [[CheckSubClassResponseV1]]. + */ + private def checkSubClass(checkSubClassRequest: CheckSubClassRequestV1): Future[CheckSubClassResponseV1] = for { response: CheckSubClassResponseV2 <- (responderManager ? CheckSubClassRequestV2( subClassIri = checkSubClassRequest.subClassIri.toSmartIri, superClassIri = checkSubClassRequest.superClassIri.toSmartIri, - checkSubClassRequest.userProfile)).mapTo[CheckSubClassResponseV2] + checkSubClassRequest.userProfile + )).mapTo[CheckSubClassResponseV2] } yield CheckSubClassResponseV1(response.isSubClass) - } /** - * Gets the IRIs of the subclasses of a resource class. - * - * @param getSubClassesRequest a [[SubClassesGetRequestV1]]. - * @return a [[SubClassesGetResponseV1]]. - */ - private def getSubClasses(getSubClassesRequest: SubClassesGetRequestV1): Future[SubClassesGetResponseV1] = { + * Gets the IRIs of the subclasses of a resource class. + * + * @param getSubClassesRequest a [[SubClassesGetRequestV1]]. + * @return a [[SubClassesGetResponseV1]]. + */ + private def getSubClasses(getSubClassesRequest: SubClassesGetRequestV1): Future[SubClassesGetResponseV1] = for { response: SubClassesGetResponseV2 <- (responderManager ? SubClassesGetRequestV2( getSubClassesRequest.resourceClassIri.toSmartIri, - getSubClassesRequest.userADM)).mapTo[SubClassesGetResponseV2] + getSubClassesRequest.userADM + )).mapTo[SubClassesGetResponseV2] subClasses = response.subClasses.map { subClassInfoV2 => SubClassInfoV1( @@ -330,20 +352,20 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon } } yield SubClassesGetResponseV1(subClasses) - } /** - * Returns information about ontology named graphs as a [[NamedGraphsResponseV1]]. - * - * @param projectIris the IRIs of the projects whose named graphs should be returned. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[NamedGraphsResponseV1]]. - */ - private def getNamedGraphs(projectIris: Set[IRI] = Set.empty[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[NamedGraphsResponseV1] = { - + * Returns information about ontology named graphs as a [[NamedGraphsResponseV1]]. + * + * @param projectIris the IRIs of the projects whose named graphs should be returned. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[NamedGraphsResponseV1]]. + */ + private def getNamedGraphs( + projectIris: Set[IRI] = Set.empty[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[NamedGraphsResponseV1] = for { projectsResponse <- (responderManager ? ProjectsGetRequestADM( featureFactoryConfig = featureFactoryConfig, @@ -352,7 +374,8 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon readOntologyMetadataV2 <- (responderManager ? OntologyMetadataGetByProjectRequestV2( projectIris = projectIris.map(_.toSmartIri), - requestingUser = userProfile)).mapTo[ReadOntologyMetadataV2] + requestingUser = userProfile + )).mapTo[ReadOntologyMetadataV2] projectsMap: Map[IRI, ProjectADM] = projectsResponse.projects.map { project => project.id -> project @@ -372,8 +395,8 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon NamedGraphV1( id = ontologyMetadata.ontologyIri.toString, shortname = project.shortname, - longname = project.longname.getOrElse( - throw InconsistentRepositoryDataException(s"Project ${project.id} has no longname")), + longname = project.longname + .getOrElse(throw InconsistentRepositoryDataException(s"Project ${project.id} has no longname")), description = project.description.headOption .getOrElse(throw InconsistentRepositoryDataException(s"Project ${project.id} has no description")) .toString, @@ -387,56 +410,57 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon vocabularies = namedGraphs ) } yield response - } /** - * Gets the [[NamedGraphEntityInfoV1]] for a named graph - * - * @param namedGraphIri the IRI of the named graph to query - * @param userProfile the profile of the user making the request. - * @return a [[NamedGraphEntityInfoV1]]. - */ - def getNamedGraphEntityInfoV1ForNamedGraph(namedGraphIri: IRI, - userProfile: UserADM): Future[NamedGraphEntityInfoV1] = { + * Gets the [[NamedGraphEntityInfoV1]] for a named graph + * + * @param namedGraphIri the IRI of the named graph to query + * @param userProfile the profile of the user making the request. + * @return a [[NamedGraphEntityInfoV1]]. + */ + def getNamedGraphEntityInfoV1ForNamedGraph(namedGraphIri: IRI, userProfile: UserADM): Future[NamedGraphEntityInfoV1] = for { response: OntologyKnoraEntitiesIriInfoV2 <- (responderManager ? OntologyKnoraEntityIrisGetRequestV2( namedGraphIri.toSmartIri, - userProfile)).mapTo[OntologyKnoraEntitiesIriInfoV2] + userProfile + )).mapTo[OntologyKnoraEntitiesIriInfoV2] classIrisForV1 = response.classIris.map(_.toString) -- OntologyConstants.KnoraBase.AbstractResourceClasses propertyIrisForV1 = response.propertyIris.map(_.toString) - OntologyConstants.KnoraBase.ResourceProperty - } yield - NamedGraphEntityInfoV1( - namedGraphIri = response.ontologyIri.toString, - resourceClasses = classIrisForV1, - propertyIris = propertyIrisForV1 - ) - } + } yield NamedGraphEntityInfoV1( + namedGraphIri = response.ontologyIri.toString, + resourceClasses = classIrisForV1, + propertyIris = propertyIrisForV1 + ) /** - * Gets all the resource classes and their properties for a named graph. - * - * @param namedGraphIriOption the IRI of the named graph or None if all the named graphs should be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return [[ResourceTypesForNamedGraphResponseV1]]. - */ - private def getResourceTypesForNamedGraph(namedGraphIriOption: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ResourceTypesForNamedGraphResponseV1] = { + * Gets all the resource classes and their properties for a named graph. + * + * @param namedGraphIriOption the IRI of the named graph or None if all the named graphs should be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return [[ResourceTypesForNamedGraphResponseV1]]. + */ + private def getResourceTypesForNamedGraph( + namedGraphIriOption: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ResourceTypesForNamedGraphResponseV1] = { // get the resource types for a named graph - def getResourceTypes(namedGraphIri: IRI): Future[Seq[ResourceTypeV1]] = { + def getResourceTypes(namedGraphIri: IRI): Future[Seq[ResourceTypeV1]] = for { // get NamedGraphEntityInfoV1 for the given named graph - namedGraphEntityInfo: NamedGraphEntityInfoV1 <- getNamedGraphEntityInfoV1ForNamedGraph(namedGraphIri, - userProfile) + namedGraphEntityInfo: NamedGraphEntityInfoV1 <- getNamedGraphEntityInfoV1ForNamedGraph( + namedGraphIri, + userProfile + ) // get resinfo for each resource class in namedGraphEntityInfo - resInfosForNamedGraphFuture: Set[Future[(String, ResourceTypeResponseV1)]] = namedGraphEntityInfo.resourceClasses - .map { resClassIri => + resInfosForNamedGraphFuture: Set[Future[(String, ResourceTypeResponseV1)]] = + namedGraphEntityInfo.resourceClasses.map { resClassIri => for { resInfo <- getResourceTypeResponseV1(resClassIri, userProfile) } yield (resClassIri, resInfo) @@ -444,26 +468,24 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon resInfosForNamedGraph: Set[(IRI, ResourceTypeResponseV1)] <- Future.sequence(resInfosForNamedGraphFuture) - resourceTypes: Vector[ResourceTypeV1] = resInfosForNamedGraph.map { - case (resClassIri, resInfo) => - val properties = resInfo.restype_info.properties.map { prop => - PropertyTypeV1( - id = prop.id, - label = - prop.label.getOrElse(throw InconsistentRepositoryDataException(s"No label given for ${prop.id}")) - ) - }.toVector - - ResourceTypeV1( - id = resClassIri, - label = resInfo.restype_info.label.getOrElse( - throw InconsistentRepositoryDataException(s"No label given for $resClassIri")), - properties = properties + resourceTypes: Vector[ResourceTypeV1] = resInfosForNamedGraph.map { case (resClassIri, resInfo) => + val properties = resInfo.restype_info.properties.map { prop => + PropertyTypeV1( + id = prop.id, + label = prop.label.getOrElse(throw InconsistentRepositoryDataException(s"No label given for ${prop.id}")) ) + }.toVector + + ResourceTypeV1( + id = resClassIri, + label = resInfo.restype_info.label.getOrElse( + throw InconsistentRepositoryDataException(s"No label given for $resClassIri") + ), + properties = properties + ) }.toVector } yield resourceTypes - } // get resource types for named graph depending on given IRI-Option namedGraphIriOption match { @@ -481,7 +503,8 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon projectNamedGraphIris: Seq[IRI] = projectNamedGraphsResponse.vocabularies.map(_.uri) resourceTypesPerProject: Seq[Future[Seq[ResourceTypeV1]]] = projectNamedGraphIris.map(iri => - getResourceTypes(iri)) + getResourceTypes(iri) + ) resourceTypes: Seq[Seq[ResourceTypeV1]] <- Future.sequence(resourceTypesPerProject) } yield ResourceTypesForNamedGraphResponseV1(resourcetypes = resourceTypes.flatten) } @@ -489,24 +512,30 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon } /** - * Gets the property types defined in the given named graph. If there is no named graph defined, get property types for all existing named graphs. - * - * @param namedGraphIriOption the IRI of the named graph or None if all the named graphs should be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[PropertyTypesForNamedGraphResponseV1]]. - */ - private def getPropertyTypesForNamedGraph(namedGraphIriOption: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[PropertyTypesForNamedGraphResponseV1] = { - - def getPropertiesForNamedGraph(namedGraphIri: IRI, - userProfile: UserADM): Future[Seq[PropertyDefinitionInNamedGraphV1]] = { + * Gets the property types defined in the given named graph. If there is no named graph defined, get property types for all existing named graphs. + * + * @param namedGraphIriOption the IRI of the named graph or None if all the named graphs should be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[PropertyTypesForNamedGraphResponseV1]]. + */ + private def getPropertyTypesForNamedGraph( + namedGraphIriOption: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[PropertyTypesForNamedGraphResponseV1] = { + + def getPropertiesForNamedGraph( + namedGraphIri: IRI, + userProfile: UserADM + ): Future[Seq[PropertyDefinitionInNamedGraphV1]] = for { namedGraphEntityInfo <- getNamedGraphEntityInfoV1ForNamedGraph(namedGraphIri, userProfile) propertyIris: Set[IRI] = namedGraphEntityInfo.propertyIris - entities: EntityInfoGetResponseV1 <- getEntityInfoResponseV1(propertyIris = propertyIris, - userProfile = userProfile) + entities: EntityInfoGetResponseV1 <- getEntityInfoResponseV1( + propertyIris = propertyIris, + userProfile = userProfile + ) propertyInfoMap: Map[IRI, PropertyInfoV1] = entities.propertyInfoMap.filterNot { case (propertyIri, propertyEntityInfo) => propertyEntityInfo.isLinkValueProp } @@ -521,12 +550,14 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon PropertyDefinitionInNamedGraphV1( id = propertyIri, name = propertyIri, - label = - entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage)), - description = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), + label = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + description = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), vocabulary = entityInfo.ontologyIri, valuetype_id = OntologyConstants.KnoraBase.LinkValue, attributes = valueUtilV1.makeAttributeString( @@ -535,8 +566,13 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon .makeAttributeRestype( entityInfo .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse(throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint")))), + .getOrElse( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + ) + ) + ), gui_name = entityInfo .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) .map(iri => SalsahGuiConversions.iri2SalsahGuiElement(iri)) @@ -546,19 +582,25 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon PropertyDefinitionInNamedGraphV1( id = propertyIri, name = propertyIri, - label = - entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage)), - description = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), + label = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + description = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), vocabulary = entityInfo.ontologyIri, valuetype_id = entityInfo .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse(throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint")), + .getOrElse( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + ), attributes = valueUtilV1.makeAttributeString( - entityInfo.getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute)), + entityInfo.getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute) + ), gui_name = entityInfo .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) .map(iri => SalsahGuiConversions.iri2SalsahGuiElement(iri)) @@ -568,7 +610,6 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon }.toVector } yield propertyDefinitions - } namedGraphIriOption match { case Some(namedGraphIri) => // get all the property types for the given named graph @@ -586,7 +627,8 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon projectNamedGraphIris: Seq[IRI] = projectNamedGraphsResponse.vocabularies.map(_.uri) propertyTypesPerProject: Seq[Future[Seq[PropertyDefinitionInNamedGraphV1]]] = projectNamedGraphIris.map(iri => - getPropertiesForNamedGraph(iri, userProfile)) + getPropertiesForNamedGraph(iri, userProfile) + ) propertyTypes: Seq[Seq[PropertyDefinitionInNamedGraphV1]] <- Future.sequence(propertyTypesPerProject) } yield PropertyTypesForNamedGraphResponseV1(properties = propertyTypes.flatten) } @@ -594,20 +636,20 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon } /** - * Gets the property types defined for the given resource class. - * - * @param resourceClassIri the IRI of the resource class to query for. - * @param userProfile the profile of the user making the request. - * @return a [[PropertyTypesForResourceTypeResponseV1]]. - */ - private def getPropertyTypesForResourceType(resourceClassIri: IRI, - userProfile: UserADM): Future[PropertyTypesForResourceTypeResponseV1] = { + * Gets the property types defined for the given resource class. + * + * @param resourceClassIri the IRI of the resource class to query for. + * @param userProfile the profile of the user making the request. + * @return a [[PropertyTypesForResourceTypeResponseV1]]. + */ + private def getPropertyTypesForResourceType( + resourceClassIri: IRI, + userProfile: UserADM + ): Future[PropertyTypesForResourceTypeResponseV1] = for { resInfo: ResourceTypeResponseV1 <- getResourceTypeResponseV1(resourceClassIri, userProfile) propertyTypes = resInfo.restype_info.properties.toVector } yield PropertyTypesForResourceTypeResponseV1(properties = propertyTypes) - } - } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/ProjectsResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/ProjectsResponderV1.scala index 38df42e412..ba5de7dced 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/ProjectsResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/ProjectsResponderV1.scala @@ -47,16 +47,16 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import scala.concurrent.Future /** - * Returns information about Knora projects. - */ + * Returns information about Knora projects. + */ class ProjectsResponderV1(responderData: ResponderData) extends Responder(responderData) { // Global lock IRI used for project creation and update private val PROJECTS_GLOBAL_LOCK_IRI = "http://rdfh.ch/projects" /** - * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: ProjectsResponderRequestV1) = msg match { case ProjectsGetRequestV1(featureFactoryConfig, userProfile) => projectsGetRequestV1(featureFactoryConfig, userProfile) @@ -71,52 +71,54 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon } /** - * Gets all the projects and returns them as a [[ProjectsResponseV1]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user that is making the request. - * @return all the projects as a [[ProjectsResponseV1]]. - * @throws NotFoundException if no projects are found. - */ - private def projectsGetRequestV1(featureFactoryConfig: FeatureFactoryConfig, - userProfile: Option[UserProfileV1]): Future[ProjectsResponseV1] = { - + * Gets all the projects and returns them as a [[ProjectsResponseV1]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user that is making the request. + * @return all the projects as a [[ProjectsResponseV1]]. + * @throws NotFoundException if no projects are found. + */ + private def projectsGetRequestV1( + featureFactoryConfig: FeatureFactoryConfig, + userProfile: Option[UserProfileV1] + ): Future[ProjectsResponseV1] = //log.debug("projectsGetRequestV1") - for { projects <- projectsGetV1( featureFactoryConfig = featureFactoryConfig, userProfile = userProfile ) - result = if (projects.nonEmpty) { - ProjectsResponseV1( - projects = projects - ) - } else { - throw NotFoundException(s"No projects found") - } + result = + if (projects.nonEmpty) { + ProjectsResponseV1( + projects = projects + ) + } else { + throw NotFoundException(s"No projects found") + } } yield result - } /** - * Gets all the projects and returns them as a sequence containing [[ProjectInfoV1]]. - * - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user that is making the request. - * @return all the projects as a sequence containing [[ProjectInfoV1]]. - */ - private def projectsGetV1(featureFactoryConfig: FeatureFactoryConfig, - userProfile: Option[UserProfileV1]): Future[Seq[ProjectInfoV1]] = { - + * Gets all the projects and returns them as a sequence containing [[ProjectInfoV1]]. + * + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user that is making the request. + * @return all the projects as a sequence containing [[ProjectInfoV1]]. + */ + private def projectsGetV1( + featureFactoryConfig: FeatureFactoryConfig, + userProfile: Option[UserProfileV1] + ): Future[Seq[ProjectInfoV1]] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt .getProjects( triplestore = settings.triplestoreType ) - .toString()) + .toString() + ) //_ = log.debug(s"getProjectsResponseV1 - query: $sparqlQueryString") projectsResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -126,9 +128,12 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon projectsWithProperties: Map[IRI, Map[IRI, Seq[String]]] = projectsResponseRows.groupBy(_.rowMap("s")).map { case (projIri: String, rows: Seq[VariableResultsRow]) => - (projIri, rows.groupBy(_.rowMap("p")).map { - case (predicate: IRI, literals: Seq[VariableResultsRow]) => predicate -> literals.map(_.rowMap("o")) - }) + ( + projIri, + rows.groupBy(_.rowMap("p")).map { case (predicate: IRI, literals: Seq[VariableResultsRow]) => + predicate -> literals.map(_.rowMap("o")) + } + ) } //_ = log.debug(s"getProjectsResponseV1 - projectsWithProperties: $projectsWithProperties") @@ -137,63 +142,70 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon userProfile = userProfile ) - projects = projectsWithProperties.map { - case (projectIri: String, propsMap: Map[String, Seq[String]]) => - val keywordsSeq: Seq[String] = - propsMap.getOrElse(OntologyConstants.KnoraAdmin.ProjectKeyword, Seq.empty[String]).sorted + projects = projectsWithProperties.map { case (projectIri: String, propsMap: Map[String, Seq[String]]) => + val keywordsSeq: Seq[String] = + propsMap.getOrElse(OntologyConstants.KnoraAdmin.ProjectKeyword, Seq.empty[String]).sorted - val maybeKeywords: Option[String] = if (keywordsSeq.nonEmpty) { - Some(keywordsSeq.mkString(", ")) - } else { - None - } + val maybeKeywords: Option[String] = if (keywordsSeq.nonEmpty) { + Some(keywordsSeq.mkString(", ")) + } else { + None + } - val ontologies = ontologiesForProjects.getOrElse(projectIri, Seq.empty[IRI]) - - ProjectInfoV1( - id = projectIri, - shortname = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.ProjectShortname, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortname defined.")) - .head, - shortcode = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.ProjectShortcode, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortcode defined.")) - .head, - longname = propsMap.get(OntologyConstants.KnoraAdmin.ProjectLongname).map(_.head), - description = propsMap.get(OntologyConstants.KnoraAdmin.ProjectDescription).map(_.head), - keywords = maybeKeywords, - logo = propsMap.get(OntologyConstants.KnoraAdmin.ProjectLogo).map(_.head), - institution = propsMap.get(OntologyConstants.KnoraAdmin.BelongsToInstitution).map(_.head), - ontologies = ontologies, - status = propsMap - .getOrElse(OntologyConstants.KnoraAdmin.Status, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no status defined.")) - .head - .toBoolean, - selfjoin = propsMap - .getOrElse( - OntologyConstants.KnoraAdmin.HasSelfJoinEnabled, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no hasSelfJoinEnabled defined.")) - .head - .toBoolean - ) + val ontologies = ontologiesForProjects.getOrElse(projectIri, Seq.empty[IRI]) + + ProjectInfoV1( + id = projectIri, + shortname = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.ProjectShortname, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortname defined.") + ) + .head, + shortcode = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.ProjectShortcode, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortcode defined.") + ) + .head, + longname = propsMap.get(OntologyConstants.KnoraAdmin.ProjectLongname).map(_.head), + description = propsMap.get(OntologyConstants.KnoraAdmin.ProjectDescription).map(_.head), + keywords = maybeKeywords, + logo = propsMap.get(OntologyConstants.KnoraAdmin.ProjectLogo).map(_.head), + institution = propsMap.get(OntologyConstants.KnoraAdmin.BelongsToInstitution).map(_.head), + ontologies = ontologies, + status = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.Status, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no status defined.") + ) + .head + .toBoolean, + selfjoin = propsMap + .getOrElse( + OntologyConstants.KnoraAdmin.HasSelfJoinEnabled, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no hasSelfJoinEnabled defined.") + ) + .head + .toBoolean + ) }.toSeq } yield projects - } /** - * Gets the ontologies that belong to each project. - * - * @param projectIris the IRIs of the projects whose ontologies should be requested. If empty, returns the ontologies - * for all projects. - * @param userProfile the requesting user. - * @return a map of project IRIs to sequences of ontology IRIs. - */ - private def getOntologiesForProjects(projectIris: Set[IRI] = Set.empty[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: Option[UserProfileV1]): Future[Map[IRI, Seq[IRI]]] = { + * Gets the ontologies that belong to each project. + * + * @param projectIris the IRIs of the projects whose ontologies should be requested. If empty, returns the ontologies + * for all projects. + * @param userProfile the requesting user. + * @return a map of project IRIs to sequences of ontology IRIs. + */ + private def getOntologiesForProjects( + projectIris: Set[IRI] = Set.empty[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: Option[UserProfileV1] + ): Future[Map[IRI, Seq[IRI]]] = for { // Get a UserADM for the UserProfileV1, because we need it to send a message to OntologyResponderV1. userADM: UserADM <- userProfile match { @@ -220,33 +232,29 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon userADM = userADM )).mapTo[NamedGraphsResponseV1] - } yield - namedGraphsResponse.vocabularies - .map { namedGraph: NamedGraphV1 => - namedGraph.project_id -> namedGraph.id - } - .groupBy(_._1) - .map { - case (projectIri, projectIriAndOntologies: Seq[(IRI, IRI)]) => - projectIri -> projectIriAndOntologies.map(_._2) - } - } + } yield namedGraphsResponse.vocabularies.map { namedGraph: NamedGraphV1 => + namedGraph.project_id -> namedGraph.id + } + .groupBy(_._1) + .map { case (projectIri, projectIriAndOntologies: Seq[(IRI, IRI)]) => + projectIri -> projectIriAndOntologies.map(_._2) + } /** - * Gets the project with the given project IRI and returns the information as a [[ProjectInfoResponseV1]]. - * - * @param projectIri the IRI of the project requested. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of user that is making the request. - * @return information about the project as a [[ProjectInfoResponseV1]]. - * @throws NotFoundException when no project for the given IRI can be found - */ - private def projectInfoByIRIGetRequestV1(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: Option[UserProfileV1] = None): Future[ProjectInfoResponseV1] = { - + * Gets the project with the given project IRI and returns the information as a [[ProjectInfoResponseV1]]. + * + * @param projectIri the IRI of the project requested. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of user that is making the request. + * @return information about the project as a [[ProjectInfoResponseV1]]. + * @throws NotFoundException when no project for the given IRI can be found + */ + private def projectInfoByIRIGetRequestV1( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: Option[UserProfileV1] = None + ): Future[ProjectInfoResponseV1] = //log.debug("projectInfoByIRIGetRequestV1 - projectIRI: {}", projectIRI) - for { maybeProjectInfo: Option[ProjectInfoV1] <- projectInfoByIRIGetV1( projectIri = projectIri, @@ -258,26 +266,24 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon case Some(pi) => pi case None => throw NotFoundException(s"Project '$projectIri' not found") } - } yield - ProjectInfoResponseV1( - project_info = projectInfo - ) - } + } yield ProjectInfoResponseV1( + project_info = projectInfo + ) /** - * Gets the project with the given project IRI and returns the information as a [[ProjectInfoV1]]. - * - * @param projectIri the IRI of the project requested. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of user that is making the request. - * @return information about the project as a [[ProjectInfoV1]]. - */ - private def projectInfoByIRIGetV1(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: Option[UserProfileV1] = None): Future[Option[ProjectInfoV1]] = { - + * Gets the project with the given project IRI and returns the information as a [[ProjectInfoV1]]. + * + * @param projectIri the IRI of the project requested. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of user that is making the request. + * @return information about the project as a [[ProjectInfoV1]]. + */ + private def projectInfoByIRIGetV1( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: Option[UserProfileV1] = None + ): Future[Option[ProjectInfoV1]] = //log.debug("projectInfoByIRIGetV1 - projectIRI: {}", projectIri) - for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -285,7 +291,8 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon triplestore = settings.triplestoreType, projectIri = projectIri ) - .toString()) + .toString() + ) projectResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] @@ -297,38 +304,39 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon projectOntologies = ontologiesForProjects.getOrElse(projectIri, Seq.empty[IRI]) - projectInfo = if (projectResponse.results.bindings.nonEmpty) { - Some( - createProjectInfoV1( - projectResponse = projectResponse.results.bindings, - projectIri = projectIri, - ontologies = projectOntologies, - userProfile - )) - } else { - None - } + projectInfo = + if (projectResponse.results.bindings.nonEmpty) { + Some( + createProjectInfoV1( + projectResponse = projectResponse.results.bindings, + projectIri = projectIri, + ontologies = projectOntologies, + userProfile + ) + ) + } else { + None + } //_ = log.debug("projectInfoByIRIGetV1 - projectInfo: {}", projectInfo) } yield projectInfo - } /** - * Gets the project with the given shortname and returns the information as a [[ProjectInfoResponseV1]]. - * - * @param shortName the shortname of the project requested. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of user that is making the request. - * @return information about the project as a [[ProjectInfoResponseV1]]. - * @throws NotFoundException in the case that no project for the given shortname can be found. - */ - private def projectInfoByShortnameGetRequestV1(shortName: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: Option[UserProfileV1]): Future[ProjectInfoResponseV1] = { - + * Gets the project with the given shortname and returns the information as a [[ProjectInfoResponseV1]]. + * + * @param shortName the shortname of the project requested. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of user that is making the request. + * @return information about the project as a [[ProjectInfoResponseV1]]. + * @throws NotFoundException in the case that no project for the given shortname can be found. + */ + private def projectInfoByShortnameGetRequestV1( + shortName: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: Option[UserProfileV1] + ): Future[ProjectInfoResponseV1] = //log.debug("projectInfoByShortnameGetRequestV1 - shortName: {}", shortName) - for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -336,18 +344,20 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon triplestore = settings.triplestoreType, shortname = shortName ) - .toString()) + .toString() + ) //_ = log.debug(s"getProjectInfoByShortnameGetRequest - query: $sparqlQueryString") projectResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] //_ = log.debug(s"getProjectInfoByShortnameGetRequest - result: $projectResponse") // get project IRI from results rows - projectIri: IRI = if (projectResponse.results.bindings.nonEmpty) { - projectResponse.results.bindings.head.rowMap("s") - } else { - throw NotFoundException(s"Project '$shortName' not found") - } + projectIri: IRI = + if (projectResponse.results.bindings.nonEmpty) { + projectResponse.results.bindings.head.rowMap("s") + } else { + throw NotFoundException(s"Project '$shortName' not found") + } ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects( projectIris = Set(projectIri), @@ -364,31 +374,29 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon userProfile ) - } yield - ProjectInfoResponseV1( - project_info = projectInfo - ) - } + } yield ProjectInfoResponseV1( + project_info = projectInfo + ) //////////////////// // Helper Methods // //////////////////// /** - * Helper method that turns SPARQL result rows into a [[ProjectInfoV1]]. - * - * @param projectResponse results from the SPARQL query representing information about the project. - * @param projectIri the IRI of the project the querid information belong to. - * @param userProfile the profile of user that is making the request. - * @return a [[ProjectInfoV1]] representing information about project. - */ - private def createProjectInfoV1(projectResponse: Seq[VariableResultsRow], - projectIri: IRI, - ontologies: Seq[IRI], - userProfile: Option[UserProfileV1]): ProjectInfoV1 = { - + * Helper method that turns SPARQL result rows into a [[ProjectInfoV1]]. + * + * @param projectResponse results from the SPARQL query representing information about the project. + * @param projectIri the IRI of the project the querid information belong to. + * @param userProfile the profile of user that is making the request. + * @return a [[ProjectInfoV1]] representing information about project. + */ + private def createProjectInfoV1( + projectResponse: Seq[VariableResultsRow], + projectIri: IRI, + ontologies: Seq[IRI], + userProfile: Option[UserProfileV1] + ): ProjectInfoV1 = //log.debug("createProjectInfoV1 - projectResponse: {}", projectResponse) - if (projectResponse.nonEmpty) { val projectProperties: Map[String, Seq[String]] = projectResponse.groupBy(_.rowMap("p")).map { @@ -410,12 +418,16 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon ProjectInfoV1( id = projectIri, shortname = projectProperties - .getOrElse(OntologyConstants.KnoraAdmin.ProjectShortname, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortname defined.")) + .getOrElse( + OntologyConstants.KnoraAdmin.ProjectShortname, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortname defined.") + ) .head, shortcode = projectProperties - .getOrElse(OntologyConstants.KnoraAdmin.ProjectShortcode, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortcode defined.")) + .getOrElse( + OntologyConstants.KnoraAdmin.ProjectShortcode, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no shortcode defined.") + ) .head, longname = projectProperties.get(OntologyConstants.KnoraAdmin.ProjectLongname).map(_.head), description = projectProperties.get(OntologyConstants.KnoraAdmin.ProjectDescription).map(_.head), @@ -424,14 +436,17 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon institution = projectProperties.get(OntologyConstants.KnoraAdmin.BelongsToInstitution).map(_.head), ontologies = ontologies, status = projectProperties - .getOrElse(OntologyConstants.KnoraAdmin.Status, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no status defined.")) + .getOrElse( + OntologyConstants.KnoraAdmin.Status, + throw InconsistentRepositoryDataException(s"Project: $projectIri has no status defined.") + ) .head .toBoolean, selfjoin = projectProperties .getOrElse( OntologyConstants.KnoraAdmin.HasSelfJoinEnabled, - throw InconsistentRepositoryDataException(s"Project: $projectIri has no hasSelfJoinEnabled defined.")) + throw InconsistentRepositoryDataException(s"Project: $projectIri has no hasSelfJoinEnabled defined.") + ) .head .toBoolean ) @@ -441,65 +456,63 @@ class ProjectsResponderV1(responderData: ResponderData) extends Responder(respon throw NotFoundException(s"For the given project IRI $projectIri no information was found") } - } - /** - * Helper method for checking if a project identified by IRI exists. - * - * @param projectIri the IRI of the project. - * @return a [[Boolean]]. - */ - def projectByIriExists(projectIri: IRI): Future[Boolean] = { + * Helper method for checking if a project identified by IRI exists. + * + * @param projectIri the IRI of the project. + * @return a [[Boolean]]. + */ + def projectByIriExists(projectIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByIri(projectIri = projectIri) - .toString) + .toString + ) //_ = log.debug("projectExists - query: {}", askString) checkProjectExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkProjectExistsResponse.result } yield result - } /** - * Helper method for checking if a project identified by shortname exists. - * - * @param shortname the shortname of the project. - * @return a [[Boolean]]. - */ - def projectByShortnameExists(shortname: String): Future[Boolean] = { + * Helper method for checking if a project identified by shortname exists. + * + * @param shortname the shortname of the project. + * @return a [[Boolean]]. + */ + def projectByShortnameExists(shortname: String): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByShortname(shortname = shortname) - .toString) + .toString + ) //_ = log.debug("projectExists - query: {}", askString) checkProjectExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkProjectExistsResponse.result } yield result - } /** - * Helper method for checking if a project identified by shortcode exists. - * - * @param shortcode the shortcode of the project. - * @return a [[Boolean]]. - */ - def projectByShortcodeExists(shortcode: String): Future[Boolean] = { + * Helper method for checking if a project identified by shortcode exists. + * + * @param shortcode the shortcode of the project. + * @return a [[Boolean]]. + */ + def projectByShortcodeExists(shortcode: String): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByShortcode(shortcode = shortcode) - .toString) + .toString + ) //_ = log.debug("projectExists - query: {}", askString) checkProjectExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkProjectExistsResponse.result } yield result - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/ResourcesResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/ResourcesResponderV1.scala index 748f4b62c2..6df0c8806a 100755 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/ResourcesResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/ResourcesResponderV1.scala @@ -71,16 +71,16 @@ import scala.concurrent.Future import scala.util.{Failure, Success, Try} /** - * Responds to requests for information about resources, and returns responses in Knora API v1 format. - */ + * Responds to requests for information about resources, and returns responses in Knora API v1 format. + */ class ResourcesResponderV1(responderData: ResponderData) extends Responder(responderData) { // Converts SPARQL query results to ApiValueV1 objects. private val valueUtilV1 = new ValueUtilV1(settings) /** - * Receives a message extending [[ResourcesResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message extending [[ResourcesResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: ResourcesResponderRequestV1) = msg match { case ResourceInfoGetRequestV1(resourceIri, featureFactoryConfig, userProfile) => getResourceInfoResponseV1(resourceIri, featureFactoryConfig, userProfile) @@ -91,33 +91,41 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo case ResourceRightsGetRequestV1(resourceIri, featureFactoryConfig, userProfile) => getRightsResponseV1(resourceIri, featureFactoryConfig, userProfile) case graphDataGetRequest: GraphDataGetRequestV1 => getGraphDataResponseV1(graphDataGetRequest) - case ResourceSearchGetRequestV1(searchString: String, - resourceIri: Option[IRI], - numberOfProps: Int, - limitOfResults: Int, - userProfile: UserADM) => + case ResourceSearchGetRequestV1( + searchString: String, + resourceIri: Option[IRI], + numberOfProps: Int, + limitOfResults: Int, + userProfile: UserADM + ) => getResourceSearchResponseV1(searchString, resourceIri, numberOfProps, limitOfResults, userProfile) - case ResourceCreateRequestV1(resourceTypeIri, - label, - values, - file, - projectIri, - featureFactoryConfig, - userProfile, - apiRequestID) => - createNewResource(resourceTypeIri, - label, - values, - file, - projectIri, - featureFactoryConfig, - userProfile, - apiRequestID) - case MultipleResourceCreateRequestV1(resourcesToCreate, - projectIri, - featureFactoryConfig, - userProfile, - apiRequestID) => + case ResourceCreateRequestV1( + resourceTypeIri, + label, + values, + file, + projectIri, + featureFactoryConfig, + userProfile, + apiRequestID + ) => + createNewResource( + resourceTypeIri, + label, + values, + file, + projectIri, + featureFactoryConfig, + userProfile, + apiRequestID + ) + case MultipleResourceCreateRequestV1( + resourcesToCreate, + projectIri, + featureFactoryConfig, + userProfile, + apiRequestID + ) => createMultipleNewResources(resourcesToCreate, projectIri, featureFactoryConfig, userProfile, apiRequestID) case ResourceCheckClassRequestV1(resourceIri: IRI, owlClass: IRI, featureFactoryConfig, userProfile: UserADM) => checkResourceClass(resourceIri, owlClass, featureFactoryConfig, userProfile) @@ -135,83 +143,89 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Methods for generating complete API responses. // TODO: move this to a test responder in the test package. - private def makeInternalServerException: Future[String] = { + private def makeInternalServerException: Future[String] = Future.failed(UpdateNotPerformedException("thrown inside the ResourcesResponder")) - } // TODO: move this to a test responder in the test package. - private def makeFutureOfUnit: Future[Unit] = { + private def makeFutureOfUnit: Future[Unit] = Future(()) - } /** - * Gets a graph of resources that are reachable via links to or from a given resource. - * - * @param graphDataGetRequest a [[GraphDataGetRequestV1]] specifying the characteristics of the graph. - * @return a [[GraphDataGetResponseV1]] representing the requested graph. - */ + * Gets a graph of resources that are reachable via links to or from a given resource. + * + * @param graphDataGetRequest a [[GraphDataGetRequestV1]] specifying the characteristics of the graph. + * @return a [[GraphDataGetResponseV1]] representing the requested graph. + */ private def getGraphDataResponseV1(graphDataGetRequest: GraphDataGetRequestV1): Future[GraphDataGetResponseV1] = { val userProfileV1 = graphDataGetRequest.userADM.asUserProfileV1 /** - * The internal representation of a node returned by a SPARQL query generated by the `getGraphData` template. - * - * @param nodeIri the IRI of the node. - * @param nodeClass the IRI of the node's class. - * @param nodeLabel the node's label. - * @param nodeCreator the node's creator. - * @param nodeProject the node's project. - * @param nodePermissions the node's permissions. - */ - case class QueryResultNode(nodeIri: IRI, - nodeClass: IRI, - nodeLabel: String, - nodeCreator: IRI, - nodeProject: IRI, - nodePermissions: String) + * The internal representation of a node returned by a SPARQL query generated by the `getGraphData` template. + * + * @param nodeIri the IRI of the node. + * @param nodeClass the IRI of the node's class. + * @param nodeLabel the node's label. + * @param nodeCreator the node's creator. + * @param nodeProject the node's project. + * @param nodePermissions the node's permissions. + */ + case class QueryResultNode( + nodeIri: IRI, + nodeClass: IRI, + nodeLabel: String, + nodeCreator: IRI, + nodeProject: IRI, + nodePermissions: String + ) /** - * The internal representation of an edge returned by a SPARQL query generated by the `getGraphData` template. - * - * @param linkValueIri the IRI of the link value. - * @param sourceNodeIri the IRI of the source node. - * @param targetNodeIri the IRI of the target node. - * @param linkProp the IRI of the link property. - * @param linkValueCreator the link value's creator. - * @param sourceNodeProject the project of the source node. - * @param linkValuePermissions the link value's permissions. - */ - case class QueryResultEdge(linkValueIri: IRI, - sourceNodeIri: IRI, - targetNodeIri: IRI, - linkProp: IRI, - linkValueCreator: IRI, - sourceNodeProject: IRI, - linkValuePermissions: String) + * The internal representation of an edge returned by a SPARQL query generated by the `getGraphData` template. + * + * @param linkValueIri the IRI of the link value. + * @param sourceNodeIri the IRI of the source node. + * @param targetNodeIri the IRI of the target node. + * @param linkProp the IRI of the link property. + * @param linkValueCreator the link value's creator. + * @param sourceNodeProject the project of the source node. + * @param linkValuePermissions the link value's permissions. + */ + case class QueryResultEdge( + linkValueIri: IRI, + sourceNodeIri: IRI, + targetNodeIri: IRI, + linkProp: IRI, + linkValueCreator: IRI, + sourceNodeProject: IRI, + linkValuePermissions: String + ) /** - * Represents results returned by a SPARQL query generated by the `getGraphData` template. - * - * @param nodes the nodes that were returned by the query. - * @param edges the edges that were returned by the query. - */ - case class GraphQueryResults(nodes: Set[QueryResultNode] = Set.empty[QueryResultNode], - edges: Set[QueryResultEdge] = Set.empty[QueryResultEdge]) + * Represents results returned by a SPARQL query generated by the `getGraphData` template. + * + * @param nodes the nodes that were returned by the query. + * @param edges the edges that were returned by the query. + */ + case class GraphQueryResults( + nodes: Set[QueryResultNode] = Set.empty[QueryResultNode], + edges: Set[QueryResultEdge] = Set.empty[QueryResultEdge] + ) /** - * Recursively queries outbound or inbound links from/to a resource. - * - * @param startNode the node to use as the starting point of the query. The user is assumed to have permission - * to see this node. - * @param outbound `true` to get outbound links, `false` to get inbound links. - * @param depth the maximum depth of the query. - * @param traversedEdges edges that have already been traversed. - * @return a [[GraphQueryResults]]. - */ - def traverseGraph(startNode: QueryResultNode, - outbound: Boolean, - depth: Int, - traversedEdges: Set[QueryResultEdge] = Set.empty[QueryResultEdge]): Future[GraphQueryResults] = { + * Recursively queries outbound or inbound links from/to a resource. + * + * @param startNode the node to use as the starting point of the query. The user is assumed to have permission + * to see this node. + * @param outbound `true` to get outbound links, `false` to get inbound links. + * @param depth the maximum depth of the query. + * @param traversedEdges edges that have already been traversed. + * @return a [[GraphQueryResults]]. + */ + def traverseGraph( + startNode: QueryResultNode, + outbound: Boolean, + depth: Int, + traversedEdges: Set[QueryResultEdge] = Set.empty[QueryResultEdge] + ): Future[GraphQueryResults] = { if (depth < 1) Future.failed(AssertionException("Depth must be at least 1")) for { @@ -224,7 +238,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo startNodeOnly = false, outbound = outbound // true to query outbound edges, false to query inbound edges ) - .toString()) + .toString() + ) // _ = println(sparql) @@ -232,13 +247,13 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo rows = response.results.bindings // Did we get any results? - recursiveResults: GraphQueryResults <- if (rows.isEmpty) { - // No. Return nothing. - Future(GraphQueryResults()) - } else { - // Yes. Get the nodes from the query results. - val otherNodes: Seq[QueryResultNode] = rows - .map { row => + recursiveResults: GraphQueryResults <- + if (rows.isEmpty) { + // No. Return nothing. + Future(GraphQueryResults()) + } else { + // Yes. Get the nodes from the query results. + val otherNodes: Seq[QueryResultNode] = rows.map { row => val rowMap = row.rowMap QueryResultNode( @@ -249,8 +264,7 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo nodeProject = rowMap("nodeProject"), nodePermissions = rowMap("nodePermissions") ) - } - .filter { node => + }.filter { node => // Filter out the nodes that the user doesn't have permission to see. PermissionUtilADM .getUserPermissionV1( @@ -263,12 +277,11 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo .nonEmpty } - // Collect the IRIs of the nodes that the user has permission to see, including the start node. - val visibleNodeIris = otherNodes.map(_.nodeIri).toSet + startNode.nodeIri + // Collect the IRIs of the nodes that the user has permission to see, including the start node. + val visibleNodeIris = otherNodes.map(_.nodeIri).toSet + startNode.nodeIri - // Get the edges from the query results. - val edges: Set[QueryResultEdge] = rows - .map { row => + // Get the edges from the query results. + val edges: Set[QueryResultEdge] = rows.map { row => val rowMap = row.rowMap val nodeIri = rowMap("node") @@ -289,72 +302,68 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo sourceNodeProject = if (outbound) startNode.nodeProject else rowMap("nodeProject"), linkValuePermissions = rowMap("linkValuePermissions") ) - } - .filter { edge => + }.filter { edge => // Filter out the edges that the user doesn't have permission to see. To see an edge, // the user must have some permission on the link value and on the source and target // nodes. - val hasPermission = visibleNodeIris.contains(edge.sourceNodeIri) && visibleNodeIris.contains( - edge.targetNodeIri) && - PermissionUtilADM - .getUserPermissionV1( - entityIri = edge.linkValueIri, - entityCreator = edge.linkValueCreator, - entityProject = edge.sourceNodeProject, - entityPermissionLiteral = edge.linkValuePermissions, - userProfile = userProfileV1 - ) - .nonEmpty + val hasPermission = + visibleNodeIris.contains(edge.sourceNodeIri) && visibleNodeIris.contains(edge.targetNodeIri) && + PermissionUtilADM + .getUserPermissionV1( + entityIri = edge.linkValueIri, + entityCreator = edge.linkValueCreator, + entityProject = edge.sourceNodeProject, + entityPermissionLiteral = edge.linkValuePermissions, + userProfile = userProfileV1 + ) + .nonEmpty // Filter out edges we've already traversed. val isRedundant = traversedEdges.contains(edge) // if (isRedundant) println(s"filtering out edge from ${edge.sourceNodeIri} to ${edge.targetNodeIri}") hasPermission && !isRedundant - } - .toSet + }.toSet - // Include only nodes that are reachable via edges that we're going to traverse (i.e. the user - // has permission to see those edges, and we haven't already traversed them). - val visibleNodeIrisFromEdges = edges.map(_.sourceNodeIri) ++ edges.map(_.targetNodeIri) - val filteredOtherNodes = otherNodes.filter(node => visibleNodeIrisFromEdges.contains(node.nodeIri)) + // Include only nodes that are reachable via edges that we're going to traverse (i.e. the user + // has permission to see those edges, and we haven't already traversed them). + val visibleNodeIrisFromEdges = edges.map(_.sourceNodeIri) ++ edges.map(_.targetNodeIri) + val filteredOtherNodes = otherNodes.filter(node => visibleNodeIrisFromEdges.contains(node.nodeIri)) - // Make a GraphQueryResults containing the resulting nodes and edges, including the start - // node. - val results = GraphQueryResults(nodes = filteredOtherNodes.toSet + startNode, edges = edges) + // Make a GraphQueryResults containing the resulting nodes and edges, including the start + // node. + val results = GraphQueryResults(nodes = filteredOtherNodes.toSet + startNode, edges = edges) - // Have we reached the maximum depth? - if (depth == 1) { - // Yes. Just return the results we have. - Future(results) - } else { - // No. Recursively get results for each of the nodes we found. - - val lowerResultFutures: Seq[Future[GraphQueryResults]] = filteredOtherNodes.map { node => - traverseGraph( - startNode = node, - outbound = outbound, - depth = depth - 1, - traversedEdges = traversedEdges ++ edges - ) - } + // Have we reached the maximum depth? + if (depth == 1) { + // Yes. Just return the results we have. + Future(results) + } else { + // No. Recursively get results for each of the nodes we found. + + val lowerResultFutures: Seq[Future[GraphQueryResults]] = filteredOtherNodes.map { node => + traverseGraph( + startNode = node, + outbound = outbound, + depth = depth - 1, + traversedEdges = traversedEdges ++ edges + ) + } - val lowerResultsFuture: Future[Seq[GraphQueryResults]] = Future.sequence(lowerResultFutures) + val lowerResultsFuture: Future[Seq[GraphQueryResults]] = Future.sequence(lowerResultFutures) - // Return those results plus the ones we found. + // Return those results plus the ones we found. - for { - lowerResultsSeq <- lowerResultsFuture - } yield - lowerResultsSeq.foldLeft(results) { - case (acc, lowerResults) => - GraphQueryResults( - nodes = acc.nodes ++ lowerResults.nodes, - edges = acc.edges ++ lowerResults.edges - ) + for { + lowerResultsSeq <- lowerResultsFuture + } yield lowerResultsSeq.foldLeft(results) { case (acc, lowerResults) => + GraphQueryResults( + nodes = acc.nodes ++ lowerResults.nodes, + edges = acc.edges ++ lowerResults.edges + ) } + } } - } } yield recursiveResults } @@ -367,7 +376,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo startNodeIri = graphDataGetRequest.resourceIri, startNodeOnly = true ) - .toString()) + .toString() + ) // _ = println(sparql) @@ -390,17 +400,20 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo ) // Make sure the user has permission to see the start node. - _ = if (PermissionUtilADM - .getUserPermissionV1( - entityIri = startNode.nodeIri, - entityCreator = startNode.nodeCreator, - entityProject = startNode.nodeProject, - entityPermissionLiteral = startNode.nodePermissions, - userProfile = userProfileV1 - ) - .isEmpty) { + _ = if ( + PermissionUtilADM + .getUserPermissionV1( + entityIri = startNode.nodeIri, + entityCreator = startNode.nodeCreator, + entityProject = startNode.nodeProject, + entityPermissionLiteral = startNode.nodePermissions, + userProfile = userProfileV1 + ) + .isEmpty + ) { throw ForbiddenException( - s"User ${graphDataGetRequest.userADM.id} does not have permission to view resource ${graphDataGetRequest.resourceIri}") + s"User ${graphDataGetRequest.userADM.id} does not have permission to view resource ${graphDataGetRequest.resourceIri}" + ) } // Recursively get the graph containing outbound links. @@ -426,9 +439,11 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceClassIris = nodes.map(_.nodeClass) propertyIris = edges.map(_.linkProp) - entityInfoRequest = EntityInfoGetRequestV1(resourceClassIris = resourceClassIris, - propertyIris = propertyIris, - userProfile = graphDataGetRequest.userADM) + entityInfoRequest = EntityInfoGetRequestV1( + resourceClassIris = resourceClassIris, + propertyIris = propertyIris, + userProfile = graphDataGetRequest.userADM + ) entityInfoResponse: EntityInfoGetResponseV1 <- (responderManager ? entityInfoRequest) .mapTo[EntityInfoGetResponseV1] @@ -474,15 +489,17 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Returns an instance of [[ResourceInfoResponseV1]] describing a resource, in Knora API v1 format. - * - * @param resourceIri the IRI of the resource to be queried. - * @param userProfile the profile of the user making the request. - * @return a [[ResourceInfoResponseV1]] containing a representation of the resource. - */ - private def getResourceInfoResponseV1(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ResourceInfoResponseV1] = { + * Returns an instance of [[ResourceInfoResponseV1]] describing a resource, in Knora API v1 format. + * + * @param resourceIri the IRI of the resource to be queried. + * @param userProfile the profile of the user making the request. + * @return a [[ResourceInfoResponseV1]] containing a representation of the resource. + */ + private def getResourceInfoResponseV1( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ResourceInfoResponseV1] = for { (userPermissions, resInfo) <- getResourceInfoV1( resourceIri = resourceIri, @@ -490,31 +507,31 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo userProfile = userProfile, queryOntology = true ) - } yield - userPermissions match { - case Some(permissions) => - ResourceInfoResponseV1( - resource_info = Some(resInfo), - rights = userPermissions - ) - case None => - throw ForbiddenException(s"User ${userProfile.id} does not have permission to view resource $resourceIri") - } - } + } yield userPermissions match { + case Some(permissions) => + ResourceInfoResponseV1( + resource_info = Some(resInfo), + rights = userPermissions + ) + case None => + throw ForbiddenException(s"User ${userProfile.id} does not have permission to view resource $resourceIri") + } /** - * Returns an instance of [[ResourceFullResponseV1]] representing a Knora resource - * with its properties and their values, in Knora API v1 format. - * - * @param resourceIri the IRI of the resource to be queried. - * @param userProfile the profile of the user making the request. - * @param getIncoming if `true` (the default), queries the resource's inconing references. - * @return a [[ResourceFullResponseV1]]. - */ - private def getFullResponseV1(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - getIncoming: Boolean = true): Future[ResourceFullResponseV1] = { + * Returns an instance of [[ResourceFullResponseV1]] representing a Knora resource + * with its properties and their values, in Knora API v1 format. + * + * @param resourceIri the IRI of the resource to be queried. + * @param userProfile the profile of the user making the request. + * @param getIncoming if `true` (the default), queries the resource's inconing references. + * @return a [[ResourceFullResponseV1]]. + */ + private def getFullResponseV1( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + getIncoming: Boolean = true + ): Future[ResourceFullResponseV1] = { val userProfileV1 = userProfile.asUserProfileV1 // Query resource info, resource properties, and incoming references in parallel. @@ -540,7 +557,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo triplestore = settings.triplestoreType, resourceIri = resourceIri ) - .toString()) + .toString() + ) response <- (storeManager ? SparqlSelectRequest(incomingRefsSparql)).mapTo[SparqlSelectResult] } yield Some(response) } else { @@ -581,110 +599,114 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo val groupedByIncomingIri: Map[IRI, Seq[VariableResultsRow]] = incomingRefsResponseRows.groupBy(_.rowMap("referringResource")) - groupedByIncomingIri.map { - case (incomingIri: IRI, rows: Seq[VariableResultsRow]) => - // Make a resource info for each referring resource, and check the permissions on the referring resource. + groupedByIncomingIri.map { case (incomingIri: IRI, rows: Seq[VariableResultsRow]) => + // Make a resource info for each referring resource, and check the permissions on the referring resource. - val rowsForResInfo = rows.filterNot( - row => - stringFormatter.optionStringToBoolean( - row.rowMap.get("isLinkValue"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isLinkValue: ${row.rowMap.get("isLinkValue")}"))) - - for { - (incomingResPermission, incomingResInfo) <- makeResourceInfoV1( - resourceIri = incomingIri, - resInfoResponseRows = rowsForResInfo, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile, - queryOntology = false + val rowsForResInfo = rows.filterNot(row => + stringFormatter.optionStringToBoolean( + row.rowMap.get("isLinkValue"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isLinkValue: ${row.rowMap.get("isLinkValue")}" ) + ) + ) - // Does the user have permission to see the referring resource? - incomingV1s: Vector[IncomingV1] <- incomingResPermission match { - case Some(_) => - // Yes. For each link from the referring resource, check whether the user has permission to see the link. If so, make an IncomingV1 for the link. + for { + (incomingResPermission, incomingResInfo) <- makeResourceInfoV1( + resourceIri = incomingIri, + resInfoResponseRows = rowsForResInfo, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile, + queryOntology = false + ) - // Filter to get only the rows representing LinkValues. - val rowsWithLinkValues = rows.filter( - row => - stringFormatter.optionStringToBoolean( - row.rowMap.get("isLinkValue"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isLinkValue: ${row.rowMap.get("isLinkValue")}"))) - - // Group them by LinkValue IRI. - val groupedByLinkValue: Map[String, Seq[VariableResultsRow]] = - rowsWithLinkValues.groupBy(_.rowMap("obj")) - - // For each LinkValue, check whether the user has permission to see the link, and if so, make an IncomingV1. - val maybeIncomingV1sWithFuture: Iterable[Future[Option[IncomingV1]]] = groupedByLinkValue.map { - case (linkValueIri: IRI, linkValueRows: Seq[VariableResultsRow]) => - // Convert the rows representing the LinkValue to a ValueProps. - val linkValueProps = - valueUtilV1.createValueProps(valueIri = linkValueIri, objRows = linkValueRows) - - // Convert the resulting ValueProps into a LinkValueV1 so we can check its rdf:predicate. - - for { - apiValueV1 <- valueUtilV1.makeValueV1( - valueProps = linkValueProps, - projectShortcode = resInfoWithoutQueryingOntology.project_shortcode, - responderManager = responderManager, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile - ) + // Does the user have permission to see the referring resource? + incomingV1s: Vector[IncomingV1] <- incomingResPermission match { + case Some(_) => + // Yes. For each link from the referring resource, check whether the user has permission to see the link. If so, make an IncomingV1 for the link. - linkValueV1: LinkValueV1 = apiValueV1 match { - case linkValueV1: LinkValueV1 => linkValueV1 - case _ => - throw InconsistentRepositoryDataException( - s"Expected $linkValueIri to be a knora-base:LinkValue, but its type is ${apiValueV1.valueTypeIri}") - } - - // Check the permissions on the LinkValue. - linkValuePermission = PermissionUtilADM.getUserPermissionWithValuePropsV1( - valueIri = linkValueIri, - valueProps = linkValueProps, - entityProject = Some(incomingResInfo.project_id), - userProfile = userProfileV1 + // Filter to get only the rows representing LinkValues. + val rowsWithLinkValues = rows.filter(row => + stringFormatter.optionStringToBoolean( + row.rowMap.get("isLinkValue"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isLinkValue: ${row.rowMap.get("isLinkValue")}" + ) + ) + ) + + // Group them by LinkValue IRI. + val groupedByLinkValue: Map[String, Seq[VariableResultsRow]] = + rowsWithLinkValues.groupBy(_.rowMap("obj")) + + // For each LinkValue, check whether the user has permission to see the link, and if so, make an IncomingV1. + val maybeIncomingV1sWithFuture: Iterable[Future[Option[IncomingV1]]] = groupedByLinkValue.map { + case (linkValueIri: IRI, linkValueRows: Seq[VariableResultsRow]) => + // Convert the rows representing the LinkValue to a ValueProps. + val linkValueProps = + valueUtilV1.createValueProps(valueIri = linkValueIri, objRows = linkValueRows) + + // Convert the resulting ValueProps into a LinkValueV1 so we can check its rdf:predicate. + + for { + apiValueV1 <- valueUtilV1.makeValueV1( + valueProps = linkValueProps, + projectShortcode = resInfoWithoutQueryingOntology.project_shortcode, + responderManager = responderManager, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile + ) + + linkValueV1: LinkValueV1 = apiValueV1 match { + case linkValueV1: LinkValueV1 => linkValueV1 + case _ => + throw InconsistentRepositoryDataException( + s"Expected $linkValueIri to be a knora-base:LinkValue, but its type is ${apiValueV1.valueTypeIri}" + ) + } + + // Check the permissions on the LinkValue. + linkValuePermission = PermissionUtilADM.getUserPermissionWithValuePropsV1( + valueIri = linkValueIri, + valueProps = linkValueProps, + entityProject = Some(incomingResInfo.project_id), + userProfile = userProfileV1 + ) + } yield linkValuePermission match { + // Does the user have permission to see this link? + case Some(_) => + // Yes. Make a Some containing an IncomingV1 for the link. + Some( + IncomingV1( + ext_res_id = ExternalResourceIDV1( + id = incomingIri, + pid = linkValueV1.predicateIri + ), + resinfo = incomingResInfo, + value = incomingResInfo.firstproperty + ) ) - } yield - linkValuePermission match { - // Does the user have permission to see this link? - case Some(_) => - // Yes. Make a Some containing an IncomingV1 for the link. - Some( - IncomingV1( - ext_res_id = ExternalResourceIDV1( - id = incomingIri, - pid = linkValueV1.predicateIri - ), - resinfo = incomingResInfo, - value = incomingResInfo.firstproperty - )) - - case None => - // No. Make a None. - None - } - } + case None => + // No. Make a None. + None + } - for { + } - // turn the Iterable of Futures into a Future of an Iterable - maybeIncomingV1s: Iterable[Option[IncomingV1]] <- Future.sequence(maybeIncomingV1sWithFuture) + for { - // Filter out the Nones, which represent incoming links that the user doesn't have permission to see. - } yield maybeIncomingV1s.flatten.toVector + // turn the Iterable of Futures into a Future of an Iterable + maybeIncomingV1s: Iterable[Option[IncomingV1]] <- Future.sequence(maybeIncomingV1sWithFuture) - case None => - // The user doesn't have permission to see the referring resource. - Future(Vector.empty[IncomingV1]) - } - } yield incomingV1s + // Filter out the Nones, which represent incoming links that the user doesn't have permission to see. + } yield maybeIncomingV1s.flatten.toVector + + case None => + // The user doesn't have permission to see the referring resource. + Future(Vector.empty[IncomingV1]) + } + } yield incomingV1s }.toVector @@ -699,7 +721,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Ask the ontology responder for information about the ontology entities that we need information about. entityInfoResponse: EntityInfoGetResponseV1 <- (responderManager ? EntityInfoGetRequestV1( resourceClassIris = incomingTypes ++ linkedResourceTypes + resInfoWithoutQueryingOntology.restype_id, - propertyIris = groupedPropsByType.groupedOrdinaryValueProperties.groupedProperties.keySet ++ groupedPropsByType.groupedLinkProperties.groupedProperties.keySet, + propertyIris = + groupedPropsByType.groupedOrdinaryValueProperties.groupedProperties.keySet ++ groupedPropsByType.groupedLinkProperties.groupedProperties.keySet, userProfile = userProfile )).mapTo[EntityInfoGetResponseV1] @@ -707,20 +730,24 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceTypeIri = resInfoWithoutQueryingOntology.restype_id resourceTypeEntityInfo = entityInfoResponse.resourceClassInfoMap(resourceTypeIri) - maybeResourceTypeIconSrc = resourceTypeEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) match { + maybeResourceTypeIconSrc = resourceTypeEntityInfo.getPredicateObject( + OntologyConstants.KnoraBase.ResourceIcon + ) match { case Some(resClassIcon) => Some(valueUtilV1.makeResourceClassIconURL(resourceTypeIri, resClassIcon)) case _ => None } resourceClassLabel = resourceTypeEntityInfo.getPredicateObject( predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage)) + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ) resInfo: ResourceInfoV1 = resInfoWithoutQueryingOntology.copy( restype_label = resourceClassLabel, - restype_description = - resourceTypeEntityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage)), + restype_description = resourceTypeEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), restype_iconsrc = maybeResourceTypeIconSrc ) @@ -739,14 +766,14 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo incoming.copy( resinfo = incoming.resinfo.copy( - restype_label = - incomingResourceTypeEntityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), - restype_description = - incomingResourceTypeEntityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), + restype_label = incomingResourceTypeEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), + restype_description = incomingResourceTypeEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), restype_iconsrc = incomingResourceTypeEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) match { case Some(resClassIcon) => @@ -759,11 +786,11 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Collect all property IRIs and their cardinalities for the queried resource's type, except the ones that point to LinkValue objects or FileValue objects, // which are not relevant in this API operation. - propsAndCardinalities: Map[IRI, KnoraCardinalityInfo] = resourceTypeEntityInfo.knoraResourceCardinalities - .filterNot { - case (propertyIri, _) => - resourceTypeEntityInfo.linkValueProperties(propertyIri) || resourceTypeEntityInfo.fileValueProperties( - propertyIri) + propsAndCardinalities: Map[IRI, KnoraCardinalityInfo] = + resourceTypeEntityInfo.knoraResourceCardinalities.filterNot { case (propertyIri, _) => + resourceTypeEntityInfo.linkValueProperties(propertyIri) || resourceTypeEntityInfo.fileValueProperties( + propertyIri + ) } // Construct PropertyV1 objects for the properties that have data for this resource. @@ -781,12 +808,14 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Construct PropertyV1 objects representing properties that have no data for this resource, but are possible properties for the resource type. // To find out which properties are possible but have no data for this resource, subtract the set of properties with data from the set of possible properties. - emptyPropsIris = propsAndCardinalities.keySet -- (groupedPropsByType.groupedOrdinaryValueProperties.groupedProperties.keySet ++ groupedPropsByType.groupedLinkProperties.groupedProperties.keySet) + emptyPropsIris = + propsAndCardinalities.keySet -- (groupedPropsByType.groupedOrdinaryValueProperties.groupedProperties.keySet ++ groupedPropsByType.groupedLinkProperties.groupedProperties.keySet) // Get information from the ontology about the properties that have no data for this resource. emptyPropsInfoResponse: EntityInfoGetResponseV1 <- (responderManager ? EntityInfoGetRequestV1( propertyIris = emptyPropsIris, - userProfile = userProfile)).mapTo[EntityInfoGetResponseV1] + userProfile = userProfile + )).mapTo[EntityInfoGetResponseV1] // Create a PropertyV1 for each of those properties. emptyProps: Set[PropertyV1] = emptyPropsIris.map { propertyIri => @@ -805,16 +834,22 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo guielement = propertyEntityInfo .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) .map(guiElementIri => SalsahGuiConversions.iri2SalsahGuiElement(guiElementIri)), - label = propertyEntityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), + label = propertyEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), occurrence = Some(propsAndCardinalities(propertyIri).cardinality.toString), attributes = (propertyEntityInfo.getPredicateStringObjectsWithoutLang( - OntologyConstants.SalsahGui.GuiAttribute) + valueUtilV1.makeAttributeRestype( + OntologyConstants.SalsahGui.GuiAttribute + ) + valueUtilV1.makeAttributeRestype( propertyEntityInfo .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse(throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint")))).mkString(";"), + .getOrElse( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + ) + )).mkString(";"), value_rights = Nil ) @@ -826,9 +861,10 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo guielement = propertyEntityInfo .getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp) .map(guiElementIri => SalsahGuiConversions.iri2SalsahGuiElement(guiElementIri)), - label = propertyEntityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)), + label = propertyEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ), occurrence = Some(propsAndCardinalities(propertyIri).cardinality.toString), attributes = propertyEntityInfo .getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute) @@ -839,89 +875,97 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } // Add a fake property `__location__` if the resource has FileValues. - properties: Seq[PropertyV1] = if (resInfo.locations.nonEmpty) { - PropertyV1( - pid = "__location__", - valuetype_id = Some("-1"), - guiorder = Some(Int.MaxValue), - guielement = Some(SalsahGuiConversions.iri2SalsahGuiElement(OntologyConstants.SalsahGui.Fileupload)), - values = Vector(IntegerValueV1(0)), - value_ids = Vector("0"), - comments = Vector(None), - locations = resInfo.locations match { - case Some(locations: Seq[LocationV1]) => locations - case None => Nil - }, - value_rights = Nil - ) +: (propertiesWithData ++ emptyProps) - } else { - propertiesWithData ++ emptyProps - } + properties: Seq[PropertyV1] = + if (resInfo.locations.nonEmpty) { + PropertyV1( + pid = "__location__", + valuetype_id = Some("-1"), + guiorder = Some(Int.MaxValue), + guielement = Some(SalsahGuiConversions.iri2SalsahGuiElement(OntologyConstants.SalsahGui.Fileupload)), + values = Vector(IntegerValueV1(0)), + value_ids = Vector("0"), + comments = Vector(None), + locations = resInfo.locations match { + case Some(locations: Seq[LocationV1]) => locations + case None => Nil + }, + value_rights = Nil + ) +: (propertiesWithData ++ emptyProps) + } else { + propertiesWithData ++ emptyProps + } // Construct the API response. Return no data if the user has no view permissions on the queried resource. - resFullResponse = if (resData.rights.nonEmpty) { - ResourceFullResponseV1( - resinfo = Some(resInfo), - resdata = Some(resData), - props = Some(PropsV1(properties)), - incoming = incomingRefs, - access = "OK" - ) - } else { - throw ForbiddenException(s"User ${userProfile.id} does not have permission to query resource $resourceIri") - } + resFullResponse = + if (resData.rights.nonEmpty) { + ResourceFullResponseV1( + resinfo = Some(resInfo), + resdata = Some(resData), + props = Some(PropsV1(properties)), + incoming = incomingRefs, + access = "OK" + ) + } else { + throw ForbiddenException(s"User ${userProfile.id} does not have permission to query resource $resourceIri") + } } yield resFullResponse } /** - * Returns an instance of [[ResourceContextResponseV1]] describing the context of a resource, in Knora API v1 format. - * - * @param resourceIri the IRI of the resource to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param resinfo a flag if resinfo should be retrieved or not. - * @return a [[ResourceContextResponseV1]] describing the context of the resource. - */ - private def getContextResponseV1(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - resinfo: Boolean): Future[ResourceContextResponseV1] = { + * Returns an instance of [[ResourceContextResponseV1]] describing the context of a resource, in Knora API v1 format. + * + * @param resourceIri the IRI of the resource to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param resinfo a flag if resinfo should be retrieved or not. + * @return a [[ResourceContextResponseV1]] describing the context of the resource. + */ + private def getContextResponseV1( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + resinfo: Boolean + ): Future[ResourceContextResponseV1] = { val userProfileV1 = userProfile.asUserProfileV1 /** - * Represents a source object (e.g. a page of a book). - * - * @param id IRI of the source Object. - * @param firstprop first property of the source object. - * @param seqnum sequence number of the source object. - * @param permissionCode the current user's permissions on the source object. - * @param fileValue the file value, if any, belonging to the source object. - */ - case class SourceObject(id: IRI, - firstprop: Option[String], - seqnum: Option[Int], - permissionCode: Option[Int], - projectShortcode: String, - fileValue: Option[StillImageFileValue]) + * Represents a source object (e.g. a page of a book). + * + * @param id IRI of the source Object. + * @param firstprop first property of the source object. + * @param seqnum sequence number of the source object. + * @param permissionCode the current user's permissions on the source object. + * @param fileValue the file value, if any, belonging to the source object. + */ + case class SourceObject( + id: IRI, + firstprop: Option[String], + seqnum: Option[Int], + permissionCode: Option[Int], + projectShortcode: String, + fileValue: Option[StillImageFileValue] + ) /** - * Represents a still image file value belonging to a source object (e.g., an image representation of a page). - * - * @param id the file value IRI - * @param permissionCode the current user's permission code on the file value. - * @param image a [[StillImageFileValueV1]] - */ + * Represents a still image file value belonging to a source object (e.g., an image representation of a page). + * + * @param id the file value IRI + * @param permissionCode the current user's permission code on the file value. + * @param image a [[StillImageFileValueV1]] + */ case class StillImageFileValue(id: IRI, permissionCode: Option[Int], image: StillImageFileValueV1) /** - * Creates a [[StillImageFileValue]] from a [[VariableResultsRow]] representing a row of context query results. - * If the row doesn't contain a file value IRI, returns [[None]]. - * - * @param row a [[VariableResultsRow]] representing a [[StillImageFileValueV1]]. - * @return a [[StillImageFileValue]]. - */ - def createStillImageFileValueFromResultRow(projectShortcode: String, - row: VariableResultsRow): Option[StillImageFileValue] = { + * Creates a [[StillImageFileValue]] from a [[VariableResultsRow]] representing a row of context query results. + * If the row doesn't contain a file value IRI, returns [[None]]. + * + * @param row a [[VariableResultsRow]] representing a [[StillImageFileValueV1]]. + * @return a [[StillImageFileValue]]. + */ + def createStillImageFileValueFromResultRow( + projectShortcode: String, + row: VariableResultsRow + ): Option[StillImageFileValue] = { // if the file value has no project, get the project from the source object val fileValueProject = row.rowMap("sourceObjectAttachedToProject") @@ -948,18 +992,19 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo dimX = row.rowMap("dimX").toInt, dimY = row.rowMap("dimY").toInt ) - )) + ) + ) case None => None } } /** - * Creates a [[SourceObject]] from a [[VariableResultsRow]]. - * - * @param row a [[VariableResultsRow]] representing a [[SourceObject]]. - * @return a [[SourceObject]]. - */ + * Creates a [[SourceObject]] from a [[VariableResultsRow]]. + * + * @param row a [[VariableResultsRow]] representing a [[SourceObject]]. + * @return a [[SourceObject]]. + */ def createSourceObjectFromResultRow(projectShortcode: String, row: VariableResultsRow): SourceObject = { val sourceObjectIri = row.rowMap("sourceObject") val sourceObjectOwner = row.rowMap("sourceObjectAttachedToUser") @@ -1011,7 +1056,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo _ = if (userPermission.isEmpty) { throw ForbiddenException( - s"User $userIri does not have permission to query the context of resource $resourceIri") + s"User $userIri does not have permission to query the context of resource $resourceIri" + ) } // If this resource is part of another resource, get its parent resource. @@ -1024,117 +1070,122 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo isPartOfResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(isPartOfSparqlQuery)) .mapTo[SparqlSelectResult] - (containingResourceIriOption: Option[IRI], containingResInfoV1Option: Option[ResourceInfoV1]) <- isPartOfResponse.results.bindings match { - case rows if rows.nonEmpty => - val rowMap = rows.head.rowMap - val containingResourceIri = rowMap("containingResource") - val containingResourceProject = rowMap("containingResourceProject") + (containingResourceIriOption: Option[IRI], containingResInfoV1Option: Option[ResourceInfoV1]) <- + isPartOfResponse.results.bindings match { + case rows if rows.nonEmpty => + val rowMap = rows.head.rowMap + val containingResourceIri = rowMap("containingResource") + val containingResourceProject = rowMap("containingResourceProject") - for { - (containingResourcePermissionCode, resInfoV1) <- getResourceInfoV1( - resourceIri = containingResourceIri, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile, - queryOntology = true - ) + for { + (containingResourcePermissionCode, resInfoV1) <- getResourceInfoV1( + resourceIri = containingResourceIri, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile, + queryOntology = true + ) - linkValueIri = rowMap("linkValue") - linkValueCreator = rowMap("linkValueCreator") - linkValuePermissions = rowMap("linkValuePermissions") - linkValuePermissionCode = PermissionUtilADM.getUserPermissionV1( - entityIri = linkValueIri, - entityCreator = linkValueCreator, - entityProject = containingResourceProject, - entityPermissionLiteral = linkValuePermissions, - userProfile = userProfileV1 - ) + linkValueIri = rowMap("linkValue") + linkValueCreator = rowMap("linkValueCreator") + linkValuePermissions = rowMap("linkValuePermissions") + linkValuePermissionCode = PermissionUtilADM.getUserPermissionV1( + entityIri = linkValueIri, + entityCreator = linkValueCreator, + entityProject = containingResourceProject, + entityPermissionLiteral = linkValuePermissions, + userProfile = userProfileV1 + ) - // Allow the user to see the link only if they have permission to see both the containing resource and the link value. - permissionCode = Seq(containingResourcePermissionCode, linkValuePermissionCode).min + // Allow the user to see the link only if they have permission to see both the containing resource and the link value. + permissionCode = Seq(containingResourcePermissionCode, linkValuePermissionCode).min - } yield - permissionCode match { + } yield permissionCode match { case Some(permission) => (Some(containingResourceIri), Some(resInfoV1)) case None => (None, None) } - case _ => Future((None, None)) - } + case _ => Future((None, None)) + } - resourceContexts: Seq[ResourceContextItemV1] <- if (containingResInfoV1Option.isEmpty) { - for { - // Otherwise, do a SPARQL query that returns resources that are part of this resource (as indicated by knora-base:isPartOf). - contextSparqlQuery <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v1.txt - .getContext( - triplestore = settings.triplestoreType, - resourceIri = resourceIri - ) - .toString()) + resourceContexts: Seq[ResourceContextItemV1] <- + if (containingResInfoV1Option.isEmpty) { + for { + // Otherwise, do a SPARQL query that returns resources that are part of this resource (as indicated by knora-base:isPartOf). + contextSparqlQuery <- Future( + org.knora.webapi.messages.twirl.queries.sparql.v1.txt + .getContext( + triplestore = settings.triplestoreType, + resourceIri = resourceIri + ) + .toString() + ) - // _ = println(contextSparqlQuery) + // _ = println(contextSparqlQuery) - contextQueryResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(contextSparqlQuery)) - .mapTo[SparqlSelectResult] - rows: Seq[VariableResultsRow] = contextQueryResponse.results.bindings + contextQueryResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(contextSparqlQuery)) + .mapTo[SparqlSelectResult] + rows: Seq[VariableResultsRow] = contextQueryResponse.results.bindings - // The results consist of one row per source object. - sourceObjects: Seq[SourceObject] = rows.map { row: VariableResultsRow => - val sourceObject: IRI = row.rowMap("sourceObject") - val projectShortcode: String = sourceObject.toSmartIri.getProjectCode - .getOrElse(throw InconsistentRepositoryDataException(s"Invalid resource IRI: $sourceObject")) - createSourceObjectFromResultRow(projectShortcode, row) - } + // The results consist of one row per source object. + sourceObjects: Seq[SourceObject] = rows.map { row: VariableResultsRow => + val sourceObject: IRI = row.rowMap("sourceObject") + val projectShortcode: String = sourceObject.toSmartIri.getProjectCode + .getOrElse(throw InconsistentRepositoryDataException(s"Invalid resource IRI: $sourceObject")) + createSourceObjectFromResultRow(projectShortcode, row) + } - // Filter the source objects by eliminating the ones that the user doesn't have permission to see. - sourceObjectsWithPermissions = sourceObjects.filter(sourceObj => sourceObj.permissionCode.nonEmpty) - - contextItems: Seq[ResourceContextItemV1] = sourceObjectsWithPermissions.map { sourceObj: SourceObject => - // Generate a IIIF preview URL from the full-size image. - val (preview: Option[LocationV1], locations: Option[Seq[LocationV1]]): (Option[LocationV1], - Option[Seq[LocationV1]]) = - sourceObj.fileValue.find(fileVal => fileVal.permissionCode.nonEmpty) match { - case Some(full: StillImageFileValue) => - val preview: LocationV1 = - valueUtilV1.fileValueV12LocationV1(fullSizeImageFileValueToPreview(full.image)) - val fileVals: Seq[LocationV1] = - createMultipleImageResolutions(full.image).map(valueUtilV1.fileValueV12LocationV1) - (Some(preview), Some(Vector(preview) ++ fileVals)) - - case None => (None, None) - } + // Filter the source objects by eliminating the ones that the user doesn't have permission to see. + sourceObjectsWithPermissions = sourceObjects.filter(sourceObj => sourceObj.permissionCode.nonEmpty) + + contextItems: Seq[ResourceContextItemV1] = sourceObjectsWithPermissions.map { sourceObj: SourceObject => + // Generate a IIIF preview URL from the full-size image. + val (preview: Option[LocationV1], locations: Option[Seq[LocationV1]]): ( + Option[LocationV1], + Option[Seq[LocationV1]] + ) = + sourceObj.fileValue.find(fileVal => fileVal.permissionCode.nonEmpty) match { + case Some(full: StillImageFileValue) => + val preview: LocationV1 = + valueUtilV1.fileValueV12LocationV1(fullSizeImageFileValueToPreview(full.image)) + val fileVals: Seq[LocationV1] = + createMultipleImageResolutions(full.image).map(valueUtilV1.fileValueV12LocationV1) + (Some(preview), Some(Vector(preview) ++ fileVals)) + + case None => (None, None) + } - ResourceContextItemV1( - res_id = sourceObj.id, - preview = preview, - locations = locations, - firstprop = sourceObj.firstprop - ) - } + ResourceContextItemV1( + res_id = sourceObj.id, + preview = preview, + locations = locations, + firstprop = sourceObj.firstprop + ) + } - } yield contextItems - } else { - Future(Nil) - } + } yield contextItems + } else { + Future(Nil) + } - resinfoV1WithRegionsOption: Option[ResourceInfoV1] <- if (resinfo) { - - for { - // - // check if there are regions pointing to this resource - // - regionSparqlQuery <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v1.txt - .getRegions( - triplestore = settings.triplestoreType, - resourceIri = resourceIri - ) - .toString()) - regionQueryResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(regionSparqlQuery)) - .mapTo[SparqlSelectResult] - regionRows = regionQueryResponse.results.bindings + resinfoV1WithRegionsOption: Option[ResourceInfoV1] <- + if (resinfo) { + + for { + // + // check if there are regions pointing to this resource + // + regionSparqlQuery <- Future( + org.knora.webapi.messages.twirl.queries.sparql.v1.txt + .getRegions( + triplestore = settings.triplestoreType, + resourceIri = resourceIri + ) + .toString() + ) + regionQueryResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(regionSparqlQuery)) + .mapTo[SparqlSelectResult] + regionRows = regionQueryResponse.results.bindings - regionPropertiesSequencedFutures: Seq[Future[PropsGetForRegionV1]] = regionRows - .filter { regionRow => + regionPropertiesSequencedFutures: Seq[Future[PropsGetForRegionV1]] = regionRows.filter { regionRow => val permissionCodeForRegion = PermissionUtilADM.getUserPermissionV1( entityIri = regionRow.rowMap("region"), entityCreator = regionRow.rowMap("owner"), @@ -1145,8 +1196,7 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // ignore regions the user has no permissions on permissionCodeForRegion.nonEmpty - } - .map { regionRow => + }.map { regionRow => val regionIri = regionRow.rowMap("region") val resClass = regionRow.rowMap("resclass") // possibly we deal with a subclass of knora-base:Region @@ -1173,41 +1223,47 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo regionInfo: ClassInfoV1 = entityInfoResponse.resourceClassInfoMap(resClass) - resClassIcon: Option[String] = regionInfo.predicates.get(OntologyConstants.KnoraBase.ResourceIcon) match { + resClassIcon: Option[String] = regionInfo.predicates.get( + OntologyConstants.KnoraBase.ResourceIcon + ) match { case Some(predicateInfo: PredicateInfoV1) => Some( valueUtilV1.makeResourceClassIconURL( resClass, - predicateInfo.objects.headOption.getOrElse(throw InconsistentRepositoryDataException( - s"resourceClass $resClass has no value for ${OntologyConstants.KnoraBase.ResourceIcon}")) - )) + predicateInfo.objects.headOption.getOrElse( + throw InconsistentRepositoryDataException( + s"resourceClass $resClass has no value for ${OntologyConstants.KnoraBase.ResourceIcon}" + ) + ) + ) + ) case None => None } - } yield - PropsGetForRegionV1( - properties = propsGetV1, - res_id = regionIri, - iconsrc = resClassIcon - ) + } yield PropsGetForRegionV1( + properties = propsGetV1, + res_id = regionIri, + iconsrc = resClassIcon + ) } - // turn sequenced Futures into one Future of a sequence - regionProperties: Seq[PropsGetForRegionV1] <- Future.sequence(regionPropertiesSequencedFutures) + // turn sequenced Futures into one Future of a sequence + regionProperties: Seq[PropsGetForRegionV1] <- Future.sequence(regionPropertiesSequencedFutures) - resinfoWithRegions: Option[ResourceInfoV1] = if (regionProperties.nonEmpty) { - // regions are given, append them to resinfo - Some(resInfoV1.copy(regions = Some(regionProperties))) - } else { - // no regions given, just return resinfo - Some(resInfoV1) - } - } yield resinfoWithRegions - } else { - // resinfo is not requested - Future(None) - } + resinfoWithRegions: Option[ResourceInfoV1] = + if (regionProperties.nonEmpty) { + // regions are given, append them to resinfo + Some(resInfoV1.copy(regions = Some(regionProperties))) + } else { + // no regions given, just return resinfo + Some(resInfoV1) + } + } yield resinfoWithRegions + } else { + // resinfo is not requested + Future(None) + } resourceContextV1 = containingResourceIriOption match { case Some(_) => @@ -1248,15 +1304,17 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Returns an instance of [[ResourceRightsResponseV1]] describing the permissions on a resource for the current user, in Knora API v1 format. - * - * @param resourceIri the IRI of the resource to be queried. - * @param userProfile the profile of the user making the request. - * @return a [[ResourceRightsResponseV1]] describing the permissions on the resource. - */ - private def getRightsResponseV1(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ResourceRightsResponseV1] = { + * Returns an instance of [[ResourceRightsResponseV1]] describing the permissions on a resource for the current user, in Knora API v1 format. + * + * @param resourceIri the IRI of the resource to be queried. + * @param userProfile the profile of the user making the request. + * @return a [[ResourceRightsResponseV1]] describing the permissions on the resource. + */ + private def getRightsResponseV1( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ResourceRightsResponseV1] = for { (userPermission, _) <- getResourceInfoV1( resourceIri = resourceIri, @@ -1268,23 +1326,24 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Construct an API response. rightsResponse = ResourceRightsResponseV1(rights = userPermission) } yield rightsResponse - } /** - * Searches for resources matching the given criteria. - * - * @param searchString the string to search for. - * @param resourceTypeIri if set, restrict search to this resource type. - * @param numberOfProps the amount of describing properties to be returned for each found resource (e.g if set to two, for an incunabula book its title and creator would be returned). - * @param limitOfResults limits number of resources to be returned. - * @param userProfile the profile of the user making the request. - * @return the resources matching the given criteria. - */ - private def getResourceSearchResponseV1(searchString: String, - resourceTypeIri: Option[IRI], - numberOfProps: Int, - limitOfResults: Int, - userProfile: UserADM): Future[ResourceSearchResponseV1] = { + * Searches for resources matching the given criteria. + * + * @param searchString the string to search for. + * @param resourceTypeIri if set, restrict search to this resource type. + * @param numberOfProps the amount of describing properties to be returned for each found resource (e.g if set to two, for an incunabula book its title and creator would be returned). + * @param limitOfResults limits number of resources to be returned. + * @param userProfile the profile of the user making the request. + * @return the resources matching the given criteria. + */ + private def getResourceSearchResponseV1( + searchString: String, + resourceTypeIri: Option[IRI], + numberOfProps: Int, + limitOfResults: Int, + userProfile: UserADM + ): Future[ResourceSearchResponseV1] = { val userProfileV1 = userProfile.asUserProfileV1 val searchPhrase = MatchStringWhileTyping(searchString) @@ -1300,7 +1359,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo limitOfResults = limitOfResults, separator = StringFormatter.INFORMATION_SEPARATOR_ONE ) - .toString()) + .toString() + ) // _ = println(searchResourcesSparql) @@ -1326,93 +1386,101 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo for { resourceClassInfoResponse <- (responderManager ? EntityInfoGetRequestV1( resourceClassIris = Set(resourceClass), - userProfile = userProfile)).mapTo[EntityInfoGetResponseV1] + userProfile = userProfile + )).mapTo[EntityInfoGetResponseV1] cardinalities: Map[IRI, KnoraCardinalityInfo] = resourceClassInfoResponse .resourceClassInfoMap(resourceClass) .knoraResourceCardinalities - searchResultRow: ResourceSearchResultRowV1 = if (numberOfProps > 1) { - // The client requested more than one property per resource that was found. - - val maybeValues: Option[String] = row.rowMap.get("values") - maybeValues match { - case Some(valuesReturned) => - val valueStrings = valuesReturned.split(StringFormatter.INFORMATION_SEPARATOR_ONE) - val properties = row.rowMap("properties").split(StringFormatter.INFORMATION_SEPARATOR_ONE) - val valueOrders = - row.rowMap("valueOrders").split(StringFormatter.INFORMATION_SEPARATOR_ONE).map(_.toInt) - - val guiOrders: Array[Int] = properties.map { propertyIri => - cardinalities(propertyIri).guiOrder match { - case Some(order) => order - case None => -1 + searchResultRow: ResourceSearchResultRowV1 = + if (numberOfProps > 1) { + // The client requested more than one property per resource that was found. + + val maybeValues: Option[String] = row.rowMap.get("values") + maybeValues match { + case Some(valuesReturned) => + val valueStrings = valuesReturned.split(StringFormatter.INFORMATION_SEPARATOR_ONE) + val properties = row.rowMap("properties").split(StringFormatter.INFORMATION_SEPARATOR_ONE) + val valueOrders = + row.rowMap("valueOrders").split(StringFormatter.INFORMATION_SEPARATOR_ONE).map(_.toInt) + + val guiOrders: Array[Int] = properties.map { propertyIri => + cardinalities(propertyIri).guiOrder match { + case Some(order) => order + case None => -1 + } } - } - // create a list of three tuples, sort it by guiOrder and valueOrder and return only string values - val values: Seq[String] = - (valueStrings, guiOrders, valueOrders).zipped.toVector.sortBy(row => (row._2, row._3)).map(_._1) + // create a list of three tuples, sort it by guiOrder and valueOrder and return only string values + val values: Seq[String] = + (valueStrings, guiOrders, valueOrders).zipped.toVector.sortBy(row => (row._2, row._3)).map(_._1) - // ?values is given: it is one string to be split by separator - val propValues = values.foldLeft(Vector(firstProp)) { - case (acc, prop: String) => + // ?values is given: it is one string to be split by separator + val propValues = values.foldLeft(Vector(firstProp)) { case (acc, prop: String) => if (prop == firstProp || prop == acc.last) { // in the SPARQL results, all values are returned four times because of inclusion of permissions. If already existent, ignore prop. acc } else { acc :+ prop // append prop to List } - } + } - ResourceSearchResultRowV1( - id = row.rowMap("resourceIri"), - value = propValues.slice(0, numberOfProps), // take only as many elements as were requested by the client. - rights = permissionCode - ) + ResourceSearchResultRowV1( + id = row.rowMap("resourceIri"), + value = propValues.slice( + 0, + numberOfProps + ), // take only as many elements as were requested by the client. + rights = permissionCode + ) - case None => - log.debug("more values were asked (numberOfProps > 1), but there were none to be found") - ResourceSearchResultRowV1( - id = row.rowMap("resourceIri"), - value = Vector(firstProp), - rights = permissionCode - ) + case None => + log.debug("more values were asked (numberOfProps > 1), but there were none to be found") + ResourceSearchResultRowV1( + id = row.rowMap("resourceIri"), + value = Vector(firstProp), + rights = permissionCode + ) + } + } else { + // ?firstProp is sufficient: the client requested just one property per resource that was found + ResourceSearchResultRowV1( + id = row.rowMap("resourceIri"), + value = Vector(firstProp), + rights = permissionCode + ) } - } else { - // ?firstProp is sufficient: the client requested just one property per resource that was found - ResourceSearchResultRowV1( - id = row.rowMap("resourceIri"), - value = Vector(firstProp), - rights = permissionCode - ) - } } yield searchResultRow } resources: Seq[ResourceSearchResultRowV1] <- Future.sequence(resultFutures) - filteredResources = resources.filter(_.rights.nonEmpty) // user must have permissions to see resource (must not be None) + filteredResources = resources.filter( + _.rights.nonEmpty + ) // user must have permissions to see resource (must not be None) } yield ResourceSearchResponseV1(resources = filteredResources) } /** - * Create multiple resources and attach the given values to them. - * - * @param resourcesToCreate collection of ResourceRequests . - * @param projectIri IRI of the project . - * @param apiRequestID the the ID of the API request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[MultipleResourceCreateResponseV1]] informing the client about the new resources. - */ - private def createMultipleNewResources(resourcesToCreate: Seq[OneOfMultipleResourceCreateRequestV1], - projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[MultipleResourceCreateResponseV1] = { + * Create multiple resources and attach the given values to them. + * + * @param resourcesToCreate collection of ResourceRequests . + * @param projectIri IRI of the project . + * @param apiRequestID the the ID of the API request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[MultipleResourceCreateResponseV1]] informing the client about the new resources. + */ + private def createMultipleNewResources( + resourcesToCreate: Seq[OneOfMultipleResourceCreateRequestV1], + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[MultipleResourceCreateResponseV1] = { // Convert all the image metadata in the request to FileValueContentV2 instances, so we // can use ResourceUtilV2.doSipiPostUpdate after updating the triplestore. val fileValueContentV2s: Seq[FileValueContentV2] = resourcesToCreate.flatMap { resourceToCreate => @@ -1444,7 +1512,9 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceProjectIri: IRI = projectADM.id - _ = if (resourceProjectIri == OntologyConstants.KnoraAdmin.SystemProject || resourceProjectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { + _ = if ( + resourceProjectIri == OntologyConstants.KnoraAdmin.SystemProject || resourceProjectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { throw BadRequestException(s"Resources cannot be created in project $resourceProjectIri") } @@ -1485,17 +1555,22 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceClassOntologyIris: Set[SmartIri] = resourceClasses.map(_.toSmartIri.getOntologyFromEntity) readOntologyMetadataV2: ReadOntologyMetadataV2 <- (responderManager ? OntologyMetadataGetByIriRequestV2( resourceClassOntologyIris, - requestingUser)).mapTo[ReadOntologyMetadataV2] + requestingUser + )).mapTo[ReadOntologyMetadataV2] _ = for (ontologyMetadata <- readOntologyMetadataV2.ontologies) { val ontologyProjectIri: IRI = ontologyMetadata.projectIri .getOrElse( - throw InconsistentRepositoryDataException(s"Ontology ${ontologyMetadata.ontologyIri} has no project")) + throw InconsistentRepositoryDataException(s"Ontology ${ontologyMetadata.ontologyIri} has no project") + ) .toString - if (resourceProjectIri != ontologyProjectIri && !(ontologyMetadata.ontologyIri.isKnoraBuiltInDefinitionIri || ontologyMetadata.ontologyIri.isKnoraSharedDefinitionIri)) { + if ( + resourceProjectIri != ontologyProjectIri && !(ontologyMetadata.ontologyIri.isKnoraBuiltInDefinitionIri || ontologyMetadata.ontologyIri.isKnoraSharedDefinitionIri) + ) { throw BadRequestException( - s"Cannot create a resource in project $resourceProjectIri with a resource class from ontology ${ontologyMetadata.ontologyIri}, which belongs to another project and is not shared") + s"Cannot create a resource in project $resourceProjectIri with a resource class from ontology ${ontologyMetadata.ontologyIri}, which belongs to another project and is not shared" + ) } } @@ -1516,15 +1591,14 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo userProfile = requestingUser )).mapTo[EntityInfoGetResponseV1] - propertyEntityInfoMapsPerResource: Map[IRI, Map[IRI, PropertyInfoV1]] = resourceClassesEntityInfoResponse.resourceClassInfoMap - .map { - case (resourceClassIri, resourceEntityInfo) => - val propertyEntityInfoMapForResource: Map[IRI, PropertyInfoV1] = - resourceEntityInfo.knoraResourceCardinalities.keySet.map { propertyIri => - (propertyIri, propertyEntityInfoResponse.propertyInfoMap(propertyIri)) - }.toMap + propertyEntityInfoMapsPerResource: Map[IRI, Map[IRI, PropertyInfoV1]] = + resourceClassesEntityInfoResponse.resourceClassInfoMap.map { case (resourceClassIri, resourceEntityInfo) => + val propertyEntityInfoMapForResource: Map[IRI, PropertyInfoV1] = + resourceEntityInfo.knoraResourceCardinalities.keySet.map { propertyIri => + (propertyIri, propertyEntityInfoResponse.propertyInfoMap(propertyIri)) + }.toMap - (resourceClassIri, propertyEntityInfoMapForResource) + (resourceClassIri, propertyEntityInfoMapForResource) } // Get the default object access permissions for all the resource classes and properties used in the request. @@ -1544,49 +1618,57 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } defaultResourceClassAccessPermissionsSeq: Vector[(IRI, String)] <- Future.sequence( - defaultResourceClassAccessPermissionsFutures) - defaultResourceClassAccessPermissionsMap = new ErrorHandlingMap(defaultResourceClassAccessPermissionsSeq.toMap, { - key: IRI => + defaultResourceClassAccessPermissionsFutures + ) + defaultResourceClassAccessPermissionsMap = new ErrorHandlingMap( + defaultResourceClassAccessPermissionsSeq.toMap, + { key: IRI => s"No default resource class access permissions found for resource class $key" - }) + } + ) - defaultPropertyAccessPermissionsFutures: Map[IRI, Future[Map[IRI, String]]] = propertyEntityInfoMapsPerResource - .map { - case (resourceClassIri, propertyInfoMap) => - val propertyPermissionFutures = propertyInfoMap.keys.map { propertyIri => - for { - defaultObjectAccessPermissions <- { - responderManager ? DefaultObjectAccessPermissionsStringForPropertyGetADM( - projectIri = projectIri, - resourceClassIri = resourceClassIri, - propertyIri = propertyIri, - targetUser = requestingUser, - requestingUser = KnoraSystemInstances.Users.SystemUser - ) - }.mapTo[DefaultObjectAccessPermissionsStringResponseADM] - } yield (propertyIri, defaultObjectAccessPermissions.permissionLiteral) - } + defaultPropertyAccessPermissionsFutures: Map[IRI, Future[Map[IRI, String]]] = + propertyEntityInfoMapsPerResource.map { case (resourceClassIri, propertyInfoMap) => + val propertyPermissionFutures = propertyInfoMap.keys.map { propertyIri => + for { + defaultObjectAccessPermissions <- { + responderManager ? DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri = projectIri, + resourceClassIri = resourceClassIri, + propertyIri = propertyIri, + targetUser = requestingUser, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + }.mapTo[DefaultObjectAccessPermissionsStringResponseADM] + } yield (propertyIri, defaultObjectAccessPermissions.permissionLiteral) + } - val propertyPermissionsFuture: Future[Map[IRI, String]] = - Future.sequence(propertyPermissionFutures).map(_.toMap) - (resourceClassIri, propertyPermissionsFuture) + val propertyPermissionsFuture: Future[Map[IRI, String]] = + Future.sequence(propertyPermissionFutures).map(_.toMap) + (resourceClassIri, propertyPermissionsFuture) } defaultPropertyAccessPermissionsMapContents: Map[IRI, Map[IRI, String]] <- ActorUtil.sequenceFuturesInMap( - defaultPropertyAccessPermissionsFutures) + defaultPropertyAccessPermissionsFutures + ) - defaultPropertyAccessPermissionsMapToWrap: Map[IRI, Map[IRI, String]] = defaultPropertyAccessPermissionsMapContents - .map { + defaultPropertyAccessPermissionsMapToWrap: Map[IRI, Map[IRI, String]] = + defaultPropertyAccessPermissionsMapContents.map { case (resourceClassIri: IRI, propertyPermissions: Map[IRI, String]) => - resourceClassIri -> new ErrorHandlingMap(propertyPermissions, { key: IRI => - s"No default access permissions found for property $key in resource class $resourceClassIri" - }) + resourceClassIri -> new ErrorHandlingMap( + propertyPermissions, + { key: IRI => + s"No default access permissions found for property $key in resource class $resourceClassIri" + } + ) } defaultPropertyAccessPermissionsMap: Map[IRI, Map[IRI, String]] = new ErrorHandlingMap( - defaultPropertyAccessPermissionsMapToWrap.toMap, { key: IRI => + defaultPropertyAccessPermissionsMapToWrap.toMap, + { key: IRI => s"No default property access permissions found for resource class $key" - }) + } + ) resourceCreationFutures: Seq[Future[SparqlTemplateResourceToCreate]] = resourcesToCreate.map { resourceCreateRequest: OneOfMultipleResourceCreateRequestV1 => @@ -1596,13 +1678,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Check user's PermissionProfile (part of UserADM) to see if the user has the permission to // create a new resource in the given project. defaultObjectAccessPermissions: String <- FastFuture( - Try(defaultResourceClassAccessPermissionsMap(resourceCreateRequest.resourceTypeIri))) + Try(defaultResourceClassAccessPermissionsMap(resourceCreateRequest.resourceTypeIri)) + ) // _ = log.debug(s"createNewResource - defaultObjectAccessPermissions: $defaultObjectAccessPermissions") _ = if (resourceCreateRequest.resourceTypeIri == OntologyConstants.KnoraBase.Resource) { throw BadRequestException( - s"Instances of knora-base:Resource cannot be created, only instances of subclasses") + s"Instances of knora-base:Resource cannot be created, only instances of subclasses" + ) } resourceIri = clientResourceIDsToResourceIris(resourceCreateRequest.clientResourceID) @@ -1622,50 +1706,49 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo ) // Convert each LinkToClientIDUpdateV1 into a LinkUpdateV1. - resourceValuesWithLinkTargetIris: Map[IRI, Seq[CreateValueV1WithComment]] = resourceCreateRequest.values - .map { - case (propertyIri, valuesWithComments) => - val valuesWithLinkTargetIris = valuesWithComments.map { valueToCreate => - valueToCreate.updateValueV1 match { - case LinkToClientIDUpdateV1(clientIDForTargetResource) => - CreateValueV1WithComment( - LinkUpdateV1( - targetResourceIri = clientResourceIDsToResourceIris(clientIDForTargetResource), - targetExists = false - ) + resourceValuesWithLinkTargetIris: Map[IRI, Seq[CreateValueV1WithComment]] = + resourceCreateRequest.values.map { case (propertyIri, valuesWithComments) => + val valuesWithLinkTargetIris = valuesWithComments.map { valueToCreate => + valueToCreate.updateValueV1 match { + case LinkToClientIDUpdateV1(clientIDForTargetResource) => + CreateValueV1WithComment( + LinkUpdateV1( + targetResourceIri = clientResourceIDsToResourceIris(clientIDForTargetResource), + targetExists = false ) - case _ => valueToCreate - } + ) + case _ => valueToCreate } + } - propertyIri -> valuesWithLinkTargetIris + propertyIri -> valuesWithLinkTargetIris } // generate sparql for every resource - generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1 <- generateSparqlForValuesOfNewResource( - projectIri = projectIri, - resourceIri = resourceIri, - resourceClassIri = resourceCreateRequest.resourceTypeIri, - defaultPropertyAccessPermissions = - defaultPropertyAccessPermissionsMap(resourceCreateRequest.resourceTypeIri), - values = resourceValuesWithLinkTargetIris, - clientResourceIDsToResourceIris = clientResourceIDsToResourceIris, - creationDate = creationDate, - fileValues = fileValues, - featureFactoryConfig = featureFactoryConfig, - userProfile = requestingUser, - apiRequestID = apiRequestID - ) + generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1 <- + generateSparqlForValuesOfNewResource( + projectIri = projectIri, + resourceIri = resourceIri, + resourceClassIri = resourceCreateRequest.resourceTypeIri, + defaultPropertyAccessPermissions = + defaultPropertyAccessPermissionsMap(resourceCreateRequest.resourceTypeIri), + values = resourceValuesWithLinkTargetIris, + clientResourceIDsToResourceIris = clientResourceIDsToResourceIris, + creationDate = creationDate, + fileValues = fileValues, + featureFactoryConfig = featureFactoryConfig, + userProfile = requestingUser, + apiRequestID = apiRequestID + ) - } yield - SparqlTemplateResourceToCreate( - resourceIri = resourceIri, - permissions = defaultObjectAccessPermissions, - sparqlForValues = generateSparqlForValuesResponse.insertSparql, - resourceClassIri = resourceCreateRequest.resourceTypeIri, - resourceLabel = resourceCreateRequest.label, - resourceCreationDate = creationDate - ) + } yield SparqlTemplateResourceToCreate( + resourceIri = resourceIri, + permissions = defaultObjectAccessPermissions, + sparqlForValues = generateSparqlForValuesResponse.insertSparql, + resourceClassIri = resourceCreateRequest.resourceTypeIri, + resourceLabel = resourceCreateRequest.label, + resourceCreationDate = creationDate + ) } // change sequence of futures to future of sequences @@ -1705,17 +1788,19 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Asks Sipi to to move temporary image files to permanent storage if a triplestore update was successful, - * or to delete the temporary files if the triplestore update failed. - * - * @param updateFuture the future resulting from the triplestore update. - * @param fileValueContentV2s the file values that were created, if any. - * @param requestingUser the user making the request. - * @return `updateFuture`, or a failed future (if Sipi failed to move a file to permanent storage). - */ - private def doSipiPostUpdateForResources[T <: UpdateResultInProject](updateFuture: Future[T], - fileValueContentV2s: Seq[FileValueContentV2], - requestingUser: UserADM): Future[T] = { + * Asks Sipi to to move temporary image files to permanent storage if a triplestore update was successful, + * or to delete the temporary files if the triplestore update failed. + * + * @param updateFuture the future resulting from the triplestore update. + * @param fileValueContentV2s the file values that were created, if any. + * @param requestingUser the user making the request. + * @return `updateFuture`, or a failed future (if Sipi failed to move a file to permanent storage). + */ + private def doSipiPostUpdateForResources[T <: UpdateResultInProject]( + updateFuture: Future[T], + fileValueContentV2s: Seq[FileValueContentV2], + requestingUser: UserADM + ): Future[T] = { val resultFutures: Seq[Future[T]] = fileValueContentV2s.map { valueContent => ResourceUtilV2.doSipiPostUpdate( updateFuture = updateFuture, @@ -1734,122 +1819,129 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Check the resource to be created. - * - * @param resourceClassIri type of resource. - * @param resourceClassInfo ontology information about the resource class. - * @param propertyInfoMap ontology information about the properties attached to the resource class. - * @param values values to be created for resource. If `linkTargetsAlreadyExist` is true, any links must be represented as [[LinkUpdateV1]] instances. - * Otherwise, they must be represented as [[LinkToClientIDUpdateV1]] instances, so that appropriate error messages can - * be generated for links to missing resources. - * @param convertedFile an already converted file to be attached to the resource. - * @param clientResourceIDsToResourceClasses for each client resource ID, the IRI of the resource's class. Used only if `linkTargetsAlreadyExist` is false. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a tuple (IRI, Vector[CreateValueV1WithComment]) containing the IRI of the resource and a collection of holders of [[UpdateValueV1]] and comment. - */ - private def checkResource(resourceClassIri: IRI, - resourceClassInfo: ClassInfoV1, - propertyInfoMap: Map[IRI, PropertyInfoV1], - values: Map[IRI, Seq[CreateValueV1WithComment]], - convertedFile: Option[FileValueV1], - clientResourceIDsToResourceClasses: Map[String, IRI] = new ErrorHandlingMap[IRI, IRI]( - toWrap = Map.empty[IRI, IRI], - errorTemplateFun = { key => - s"Resource $key is the target of a link, but was not provided in the request" - }, - errorFun = { errorMsg => - throw BadRequestException(errorMsg) - } - ), - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Option[(IRI, Vector[CreateValueV1WithComment])]] = { + * Check the resource to be created. + * + * @param resourceClassIri type of resource. + * @param resourceClassInfo ontology information about the resource class. + * @param propertyInfoMap ontology information about the properties attached to the resource class. + * @param values values to be created for resource. If `linkTargetsAlreadyExist` is true, any links must be represented as [[LinkUpdateV1]] instances. + * Otherwise, they must be represented as [[LinkToClientIDUpdateV1]] instances, so that appropriate error messages can + * be generated for links to missing resources. + * @param convertedFile an already converted file to be attached to the resource. + * @param clientResourceIDsToResourceClasses for each client resource ID, the IRI of the resource's class. Used only if `linkTargetsAlreadyExist` is false. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a tuple (IRI, Vector[CreateValueV1WithComment]) containing the IRI of the resource and a collection of holders of [[UpdateValueV1]] and comment. + */ + private def checkResource( + resourceClassIri: IRI, + resourceClassInfo: ClassInfoV1, + propertyInfoMap: Map[IRI, PropertyInfoV1], + values: Map[IRI, Seq[CreateValueV1WithComment]], + convertedFile: Option[FileValueV1], + clientResourceIDsToResourceClasses: Map[String, IRI] = new ErrorHandlingMap[IRI, IRI]( + toWrap = Map.empty[IRI, IRI], + errorTemplateFun = { key => + s"Resource $key is the target of a link, but was not provided in the request" + }, + errorFun = { errorMsg => + throw BadRequestException(errorMsg) + } + ), + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Option[(IRI, Vector[CreateValueV1WithComment])]] = { for { // Check that each submitted value is consistent with the knora-base:objectClassConstraint of the property that is supposed to // point to it. propertyObjectClassConstraintChecks: Seq[Unit] <- Future.sequence { - values.foldLeft(Vector.empty[Future[Unit]]) { - case (acc, (propertyIri, valuesWithComments)) => - val propertyInfo = - propertyInfoMap.getOrElse(propertyIri, throw NotFoundException(s"Property not found: $propertyIri")) - val propertyObjectClassConstraint = - propertyInfo.getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint).getOrElse { - throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint") - } + values.foldLeft(Vector.empty[Future[Unit]]) { case (acc, (propertyIri, valuesWithComments)) => + val propertyInfo = + propertyInfoMap.getOrElse(propertyIri, throw NotFoundException(s"Property not found: $propertyIri")) + val propertyObjectClassConstraint = + propertyInfo.getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint).getOrElse { + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + } - acc ++ valuesWithComments.map { valueV1WithComment: CreateValueV1WithComment => - valueV1WithComment.updateValueV1 match { - case LinkToClientIDUpdateV1(targetClientID) => - // We're creating a link to a resource that doesn't exist yet, because it - // will be created in the same transaction. Check that it will belong to a - // suitable class. - val checkSubClassRequest = CheckSubClassRequestV1( - subClassIri = clientResourceIDsToResourceClasses(targetClientID), - superClassIri = propertyObjectClassConstraint, - userProfile = userProfile - ) + acc ++ valuesWithComments.map { valueV1WithComment: CreateValueV1WithComment => + valueV1WithComment.updateValueV1 match { + case LinkToClientIDUpdateV1(targetClientID) => + // We're creating a link to a resource that doesn't exist yet, because it + // will be created in the same transaction. Check that it will belong to a + // suitable class. + val checkSubClassRequest = CheckSubClassRequestV1( + subClassIri = clientResourceIDsToResourceClasses(targetClientID), + superClassIri = propertyObjectClassConstraint, + userProfile = userProfile + ) - for { - subClassResponse <- (responderManager ? checkSubClassRequest).mapTo[CheckSubClassResponseV1] + for { + subClassResponse <- (responderManager ? checkSubClassRequest).mapTo[CheckSubClassResponseV1] - _ = if (!subClassResponse.isSubClass) { - throw OntologyConstraintException( - s"Resource $targetClientID cannot be the target of property $propertyIri, because it is not a member of OWL class $propertyObjectClassConstraint") - } - } yield () + _ = if (!subClassResponse.isSubClass) { + throw OntologyConstraintException( + s"Resource $targetClientID cannot be the target of property $propertyIri, because it is not a member of OWL class $propertyObjectClassConstraint" + ) + } + } yield () - case linkUpdate: LinkUpdateV1 => - // We're creating a link to an existing resource. Check that it belongs to a - // suitable class. - for { - checkTargetClassResponse <- checkResourceClass( - resourceIri = linkUpdate.targetResourceIri, - owlClass = propertyObjectClassConstraint, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile - ).mapTo[ResourceCheckClassResponseV1] - - _ = if (!checkTargetClassResponse.isInClass) { - throw OntologyConstraintException( - s"Resource ${linkUpdate.targetResourceIri} cannot be the target of property $propertyIri, because it is not a member of OWL class $propertyObjectClassConstraint") - } - } yield () - - case otherValue => - // We're creating an ordinary value. Check that its type is valid for the property's - // knora-base:objectClassConstraint. - valueUtilV1.checkValueTypeForPropertyObjectClassConstraint( - propertyIri = propertyIri, - propertyObjectClassConstraint = propertyObjectClassConstraint, - valueType = otherValue.valueTypeIri, - responderManager = responderManager, + case linkUpdate: LinkUpdateV1 => + // We're creating a link to an existing resource. Check that it belongs to a + // suitable class. + for { + checkTargetClassResponse <- checkResourceClass( + resourceIri = linkUpdate.targetResourceIri, + owlClass = propertyObjectClassConstraint, + featureFactoryConfig = featureFactoryConfig, userProfile = userProfile - ) - } + ).mapTo[ResourceCheckClassResponseV1] + + _ = if (!checkTargetClassResponse.isInClass) { + throw OntologyConstraintException( + s"Resource ${linkUpdate.targetResourceIri} cannot be the target of property $propertyIri, because it is not a member of OWL class $propertyObjectClassConstraint" + ) + } + } yield () + + case otherValue => + // We're creating an ordinary value. Check that its type is valid for the property's + // knora-base:objectClassConstraint. + valueUtilV1.checkValueTypeForPropertyObjectClassConstraint( + propertyIri = propertyIri, + propertyObjectClassConstraint = propertyObjectClassConstraint, + valueType = otherValue.valueTypeIri, + responderManager = responderManager, + userProfile = userProfile + ) } + } } } // Check that the resource class has a suitable cardinality for each submitted value. - _ = values.foreach { - case (propertyIri, valuesForProperty) => - val cardinalityInfo = resourceClassInfo.knoraResourceCardinalities.getOrElse( - propertyIri, - throw OntologyConstraintException( - s"Resource class $resourceClassIri has no cardinality for property $propertyIri")) + _ = values.foreach { case (propertyIri, valuesForProperty) => + val cardinalityInfo = resourceClassInfo.knoraResourceCardinalities.getOrElse( + propertyIri, + throw OntologyConstraintException( + s"Resource class $resourceClassIri has no cardinality for property $propertyIri" + ) + ) - if ((cardinalityInfo.cardinality == Cardinality.MayHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveOne) && valuesForProperty.size > 1) { - throw OntologyConstraintException( - s"Resource class $resourceClassIri does not allow more than one value for property $propertyIri") - } + if ( + (cardinalityInfo.cardinality == Cardinality.MayHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveOne) && valuesForProperty.size > 1 + ) { + throw OntologyConstraintException( + s"Resource class $resourceClassIri does not allow more than one value for property $propertyIri" + ) + } } // Check that no required values are missing. - requiredProps: Set[IRI] = resourceClassInfo.knoraResourceCardinalities.filter { - case (_, cardinalityInfo) => - cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome + requiredProps: Set[IRI] = resourceClassInfo.knoraResourceCardinalities.filter { case (_, cardinalityInfo) => + cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome }.keySet -- resourceClassInfo.linkValueProperties -- resourceClassInfo.fileValueProperties // exclude link value and file value properties from checking submittedPropertyIris = values.keySet @@ -1857,59 +1949,63 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo _ = if (!requiredProps.subsetOf(submittedPropertyIris)) { val missingProps = (requiredProps -- submittedPropertyIris).mkString(", ") throw OntologyConstraintException( - s"Values were not submitted for the following property or properties, which are required by resource class $resourceClassIri: $missingProps") + s"Values were not submitted for the following property or properties, which are required by resource class $resourceClassIri: $missingProps" + ) } // check if a file value is required by the ontology - fileValues: Option[(IRI, Vector[CreateValueV1WithComment])] = if (resourceClassInfo.fileValueProperties.nonEmpty) { - convertedFile match { - case Some(converted) => - // TODO: check if the file type returned by Sipi corresponds to the expected fileValue property in resourceClassInfo.fileValueProperties.head - Some(resourceClassInfo.fileValueProperties.head -> Vector(CreateValueV1WithComment(converted))) - - case None => throw BadRequestException(s"File required but none submitted") - } + fileValues: Option[(IRI, Vector[CreateValueV1WithComment])] = + if (resourceClassInfo.fileValueProperties.nonEmpty) { + convertedFile match { + case Some(converted) => + // TODO: check if the file type returned by Sipi corresponds to the expected fileValue property in resourceClassInfo.fileValueProperties.head + Some(resourceClassInfo.fileValueProperties.head -> Vector(CreateValueV1WithComment(converted))) + + case None => throw BadRequestException(s"File required but none submitted") + } - } else { - if (convertedFile.nonEmpty) { - throw BadRequestException( - s"File params are given but resource class $resourceClassIri does not allow any representation") } else { - None + if (convertedFile.nonEmpty) { + throw BadRequestException( + s"File params are given but resource class $resourceClassIri does not allow any representation" + ) + } else { + None + } } - } } yield fileValues } /** - * Generates SPARQL to create the values for a resource. - * - * @param projectIri the IRI of the project. - * @param resourceIri the IRI of the resource to be created. - * @param resourceClassIri the IRI of the resource class. - * @param defaultPropertyAccessPermissions the default object access permissions of each property attached to the resource class. - * @param values the values to be created for resource. - * @param fileValues the file values to be created with the resource. - * @param clientResourceIDsToResourceIris a map of client resource IDs (which may appear in standoff link tags - * in values passed to this method) to the IRIs that will be used for - * those resources. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @param apiRequestID the the ID of the API request. - * @return a [[GenerateSparqlToCreateMultipleValuesResponseV1]] returns response of generation of SPARQL for multiple values. - */ + * Generates SPARQL to create the values for a resource. + * + * @param projectIri the IRI of the project. + * @param resourceIri the IRI of the resource to be created. + * @param resourceClassIri the IRI of the resource class. + * @param defaultPropertyAccessPermissions the default object access permissions of each property attached to the resource class. + * @param values the values to be created for resource. + * @param fileValues the file values to be created with the resource. + * @param clientResourceIDsToResourceIris a map of client resource IDs (which may appear in standoff link tags + * in values passed to this method) to the IRIs that will be used for + * those resources. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @param apiRequestID the the ID of the API request. + * @return a [[GenerateSparqlToCreateMultipleValuesResponseV1]] returns response of generation of SPARQL for multiple values. + */ def generateSparqlForValuesOfNewResource( - projectIri: IRI, - resourceIri: IRI, - resourceClassIri: IRI, - defaultPropertyAccessPermissions: Map[IRI, String], - values: Map[IRI, Seq[CreateValueV1WithComment]], - fileValues: Option[(IRI, Vector[CreateValueV1WithComment])], - clientResourceIDsToResourceIris: Map[String, IRI], - creationDate: Instant, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID): Future[GenerateSparqlToCreateMultipleValuesResponseV1] = { + projectIri: IRI, + resourceIri: IRI, + resourceClassIri: IRI, + defaultPropertyAccessPermissions: Map[IRI, String], + values: Map[IRI, Seq[CreateValueV1WithComment]], + fileValues: Option[(IRI, Vector[CreateValueV1WithComment])], + clientResourceIDsToResourceIris: Map[String, IRI], + creationDate: Instant, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID + ): Future[GenerateSparqlToCreateMultipleValuesResponseV1] = for { // Ask the values responder for the SPARQL statements that are needed to create the values. generateSparqlForValuesRequest <- Future( @@ -1924,26 +2020,29 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo featureFactoryConfig = featureFactoryConfig, userProfile = userProfile, apiRequestID = apiRequestID - )) + ) + ) - generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1 <- (responderManager ? generateSparqlForValuesRequest) - .mapTo[GenerateSparqlToCreateMultipleValuesResponseV1] + generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1 <- + (responderManager ? generateSparqlForValuesRequest) + .mapTo[GenerateSparqlToCreateMultipleValuesResponseV1] } yield generateSparqlForValuesResponse - } /** - * Generates SPARQL to create multiple resources in a single update operation. - * - * @param resourcesToCreate Collection of the resources to be created . - * @param projectIri IRI of the project . - * @param namedGraph the named graph the resources belongs to. - * @param creatorIri the creator of the resources to be created. - * @return a [String] returns a Sparql query for creating the resources and their values . - */ - def generateSparqlForNewResources(resourcesToCreate: Seq[SparqlTemplateResourceToCreate], - projectIri: IRI, - namedGraph: IRI, - creatorIri: IRI): String = { + * Generates SPARQL to create multiple resources in a single update operation. + * + * @param resourcesToCreate Collection of the resources to be created . + * @param projectIri IRI of the project . + * @param namedGraph the named graph the resources belongs to. + * @param creatorIri the creator of the resources to be created. + * @return a [String] returns a Sparql query for creating the resources and their values . + */ + def generateSparqlForNewResources( + resourcesToCreate: Seq[SparqlTemplateResourceToCreate], + projectIri: IRI, + namedGraph: IRI, + creatorIri: IRI + ): String = // Generate SPARQL for creating the resources, and include the SPARQL for creating the values of every resource. org.knora.webapi.messages.twirl.queries.sparql.v1.txt .createNewResources( @@ -1954,27 +2053,28 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo creatorIri = creatorIri ) .toString() - } /** - * Verifies the created resource and its values. - * - * @param resourceIri IRI of the created resource . - * @param creatorIri the creator of the resources to be created. - * @param createNewResourceSparql Sparql query to create the resource . - * @param generateSparqlForValuesResponse Sparql statement for creation of values of resource. - * @param projectADM the project in which the resource was created. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ResourceCreateResponseV1]] containing information about the created resource . - */ - def verifyResourceCreated(resourceIri: IRI, - creatorIri: IRI, - createNewResourceSparql: String, - generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1, - projectADM: ProjectADM, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ResourceCreateResponseV1] = { + * Verifies the created resource and its values. + * + * @param resourceIri IRI of the created resource . + * @param creatorIri the creator of the resources to be created. + * @param createNewResourceSparql Sparql query to create the resource . + * @param generateSparqlForValuesResponse Sparql statement for creation of values of resource. + * @param projectADM the project in which the resource was created. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ResourceCreateResponseV1]] containing information about the created resource . + */ + def verifyResourceCreated( + resourceIri: IRI, + creatorIri: IRI, + createNewResourceSparql: String, + generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1, + projectADM: ProjectADM, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ResourceCreateResponseV1] = // Verify that the resource was created. for { createdResourcesSparql <- Future( @@ -1983,15 +2083,18 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo triplestore = settings.triplestoreType, resourceIri = resourceIri ) - .toString()) + .toString() + ) createdResourceResponse <- (storeManager ? SparqlSelectRequest(createdResourcesSparql)).mapTo[SparqlSelectResult] _ = if (createdResourceResponse.results.bindings.isEmpty) { log.error( - s"Attempted a SPARQL update to create a new resource, but it inserted no rows:\n\n$createNewResourceSparql") + s"Attempted a SPARQL update to create a new resource, but it inserted no rows:\n\n$createNewResourceSparql" + ) throw UpdateNotPerformedException( - s"Resource $resourceIri was not created. Please report this as a possible bug.") + s"Resource $resourceIri was not created. Please report this as a possible bug." + ) } // Verify that all the requested values were created. @@ -2002,19 +2105,25 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo userProfile = userProfile ) - verifyMultipleValueCreationResponse: VerifyMultipleValueCreationResponseV1 <- (responderManager ? verifyCreateValuesRequest) - .mapTo[VerifyMultipleValueCreationResponseV1] + verifyMultipleValueCreationResponse: VerifyMultipleValueCreationResponseV1 <- + (responderManager ? verifyCreateValuesRequest) + .mapTo[VerifyMultipleValueCreationResponseV1] // Convert CreateValueResponseV1 objects to ResourceCreateValueResponseV1 objects. - resourceCreateValueResponses: Map[IRI, Seq[ResourceCreateValueResponseV1]] = verifyMultipleValueCreationResponse.verifiedValues - .map { + resourceCreateValueResponses: Map[IRI, Seq[ResourceCreateValueResponseV1]] = + verifyMultipleValueCreationResponse.verifiedValues.map { case (propIri: IRI, values: Seq[CreateValueResponseV1]) => - (propIri, values.map { valueResponse: CreateValueResponseV1 => - valueUtilV1.convertCreateValueResponseV1ToResourceCreateValueResponseV1(creatorIri = creatorIri, - propertyIri = propIri, - resourceIri = resourceIri, - valueResponse = valueResponse) - }) + ( + propIri, + values.map { valueResponse: CreateValueResponseV1 => + valueUtilV1.convertCreateValueResponseV1ToResourceCreateValueResponseV1( + creatorIri = creatorIri, + propertyIri = propIri, + resourceIri = resourceIri, + valueResponse = valueResponse + ) + } + ) } apiResponse: ResourceCreateResponseV1 = ResourceCreateResponseV1( @@ -2023,35 +2132,36 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo projectADM = projectADM ) } yield apiResponse - } /** - * Does pre-update checks, creates a resource, and verifies that it was created. - * - * @param resourceClassIri the IRI of the resource class. - * @param projectADM the project in which the resource should be created. - * @param label the `rdfs:label` of the resource to be created. - * @param resourceIri the IRI of the resource to be created. - * @param values the values to be attached to the resource. - * @param file a file that has been uploaded to Sipi's temporary storage and should be attached to the resource. - * @param creatorIri the creator of the resource to be created. - * @param namedGraph the named graph the resource belongs to. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @param apiRequestID the request ID used for locking the resource. - * @return a [[ResourceCreateResponseV1]] containing information about the created resource. - */ - def createResourceAndCheck(resourceClassIri: IRI, - projectADM: ProjectADM, - label: String, - resourceIri: IRI, - values: Map[IRI, Seq[CreateValueV1WithComment]], - file: Option[FileValueV1], - creatorIri: IRI, - namedGraph: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[ResourceCreateResponseV1] = { + * Does pre-update checks, creates a resource, and verifies that it was created. + * + * @param resourceClassIri the IRI of the resource class. + * @param projectADM the project in which the resource should be created. + * @param label the `rdfs:label` of the resource to be created. + * @param resourceIri the IRI of the resource to be created. + * @param values the values to be attached to the resource. + * @param file a file that has been uploaded to Sipi's temporary storage and should be attached to the resource. + * @param creatorIri the creator of the resource to be created. + * @param namedGraph the named graph the resource belongs to. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @param apiRequestID the request ID used for locking the resource. + * @return a [[ResourceCreateResponseV1]] containing information about the created resource. + */ + def createResourceAndCheck( + resourceClassIri: IRI, + projectADM: ProjectADM, + label: String, + resourceIri: IRI, + values: Map[IRI, Seq[CreateValueV1WithComment]], + file: Option[FileValueV1], + creatorIri: IRI, + namedGraph: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[ResourceCreateResponseV1] = { val fileValueContent: Option[FileValueContentV2] = file.map(_.toFileValueContentV2) val updateFuture = for { @@ -2085,8 +2195,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo defaultResourceClassAccessPermissions = defaultResourceClassAccessPermissionsResponse.permissionLiteral - defaultPropertyAccessPermissionsFutures: Iterable[Future[(IRI, String)]] = propertyEntityInfoResponse.propertyInfoMap.keys - .map { propertyIri => + defaultPropertyAccessPermissionsFutures: Iterable[Future[(IRI, String)]] = + propertyEntityInfoResponse.propertyInfoMap.keys.map { propertyIri => for { defaultObjectAccessPermissions <- { responderManager ? DefaultObjectAccessPermissionsStringForPropertyGetADM( @@ -2101,7 +2211,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } defaultPropertyAccessPermissionsIterable: Iterable[(IRI, String)] <- Future.sequence( - defaultPropertyAccessPermissionsFutures) + defaultPropertyAccessPermissionsFutures + ) defaultPropertyAccessPermissions = defaultPropertyAccessPermissionsIterable.toMap fileValues <- checkResource( @@ -2119,19 +2230,20 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Make a timestamp for the resource and its values. creationDate: Instant = Instant.now - generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1 <- generateSparqlForValuesOfNewResource( - projectIri = projectADM.id, - resourceIri = resourceIri, - resourceClassIri = resourceClassIri, - defaultPropertyAccessPermissions = defaultPropertyAccessPermissions, - values = values, - fileValues = fileValues, - clientResourceIDsToResourceIris = Map.empty[String, IRI], - creationDate = creationDate, - featureFactoryConfig = featureFactoryConfig, - userProfile = requestingUser, - apiRequestID = apiRequestID - ) + generateSparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV1 <- + generateSparqlForValuesOfNewResource( + projectIri = projectADM.id, + resourceIri = resourceIri, + resourceClassIri = resourceClassIri, + defaultPropertyAccessPermissions = defaultPropertyAccessPermissions, + values = values, + fileValues = fileValues, + clientResourceIDsToResourceIris = Map.empty[String, IRI], + creationDate = creationDate, + featureFactoryConfig = featureFactoryConfig, + userProfile = requestingUser, + apiRequestID = apiRequestID + ) resourcesToCreate: Seq[SparqlTemplateResourceToCreate] = Seq( SparqlTemplateResourceToCreate( @@ -2174,25 +2286,27 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Creates a new resource and attaches the given values to it. - * - * @param resourceClassIri the resource type of the resource to be created. - * @param values the values to be attached to the resource. - * @param file a file that has been uploaded to Sipi's temporary storage and should be attached to the resource. - * @param projectIri the project the resource belongs to. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the user that is creating the resource - * @param apiRequestID the ID of this API request. - * @return a [[ResourceCreateResponseV1]] informing the client about the new resource. - */ - private def createNewResource(resourceClassIri: IRI, - label: String, - values: Map[IRI, Seq[CreateValueV1WithComment]], - file: Option[FileValueV1] = None, - projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID): Future[ResourceCreateResponseV1] = { + * Creates a new resource and attaches the given values to it. + * + * @param resourceClassIri the resource type of the resource to be created. + * @param values the values to be attached to the resource. + * @param file a file that has been uploaded to Sipi's temporary storage and should be attached to the resource. + * @param projectIri the project the resource belongs to. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the user that is creating the resource + * @param apiRequestID the ID of this API request. + * @return a [[ResourceCreateResponseV1]] informing the client about the new resource. + */ + private def createNewResource( + resourceClassIri: IRI, + label: String, + values: Map[IRI, Seq[CreateValueV1WithComment]], + file: Option[FileValueV1] = None, + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID + ): Future[ResourceCreateResponseV1] = for { // Get user's IRI and don't allow anonymous users to create resources. @@ -2221,7 +2335,9 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceProjectIri: IRI = projectResponse.project.id - _ = if (resourceProjectIri == OntologyConstants.KnoraAdmin.SystemProject || resourceProjectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { + _ = if ( + resourceProjectIri == OntologyConstants.KnoraAdmin.SystemProject || resourceProjectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { throw BadRequestException(s"Resources cannot be created in project $resourceProjectIri") } @@ -2230,16 +2346,20 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceClassOntologyIri: SmartIri = resourceClassIri.toSmartIri.getOntologyFromEntity readOntologyMetadataV2: ReadOntologyMetadataV2 <- (responderManager ? OntologyMetadataGetByIriRequestV2( Set(resourceClassOntologyIri), - userProfile)).mapTo[ReadOntologyMetadataV2] + userProfile + )).mapTo[ReadOntologyMetadataV2] ontologyMetadata: OntologyMetadataV2 = readOntologyMetadataV2.ontologies.headOption .getOrElse(throw BadRequestException(s"Ontology $resourceClassOntologyIri not found")) ontologyProjectIri: IRI = ontologyMetadata.projectIri .getOrElse(throw InconsistentRepositoryDataException(s"Ontology $resourceClassOntologyIri has no project")) .toString - _ = if (resourceProjectIri != ontologyProjectIri && !(ontologyMetadata.ontologyIri.isKnoraBuiltInDefinitionIri || ontologyMetadata.ontologyIri.isKnoraSharedDefinitionIri)) { + _ = if ( + resourceProjectIri != ontologyProjectIri && !(ontologyMetadata.ontologyIri.isKnoraBuiltInDefinitionIri || ontologyMetadata.ontologyIri.isKnoraSharedDefinitionIri) + ) { throw BadRequestException( - s"Cannot create a resource in project $resourceProjectIri with resource class $resourceClassIri, which is defined in a non-shared ontology in another project") + s"Cannot create a resource in project $resourceProjectIri with resource class $resourceClassIri, which is defined in a non-shared ontology in another project" + ) } namedGraph = StringFormatter.getGeneralInstance.projectDataNamedGraphV2(projectResponse.project) @@ -2247,11 +2367,12 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Check user's PermissionProfile (part of UserADM) to see if the user has the permission to // create a new resource in the given project. - _ = if (!userProfile.permissions.hasPermissionFor(ResourceCreateOperation(resourceClassIri), - resourceProjectIri, - None)) { + _ = if ( + !userProfile.permissions.hasPermissionFor(ResourceCreateOperation(resourceClassIri), resourceProjectIri, None) + ) { throw ForbiddenException( - s"User $userIri does not have permissions to create a resource in project $resourceProjectIri") + s"User $userIri does not have permissions to create a resource in project $resourceProjectIri" + ) } result: ResourceCreateResponseV1 <- IriLocker.runWithIriLock( @@ -2270,20 +2391,19 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo featureFactoryConfig = featureFactoryConfig, requestingUser = userProfile, apiRequestID = apiRequestID - ) + ) ) } yield result - } /** - * Marks a resource as deleted. - * - * @param resourceDeleteRequest a [[ResourceDeleteRequestV1]]. - * @return a [[ResourceDeleteResponseV1]]. - */ + * Marks a resource as deleted. + * + * @param resourceDeleteRequest a [[ResourceDeleteRequestV1]]. + * @return a [[ResourceDeleteResponseV1]]. + */ private def deleteResourceV1(resourceDeleteRequest: ResourceDeleteRequestV1): Future[ResourceDeleteResponseV1] = { - def makeTaskFuture(userIri: IRI): Future[ResourceDeleteResponseV1] = { + def makeTaskFuture(userIri: IRI): Future[ResourceDeleteResponseV1] = for { // Check that the user has permission to delete the resource. (permissionCode, resourceInfo) <- getResourceInfoV1( @@ -2293,11 +2413,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo queryOntology = false ) - _ = if (!PermissionUtilADM.impliesPermissionCodeV1(userHasPermissionCode = permissionCode, - userNeedsPermission = - OntologyConstants.KnoraBase.DeletePermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = permissionCode, + userNeedsPermission = OntologyConstants.KnoraBase.DeletePermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to mark resource ${resourceDeleteRequest.resourceIri} as deleted") + s"User $userIri does not have permission to mark resource ${resourceDeleteRequest.resourceIri} as deleted" + ) } projectInfoResponse <- { @@ -2337,15 +2461,19 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo sparqlSelectResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] rows = sparqlSelectResponse.results.bindings - _ = if (rows.isEmpty || !stringFormatter.optionStringToBoolean( - rows.head.rowMap.get("isDeleted"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}"))) { + _ = if ( + rows.isEmpty || !stringFormatter.optionStringToBoolean( + rows.head.rowMap.get("isDeleted"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}" + ) + ) + ) { throw UpdateNotPerformedException( - s"Resource ${resourceDeleteRequest.resourceIri} was not marked as deleted. Please report this as a possible bug.") + s"Resource ${resourceDeleteRequest.resourceIri} was not marked as deleted. Please report this as a possible bug." + ) } } yield ResourceDeleteResponseV1(id = resourceDeleteRequest.resourceIri) - } for { // Don't allow anonymous users to delete resources. @@ -2367,19 +2495,20 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Checks whether a resource belongs to a certain OWL class or to a subclass of that class. - * - * @param resourceIri the IRI of the resource to be checked. - * @param owlClass the IRI of the OWL class to compare the resource's class to. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ResourceCheckClassResponseV1]]. - */ - private def checkResourceClass(resourceIri: IRI, - owlClass: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ResourceCheckClassResponseV1] = { - + * Checks whether a resource belongs to a certain OWL class or to a subclass of that class. + * + * @param resourceIri the IRI of the resource to be checked. + * @param owlClass the IRI of the OWL class to compare the resource's class to. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ResourceCheckClassResponseV1]]. + */ + private def checkResourceClass( + resourceIri: IRI, + owlClass: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ResourceCheckClassResponseV1] = for { // Check that the user has permission to view the resource. (permissionCode, resourceInfo) <- getResourceInfoV1( @@ -2389,9 +2518,12 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo queryOntology = false ) - _ = if (!PermissionUtilADM.impliesPermissionCodeV1(userHasPermissionCode = permissionCode, - userNeedsPermission = - OntologyConstants.KnoraBase.RestrictedViewPermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = permissionCode, + userNeedsPermission = OntologyConstants.KnoraBase.RestrictedViewPermission + ) + ) { throw ForbiddenException(s"User ${userProfile.id} does not have permission to view resource $resourceIri") } @@ -2404,25 +2536,25 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo subClassResponse <- (responderManager ? checkSubClassRequest).mapTo[CheckSubClassResponseV1] } yield ResourceCheckClassResponseV1(isInClass = subClassResponse.isSubClass) - } /** - * Changes a resource's label. - * - * @param resourceIri the IRI of the resource. - * @param label the new label. - * @param apiRequestID the the ID of the API request. - * @param userProfile the profile of the user making the request. - * @return a [[ChangeResourceLabelResponseV1]]. - */ - private def changeResourceLabelV1(resourceIri: IRI, - label: String, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ChangeResourceLabelResponseV1] = { - - def makeTaskFuture(userIri: IRI): Future[ChangeResourceLabelResponseV1] = { - + * Changes a resource's label. + * + * @param resourceIri the IRI of the resource. + * @param label the new label. + * @param apiRequestID the the ID of the API request. + * @param userProfile the profile of the user making the request. + * @return a [[ChangeResourceLabelResponseV1]]. + */ + private def changeResourceLabelV1( + resourceIri: IRI, + label: String, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ChangeResourceLabelResponseV1] = { + + def makeTaskFuture(userIri: IRI): Future[ChangeResourceLabelResponseV1] = for { // get the resource's permissions (permissionCode, resourceInfo) <- getResourceInfoV1( @@ -2433,11 +2565,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo ) // check if the given user may change its label - _ = if (!PermissionUtilADM.impliesPermissionCodeV1(userHasPermissionCode = permissionCode, - userNeedsPermission = - OntologyConstants.KnoraBase.ModifyPermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = permissionCode, + userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to change the label of resource $resourceIri") + s"User $userIri does not have permission to change the label of resource $resourceIri" + ) } projectInfoResponse <- { @@ -2485,11 +2621,11 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // we expect exactly one row to be returned if the label was updated correctly in the data. _ = if (rows.length != 1) { throw UpdateNotPerformedException( - s"The label of the resource $resourceIri was not updated correctly. Please report this as a possible bug.") + s"The label of the resource $resourceIri was not updated correctly. Please report this as a possible bug." + ) } } yield ChangeResourceLabelResponseV1(res_id = resourceIri, label = label) - } for { // Don't allow anonymous users to change a resource's label. @@ -2515,20 +2651,22 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Helper methods. /** - * Returns a [[ResourceInfoV1]] describing a resource. - * - * @param resourceIri the IRI of the resource to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the user that is making the request. - * @param queryOntology if `true`, the ontology will be queried for information about the resource type, and the [[ResourceInfoV1]] - * will include `restype_label`, `restype_description`, and `restype_iconsrc`. Otherwise, those member variables - * will be empty. - * @return a tuple (permission, [[ResourceInfoV1]]) describing the resource. - */ - private def getResourceInfoV1(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - queryOntology: Boolean): Future[(Option[Int], ResourceInfoV1)] = { + * Returns a [[ResourceInfoV1]] describing a resource. + * + * @param resourceIri the IRI of the resource to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the user that is making the request. + * @param queryOntology if `true`, the ontology will be queried for information about the resource type, and the [[ResourceInfoV1]] + * will include `restype_label`, `restype_description`, and `restype_iconsrc`. Otherwise, those member variables + * will be empty. + * @return a tuple (permission, [[ResourceInfoV1]]) describing the resource. + */ + private def getResourceInfoV1( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + queryOntology: Boolean + ): Future[(Option[Int], ResourceInfoV1)] = for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -2536,7 +2674,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo triplestore = settings.triplestoreType, resourceIri = resourceIri ) - .toString()) + .toString() + ) resInfoResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] resInfoResponseRows = resInfoResponse.results.bindings @@ -2548,21 +2687,20 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo queryOntology = queryOntology ) } yield resInfo - } /** - * - * Queries the properties for the given resource. - * - * @param resourceIri the IRI of the given resource. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[PropertiesGetResponseV1]] representing the properties of the given resource. - */ - private def getPropertiesV1(resourceIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[PropertiesGetResponseV1] = { - + * Queries the properties for the given resource. + * + * @param resourceIri the IRI of the given resource. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[PropertiesGetResponseV1]] representing the properties of the given resource. + */ + private def getPropertiesV1( + resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[PropertiesGetResponseV1] = for { // get resource class of the specified resource resclassSparqlQuery <- Future( @@ -2571,7 +2709,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo triplestore = settings.triplestoreType, resourceIri = resourceIri ) - .toString()) + .toString() + ) resclassQueryResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(resclassSparqlQuery)) .mapTo[SparqlSelectResult] @@ -2591,41 +2730,45 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } yield PropertiesGetResponseV1(PropsGetV1(propertiesGetV1)) - } - /** - * Queries the properties that have values for a given resource, and returns a [[Seq]] of [[PropertyV1]] objects representing - * those properties and their values. - * - * @param resourceIri the IRI of the resource to be queried. - * @param maybeResourceTypeIri an optional IRI representing the resource's class. If provided, an additional query will be done - * to get ontology-based information, such as labels and cardinalities, which will be included in - * the returned [[PropertyV1]] objects. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[Seq]] of [[PropertyV1]] objects representing the properties that have values for the resource. - */ - private def getResourceProperties(resourceIri: IRI, - maybeResourceTypeIri: Option[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Seq[PropertyV1]] = { + * Queries the properties that have values for a given resource, and returns a [[Seq]] of [[PropertyV1]] objects representing + * those properties and their values. + * + * @param resourceIri the IRI of the resource to be queried. + * @param maybeResourceTypeIri an optional IRI representing the resource's class. If provided, an additional query will be done + * to get ontology-based information, such as labels and cardinalities, which will be included in + * the returned [[PropertyV1]] objects. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[Seq]] of [[PropertyV1]] objects representing the properties that have values for the resource. + */ + private def getResourceProperties( + resourceIri: IRI, + maybeResourceTypeIri: Option[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Seq[PropertyV1]] = for { groupedPropsByType: GroupedPropertiesByType <- getGroupedProperties(resourceIri) // TODO: Should we get rid of the tuple and replace it by a case class? - (propertyInfoMap: Map[IRI, PropertyInfoV1], - resourceEntityInfoMap: Map[IRI, ClassInfoV1], - propsAndCardinalities: Map[IRI, KnoraCardinalityInfo]) <- maybeResourceTypeIri match { + ( + propertyInfoMap: Map[IRI, PropertyInfoV1], + resourceEntityInfoMap: Map[IRI, ClassInfoV1], + propsAndCardinalities: Map[IRI, KnoraCardinalityInfo] + ) <- maybeResourceTypeIri match { case Some(resourceTypeIri) => - val propertyEntityIris - : Set[IRI] = groupedPropsByType.groupedOrdinaryValueProperties.groupedProperties.keySet ++ groupedPropsByType.groupedLinkProperties.groupedProperties.keySet + val propertyEntityIris: Set[IRI] = + groupedPropsByType.groupedOrdinaryValueProperties.groupedProperties.keySet ++ groupedPropsByType.groupedLinkProperties.groupedProperties.keySet val resourceEntityIris: Set[IRI] = Set(resourceTypeIri) for { - entityInfoResponse <- (responderManager ? EntityInfoGetRequestV1(resourceClassIris = resourceEntityIris, - propertyIris = propertyEntityIris, - userProfile = userProfile)) + entityInfoResponse <- (responderManager ? EntityInfoGetRequestV1( + resourceClassIris = resourceEntityIris, + propertyIris = propertyEntityIris, + userProfile = userProfile + )) .mapTo[EntityInfoGetResponseV1] resourceEntityInfoMap: Map[IRI, ClassInfoV1] = entityInfoResponse.resourceClassInfoMap propertyInfoMap: Map[IRI, PropertyInfoV1] = entityInfoResponse.propertyInfoMap @@ -2633,10 +2776,9 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceTypeEntityInfo = resourceEntityInfoMap(resourceTypeIri) // all properties and their cardinalities for the queried resource's type, except the ones that point to LinkValue objects - propsAndCardinalities: Map[IRI, KnoraCardinalityInfo] = resourceTypeEntityInfo.knoraResourceCardinalities - .filterNot { - case (propertyIri, _) => - resourceTypeEntityInfo.linkValueProperties(propertyIri) + propsAndCardinalities: Map[IRI, KnoraCardinalityInfo] = + resourceTypeEntityInfo.knoraResourceCardinalities.filterNot { case (propertyIri, _) => + resourceTypeEntityInfo.linkValueProperties(propertyIri) } } yield (propertyInfoMap, resourceEntityInfoMap, propsAndCardinalities) @@ -2658,26 +2800,27 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo userProfile = userProfile ) } yield queryResult - } /** - * Converts a SPARQL query result into a [[ResourceInfoV1]]. Expects the query result to contain columns called `p` (predicate), - * `o` (object), `objPred` (file value predicate, if `o` is a file value), and `objObj` (file value object). - * - * @param resourceIri the IRI of the resource. - * @param resInfoResponseRows the SPARQL query result. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the user that is making the request. - * @param queryOntology if `true`, the ontology will be queried for information about the resource type, and the [[ResourceInfoV1]] - * will include `restype_label`, `restype_description`, and `restype_iconsrc`. Otherwise, those member variables - * will be empty. - * @return a tuple (permission, [[ResourceInfoV1]]) describing the resource. - */ - private def makeResourceInfoV1(resourceIri: IRI, - resInfoResponseRows: Seq[VariableResultsRow], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - queryOntology: Boolean): Future[(Option[Int], ResourceInfoV1)] = { + * Converts a SPARQL query result into a [[ResourceInfoV1]]. Expects the query result to contain columns called `p` (predicate), + * `o` (object), `objPred` (file value predicate, if `o` is a file value), and `objObj` (file value object). + * + * @param resourceIri the IRI of the resource. + * @param resInfoResponseRows the SPARQL query result. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the user that is making the request. + * @param queryOntology if `true`, the ontology will be queried for information about the resource type, and the [[ResourceInfoV1]] + * will include `restype_label`, `restype_description`, and `restype_iconsrc`. Otherwise, those member variables + * will be empty. + * @return a tuple (permission, [[ResourceInfoV1]]) describing the resource. + */ + private def makeResourceInfoV1( + resourceIri: IRI, + resInfoResponseRows: Seq[VariableResultsRow], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + queryOntology: Boolean + ): Future[(Option[Int], ResourceInfoV1)] = { val userProfileV1 = userProfile.asUserProfileV1 if (resInfoResponseRows.isEmpty) { @@ -2687,8 +2830,10 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Extract the permission-relevant assertions from the query results. permissionRelevantAssertions: Seq[(IRI, IRI)] <- Future( - PermissionUtilADM.filterPermissionRelevantAssertions(resInfoResponseRows.map(row => - (row.rowMap("prop"), row.rowMap("obj"))))) + PermissionUtilADM.filterPermissionRelevantAssertions( + resInfoResponseRows.map(row => (row.rowMap("prop"), row.rowMap("obj"))) + ) + ) maybeResourceProjectStatement: Option[(IRI, IRI)] = permissionRelevantAssertions.find { case (subject, predicate) => subject == OntologyConstants.KnoraBase.AttachedToProject @@ -2696,40 +2841,41 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo resourceProject = maybeResourceProjectStatement .getOrElse( - throw InconsistentRepositoryDataException(s"Resource $resourceIri has no knora-base:attachedToProject")) + throw InconsistentRepositoryDataException(s"Resource $resourceIri has no knora-base:attachedToProject") + ) ._2 projectShortcode: String = resourceIri.toSmartIri.getProjectCode .getOrElse(throw InconsistentRepositoryDataException(s"Invalid resource IRI $resourceIri")) // Get the rows describing file values from the query results, grouped by file value IRI. fileValueGroupedRows: Seq[(IRI, Seq[VariableResultsRow])] = resInfoResponseRows - .filter( - row => - stringFormatter.optionStringToBoolean( - row.rowMap.get("isFileValue"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isFileValue: ${row.rowMap.get("isFileValue")}"))) + .filter(row => + stringFormatter.optionStringToBoolean( + row.rowMap.get("isFileValue"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isFileValue: ${row.rowMap.get("isFileValue")}" + ) + ) + ) .groupBy(row => row.rowMap("obj")) .toVector // Convert the file value rows to ValueProps objects, and filter out the ones that the user doesn't have permission to see. - valuePropsForFileValues: Seq[(IRI, ValueProps)] = fileValueGroupedRows - .map { - case (fileValueIri, fileValueRows) => - (fileValueIri, valueUtilV1.createValueProps(fileValueIri, fileValueRows)) - } - .filter { - case (fileValueIri, fileValueProps) => - val permissionCode = PermissionUtilADM.getUserPermissionWithValuePropsV1( - valueIri = fileValueIri, - valueProps = fileValueProps, - entityProject = Some(resourceProject), - userProfile = userProfileV1 - ) - PermissionUtilADM.impliesPermissionCodeV1(userHasPermissionCode = permissionCode, - userNeedsPermission = - OntologyConstants.KnoraBase.RestrictedViewPermission) - } + valuePropsForFileValues: Seq[(IRI, ValueProps)] = fileValueGroupedRows.map { + case (fileValueIri, fileValueRows) => + (fileValueIri, valueUtilV1.createValueProps(fileValueIri, fileValueRows)) + }.filter { case (fileValueIri, fileValueProps) => + val permissionCode = PermissionUtilADM.getUserPermissionWithValuePropsV1( + valueIri = fileValueIri, + valueProps = fileValueProps, + entityProject = Some(resourceProject), + userProfile = userProfileV1 + ) + PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = permissionCode, + userNeedsPermission = OntologyConstants.KnoraBase.RestrictedViewPermission + ) + } // Convert the ValueProps objects into FileValueV1 objects fileValuesWithFuture: Seq[Future[FileValueV1]] = valuePropsForFileValues.map { @@ -2743,13 +2889,13 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo userProfile = userProfile ) - } yield - valueV1 match { - case fileValueV1: FileValueV1 => fileValueV1 - case otherValueV1 => - throw InconsistentRepositoryDataException( - s"Value $fileValueIri is not a knora-base:FileValue, it is an instance of ${otherValueV1.valueTypeIri}") - } + } yield valueV1 match { + case fileValueV1: FileValueV1 => fileValueV1 + case otherValueV1 => + throw InconsistentRepositoryDataException( + s"Value $fileValueIri is not a knora-base:FileValue, it is an instance of ${otherValueV1.valueTypeIri}" + ) + } } fileValues: Seq[FileValueV1] <- Future.sequence(fileValuesWithFuture) @@ -2768,7 +2914,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // Convert the file values into LocationV1 objects as required by Knora API v1. locations: Seq[LocationV1] = preview.toVector ++ fileValues.flatMap { fileValueV1 => createMultipleImageResolutions(fileValueV1).map(oneResolution => - valueUtilV1.fileValueV12LocationV1(oneResolution)) + valueUtilV1.fileValueV12LocationV1(oneResolution) + ) } // Get the user's permission on the resource. @@ -2781,36 +2928,45 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // group the SPARQL results by the predicate "prop" and map each row to a Seq of objects "obj", etc. (getting rid of VariableResultsRow). groupedByPredicateToWrap: Map[IRI, Seq[Map[String, String]]] = resInfoResponseRows .groupBy(row => row.rowMap("prop")) - .map { - case (predicate: IRI, rows: Seq[VariableResultsRow]) => (predicate, rows.map(_.rowMap - "prop")) + .map { case (predicate: IRI, rows: Seq[VariableResultsRow]) => + (predicate, rows.map(_.rowMap - "prop")) } - groupedByPredicate = new ErrorHandlingMap(groupedByPredicateToWrap, { key: IRI => - s"Resource $resourceIri has no $key" - }) + groupedByPredicate = new ErrorHandlingMap( + groupedByPredicateToWrap, + { key: IRI => + s"Resource $resourceIri has no $key" + } + ) // Query the ontology about the resource's OWL class. - (restype_label, restype_description, restype_iconsrc) <- if (queryOntology) { - val resTypeIri = groupedByPredicate(OntologyConstants.Rdf.Type).head("obj") + (restype_label, restype_description, restype_iconsrc) <- + if (queryOntology) { + val resTypeIri = groupedByPredicate(OntologyConstants.Rdf.Type).head("obj") - for { - entityInfoResponse <- (responderManager ? EntityInfoGetRequestV1(resourceClassIris = Set(resTypeIri), - userProfile = userProfile)) - .mapTo[EntityInfoGetResponseV1] - entityInfo = entityInfoResponse.resourceClassInfoMap(resTypeIri) - label = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage)) - description = entityInfo.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Comment, - preferredLangs = - Some(userProfile.lang, settings.fallbackLanguage)) - iconsrc = entityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) match { - case Some(resClassIcon) => Some(valueUtilV1.makeResourceClassIconURL(resTypeIri, resClassIcon)) - case _ => None - } - } yield (label, description, iconsrc) - } else { - Future(None, None, None) - } + for { + entityInfoResponse <- (responderManager ? EntityInfoGetRequestV1( + resourceClassIris = Set(resTypeIri), + userProfile = userProfile + )) + .mapTo[EntityInfoGetResponseV1] + entityInfo = entityInfoResponse.resourceClassInfoMap(resTypeIri) + label = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ) + description = entityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Comment, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ) + iconsrc = entityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) match { + case Some(resClassIcon) => Some(valueUtilV1.makeResourceClassIconURL(resTypeIri, resClassIcon)) + case _ => None + } + } yield (label, description, iconsrc) + } else { + Future(None, None, None) + } resourceInfo = ResourceInfoV1( restype_id = groupedByPredicate(OntologyConstants.Rdf.Type).head("obj"), @@ -2832,16 +2988,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Queries the properties that have values for a given resource, and partitions the results into two categories: (1) results for properties that point - * to ordinary Knora values ([[GroupedPropertiesByType.groupedOrdinaryValueProperties]]), (2) properties that point to link value objects (reifications of links to resources), - * and (3) properties that point to other resources ([[GroupedPropertiesByType.groupedLinkProperties]]). - * Then groups the results in each category first by property, then by property object, and finally by Knora object predicate, each level represented by a case class defined in [[GroupedProps]]. - * - * @param resourceIri the IRI of the resource to be queried. - * @return a [[GroupedPropertiesByType]] containing properties that point to ordinary value properties, link value properties, and link properties. - */ - private def getGroupedProperties(resourceIri: IRI): Future[GroupedPropertiesByType] = { - + * Queries the properties that have values for a given resource, and partitions the results into two categories: (1) results for properties that point + * to ordinary Knora values ([[GroupedPropertiesByType.groupedOrdinaryValueProperties]]), (2) properties that point to link value objects (reifications of links to resources), + * and (3) properties that point to other resources ([[GroupedPropertiesByType.groupedLinkProperties]]). + * Then groups the results in each category first by property, then by property object, and finally by Knora object predicate, each level represented by a case class defined in [[GroupedProps]]. + * + * @param resourceIri the IRI of the resource to be queried. + * @return a [[GroupedPropertiesByType]] containing properties that point to ordinary value properties, link value properties, and link properties. + */ + private def getGroupedProperties(resourceIri: IRI): Future[GroupedPropertiesByType] = for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -2849,62 +3004,68 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo triplestore = settings.triplestoreType, resourceIri = resourceIri ) - .toString()) + .toString() + ) // _ = println(sparqlQuery) resPropsResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] // Partition the property result rows into rows with value properties and rows with link properties. - (rowsWithLinks: Seq[VariableResultsRow], rowsWithValues: Seq[VariableResultsRow]) = resPropsResponse.results.bindings - .partition(_.rowMap.get("isLinkProp").exists(_.toBoolean)) + (rowsWithLinks: Seq[VariableResultsRow], rowsWithValues: Seq[VariableResultsRow]) = + resPropsResponse.results.bindings + .partition(_.rowMap.get("isLinkProp").exists(_.toBoolean)) // Partition the rows with values into rows with ordinary values and rows with link values (reifications). (rowsWithLinkValues: Seq[VariableResultsRow], rowsWithOrdinaryValues: Seq[VariableResultsRow]) = rowsWithValues .partition(_.rowMap.get("isLinkValueProp").exists(_.toBoolean)) - } yield - valueUtilV1.createGroupedPropsByType(rowsWithOrdinaryValues = rowsWithOrdinaryValues, - rowsWithLinkValues = rowsWithLinkValues, - rowsWithLinks = rowsWithLinks) - } + } yield valueUtilV1.createGroupedPropsByType( + rowsWithOrdinaryValues = rowsWithOrdinaryValues, + rowsWithLinkValues = rowsWithLinkValues, + rowsWithLinks = rowsWithLinks + ) /** - * Converts grouped property query results returned by the `getGroupedProperties` method to a [[Seq]] of [[PropertyV1]] objects, optionally - * using ontology-based data if provided. - * - * @param groupedPropertiesByType The [[GroupedPropertiesByType]] returned by `getGroupedProperties` containing the resuls of the SPARQL query. - * @param propertyInfoMap a [[Map]] of entity IRIs to [[PropertyInfoV1]] objects. If this [[Map]] is not empty, it will be used to include - * ontology-based information in the returned [[PropertyV1]] objects. - * @param resourceEntityInfoMap a [[Map]] of entity IRIs to [[ClassInfoV1]] objects. If this [[Map]] is not empty, it will be used to include - * ontology-based information for linking properties in the returned [[PropertyV1]] objects. - * @param propsAndCardinalities a [[Map]] of property IRIs to their cardinalities in the class of the queried resource. If this [[Map]] is not - * empty, it will be used to include cardinalities in the returned [[PropertyV1]] objects. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[Seq]] of [[PropertyV1]] objects. - */ - private def queryResults2PropertyV1s(containingResourceIri: IRI, - projectShortcode: String, - groupedPropertiesByType: GroupedPropertiesByType, - propertyInfoMap: Map[IRI, PropertyInfoV1], - resourceEntityInfoMap: Map[IRI, ClassInfoV1], - propsAndCardinalities: Map[IRI, KnoraCardinalityInfo], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Seq[PropertyV1]] = { + * Converts grouped property query results returned by the `getGroupedProperties` method to a [[Seq]] of [[PropertyV1]] objects, optionally + * using ontology-based data if provided. + * + * @param groupedPropertiesByType The [[GroupedPropertiesByType]] returned by `getGroupedProperties` containing the resuls of the SPARQL query. + * @param propertyInfoMap a [[Map]] of entity IRIs to [[PropertyInfoV1]] objects. If this [[Map]] is not empty, it will be used to include + * ontology-based information in the returned [[PropertyV1]] objects. + * @param resourceEntityInfoMap a [[Map]] of entity IRIs to [[ClassInfoV1]] objects. If this [[Map]] is not empty, it will be used to include + * ontology-based information for linking properties in the returned [[PropertyV1]] objects. + * @param propsAndCardinalities a [[Map]] of property IRIs to their cardinalities in the class of the queried resource. If this [[Map]] is not + * empty, it will be used to include cardinalities in the returned [[PropertyV1]] objects. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[Seq]] of [[PropertyV1]] objects. + */ + private def queryResults2PropertyV1s( + containingResourceIri: IRI, + projectShortcode: String, + groupedPropertiesByType: GroupedPropertiesByType, + propertyInfoMap: Map[IRI, PropertyInfoV1], + resourceEntityInfoMap: Map[IRI, ClassInfoV1], + propsAndCardinalities: Map[IRI, KnoraCardinalityInfo], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Seq[PropertyV1]] = { val userProfileV1 = userProfile.asUserProfileV1 /** - * Constructs a [[PropertyV1]]. - * - * @param propertyIri the IRI of the property. - * @param propertyCardinality an optional cardinality that the queried resource's class assigns to the property. - * @param propertyEntityInfo an optional [[PropertyInfoV1]] describing the property. - * @param valueObjects a list of [[ValueObjectV1]] instances representing the `knora-base:Value` objects associated with the property in the queried resource. - * @return a [[PropertyV1]]. - */ - def makePropertyV1(propertyIri: IRI, - propertyCardinality: Option[KnoraCardinalityInfo], - propertyEntityInfo: Option[PropertyInfoV1], - valueObjects: Seq[ValueObjectV1]): PropertyV1 = { + * Constructs a [[PropertyV1]]. + * + * @param propertyIri the IRI of the property. + * @param propertyCardinality an optional cardinality that the queried resource's class assigns to the property. + * @param propertyEntityInfo an optional [[PropertyInfoV1]] describing the property. + * @param valueObjects a list of [[ValueObjectV1]] instances representing the `knora-base:Value` objects associated with the property in the queried resource. + * @return a [[PropertyV1]]. + */ + def makePropertyV1( + propertyIri: IRI, + propertyCardinality: Option[KnoraCardinalityInfo], + propertyEntityInfo: Option[PropertyInfoV1], + valueObjects: Seq[ValueObjectV1] + ): PropertyV1 = PropertyV1( pid = propertyIri, valuetype_id = propertyEntityInfo.flatMap { row => @@ -2918,10 +3079,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo guiorder = propertyCardinality.flatMap(_.guiOrder), guielement = propertyEntityInfo.flatMap( _.getPredicateObject(OntologyConstants.SalsahGui.GuiElementProp).map(guiElementIri => - SalsahGuiConversions.iri2SalsahGuiElement(guiElementIri))), + SalsahGuiConversions.iri2SalsahGuiElement(guiElementIri) + ) + ), label = propertyEntityInfo.flatMap( - _.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage))), + _.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ) + ), occurrence = propertyCardinality.map(_.cardinality.toString), attributes = propertyEntityInfo match { case Some(entityInfo) => @@ -2930,8 +3096,12 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo .makeAttributeRestype( entityInfo .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse(throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint")))).mkString(";") + .getOrElse( + throw InconsistentRepositoryDataException( + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) + ) + )).mkString(";") } else { entityInfo.getPredicateStringObjectsWithoutLang(OntologyConstants.SalsahGui.GuiAttribute).mkString(";") } @@ -2960,7 +3130,6 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo value_ids = valueObjects.map(_.valueObjectIri), comments = valueObjects.map(_.comment) ) - } // Make a PropertyV1 for each value property that has data. val valuePropertiesWithDataWithFuture: Iterable[Future[Option[PropertyV1]]] = @@ -2969,8 +3138,10 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo val valueObjectsV1WithFuture: Iterable[Future[ValueObjectV1]] = valueObject.valueObjects.map { case (valObjIri: IRI, valueProps: ValueProps) => // Make sure the value object has an rdf:type. - valueProps.literalData.getOrElse(OntologyConstants.Rdf.Type, - throw InconsistentRepositoryDataException(s"$valObjIri has no rdf:type")) + valueProps.literalData.getOrElse( + OntologyConstants.Rdf.Type, + throw InconsistentRepositoryDataException(s"$valObjIri has no rdf:type") + ) for { // Convert the SPARQL query results to a ValueV1. @@ -2991,24 +3162,25 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo predicates = valueProps.literalData - } yield - ValueObjectV1( - valueObjectIri = valObjIri, - valueV1 = valueV1, - valuePermission = valPermission, - comment = predicates.get(OntologyConstants.KnoraBase.ValueHasComment).map(_.literals.head), - order = predicates.get(OntologyConstants.KnoraBase.ValueHasOrder) match { - // this should not be necessary as an order should always be given (also if there is only one value) - case Some(ValueLiterals(literals)) => literals.head.toInt - case _ => 0 // order statement is missing, set it to zero - } - ) + } yield ValueObjectV1( + valueObjectIri = valObjIri, + valueV1 = valueV1, + valuePermission = valPermission, + comment = predicates.get(OntologyConstants.KnoraBase.ValueHasComment).map(_.literals.head), + order = predicates.get(OntologyConstants.KnoraBase.ValueHasOrder) match { + // this should not be necessary as an order should always be given (also if there is only one value) + case Some(ValueLiterals(literals)) => literals.head.toInt + case _ => 0 // order statement is missing, set it to zero + } + ) } for { valueObjectsV1 <- Future.sequence(valueObjectsV1WithFuture) - valueObjectsV1Sorted = valueObjectsV1.toVector.sortBy(_.order) // sort the values by their order given in the triplestore [[OntologyConstants.KnoraBase.ValueHasOrder]] + valueObjectsV1Sorted = valueObjectsV1.toVector.sortBy( + _.order + ) // sort the values by their order given in the triplestore [[OntologyConstants.KnoraBase.ValueHasOrder]] // get all the values the user has at least viewing permissions on valueObjectListFiltered = valueObjectsV1Sorted.filter(_.valuePermission.nonEmpty) @@ -3036,14 +3208,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo for { valuePropertiesWithDataWithOption: Iterable[Option[PropertyV1]] <- Future.sequence( - valuePropertiesWithDataWithFuture) + valuePropertiesWithDataWithFuture + ) valuePropertiesWithData = valuePropertiesWithDataWithOption.toVector.flatten // Make a PropertyV1 for each link property with data. We have to treat links as a special case, because we // need information about the target resource. - linkPropertiesWithDataWithFuture: Iterable[Future[Option[PropertyV1]]] = groupedPropertiesByType.groupedLinkProperties.groupedProperties - .map { + linkPropertiesWithDataWithFuture: Iterable[Future[Option[PropertyV1]]] = + groupedPropertiesByType.groupedLinkProperties.groupedProperties.map { case (propertyIri: IRI, targetResource: ValueObjects) => val valueObjectsV1WithFuture: Vector[Future[ValueObjectV1]] = targetResource.valueObjects.map { case (targetResourceIri: IRI, valueProps: ValueProps) => @@ -3059,7 +3232,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo case Some(referencedResTypeEntityInfo) => val labelOption: Option[String] = referencedResTypeEntityInfo.getPredicateObject( predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(userProfile.lang, settings.fallbackLanguage)) + preferredLangs = Some(userProfile.lang, settings.fallbackLanguage) + ) val resIconOption: Option[String] = referencedResTypeEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) @@ -3096,20 +3270,24 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo .getOrElse( linkValuePropertyIri, throw InconsistentRepositoryDataException( - s"Resource $containingResourceIri has link property $propertyIri but does not have a corresponding link value property") + s"Resource $containingResourceIri has link property $propertyIri but does not have a corresponding link value property" + ) ) .valueObjects - .find { - case (someLinkValueIri, someLinkValueProps) => - someLinkValueProps.literalData - .getOrElse(OntologyConstants.Rdf.Object, - throw InconsistentRepositoryDataException( - s"Link value $someLinkValueIri has no rdf:object")) - .literals - .head == targetResourceIri + .find { case (someLinkValueIri, someLinkValueProps) => + someLinkValueProps.literalData + .getOrElse( + OntologyConstants.Rdf.Object, + throw InconsistentRepositoryDataException(s"Link value $someLinkValueIri has no rdf:object") + ) + .literals + .head == targetResourceIri } - .getOrElse(throw InconsistentRepositoryDataException( - s"Link property $propertyIri of resource $containingResourceIri points to resource $targetResourceIri, but there is no corresponding link value with the target resource as its rdf:object")) + .getOrElse( + throw InconsistentRepositoryDataException( + s"Link property $propertyIri of resource $containingResourceIri points to resource $targetResourceIri, but there is no corresponding link value with the target resource as its rdf:object" + ) + ) val linkValueOrder = linkValueProps.literalData.get(OntologyConstants.KnoraBase.ValueHasOrder) match { // this should not be necessary as an order should always be given (also if there is only one value) @@ -3130,7 +3308,8 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo case linkValueV1: LinkValueV1 => linkValueV1 case _ => throw InconsistentRepositoryDataException( - s"Expected $linkValueIri to be a knora-base:LinkValue, but its type is ${apiValueV1ForLinkValue.valueTypeIri}") + s"Expected $linkValueIri to be a knora-base:LinkValue, but its type is ${apiValueV1ForLinkValue.valueTypeIri}" + ) } // Check the permissions on the LinkValue. @@ -3157,16 +3336,15 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo case _ => None } - } yield - ValueObjectV1( - valueObjectIri = linkValueIri, - valueV1 = valueV1, - valuePermission = linkPermission, - order = linkValueOrder, - comment = linkValueProps.literalData - .get(OntologyConstants.KnoraBase.ValueHasComment) - .map(_.literals.head) // get comment from LinkValue - ) + } yield ValueObjectV1( + valueObjectIri = linkValueIri, + valueV1 = valueV1, + valuePermission = linkPermission, + order = linkValueOrder, + comment = linkValueProps.literalData + .get(OntologyConstants.KnoraBase.ValueHasComment) + .map(_.literals.head) // get comment from LinkValue + ) }.toVector for { @@ -3188,17 +3366,17 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo // If the property has a value that the user isn't allowed to see, and its cardinality // is MustHaveOne or MayHaveOne, don't return any information about the property. - } yield - propsAndCardinalities.get(propertyIri) match { - case Some(cardinalityInfo) - if (cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MayHaveOne) && valueObjectsV1.nonEmpty && valueObjectListFiltered.isEmpty => - None - case _ => Some(propertyV1) - } + } yield propsAndCardinalities.get(propertyIri) match { + case Some(cardinalityInfo) + if (cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MayHaveOne) && valueObjectsV1.nonEmpty && valueObjectListFiltered.isEmpty => + None + case _ => Some(propertyV1) + } } linkPropertiesWithDataWithOption: Iterable[Option[PropertyV1]] <- Future.sequence( - linkPropertiesWithDataWithFuture) + linkPropertiesWithDataWithFuture + ) linkPropertiesWithData: Vector[PropertyV1] = linkPropertiesWithDataWithOption.toVector.flatten @@ -3210,7 +3388,12 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo val valueObjects: Seq[PropertyGetValueV1] = (propertyV1.value_ids, propertyV1.values, propertyV1.comments).zipped.map { case (id: IRI, value: ApiValueV1, comment: Option[String]) => - PropertyGetValueV1(id = id, value = value, textval = value.toString, comment = comment) // TODO: person_id and lastmod are not handled yet. Probably these are never used by the GUI. + PropertyGetValueV1( + id = id, + value = value, + textval = value.toString, + comment = comment + ) // TODO: person_id and lastmod are not handled yet. Probably these are never used by the GUI. } // TODO: try to unify this with ValueUtilV1's convertCreateValueResponseV1ToResourceCreateValueResponseV1 @@ -3234,17 +3417,16 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Given a [[FileValueV1]], checks whether it represents a JPEG 2000 image. If so, returns a sequence of [[StillImageFileValueV1]] - * objects representing different possible resolutions of the same image. Otherwise, returns a sequence containing the - * same [[FileValueV1]] unchanged. - * - * @param fileValueV1 the file's metadata. - * @return a sequence of [[FileValueV1]] objects. - */ + * Given a [[FileValueV1]], checks whether it represents a JPEG 2000 image. If so, returns a sequence of [[StillImageFileValueV1]] + * objects representing different possible resolutions of the same image. Otherwise, returns a sequence containing the + * same [[FileValueV1]] unchanged. + * + * @param fileValueV1 the file's metadata. + * @return a sequence of [[FileValueV1]] objects. + */ private def createMultipleImageResolutions(fileValueV1: FileValueV1): Seq[FileValueV1] = { - def scaleImageSize(dimension: Int, power: Int): Int = { + def scaleImageSize(dimension: Int, power: Int): Int = (dimension.toDouble / math.pow(2, power)).round.toInt - } fileValueV1 match { case stillImageFileValueV1: StillImageFileValueV1 => @@ -3269,11 +3451,11 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo } /** - * Converts a full-size still image file value to a preview image. - * - * @param fullSizeImageFileValue the full-size image. - * @return a corresponding preview image. - */ + * Converts a full-size still image file value to a preview image. + * + * @param fullSizeImageFileValue the full-size image. + * @return a corresponding preview image. + */ private def fullSizeImageFileValueToPreview(fullSizeImageFileValue: StillImageFileValueV1): StillImageFileValueV1 = { val proportion = fullSizeImageFileValue.dimY.toDouble / 128.0 val previewHeight = 128 diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/SearchResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/SearchResponderV1.scala index 08e81a1fd6..5336ec7677 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/SearchResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/SearchResponderV1.scala @@ -38,9 +38,9 @@ import org.knora.webapi.util.ApacheLuceneSupport.LuceneQueryString import scala.concurrent.Future /** - * Responds to requests for user search queries and returns responses in Knora API - * v1 format. - */ + * Responds to requests for user search queries and returns responses in Knora API + * v1 format. + */ class SearchResponderV1(responderData: ResponderData) extends Responder(responderData) { // Valid combinations of value types and comparison operators, for determining whether a requested search @@ -123,25 +123,27 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde ) /** - * Represents a matching value in a search result. - * - * @param valueTypeIri the type of the value that matched. - * @param propertyIri the IRI of the property that points to the value. - * @param propertyLabel the label of the property that points to the value. - * @param literal the literal that matched. - * @param valuePermissionCode the user's permission code on the value. - */ - private case class MatchingValue(valueTypeIri: IRI, - propertyIri: IRI, - propertyLabel: String, - literal: String, - valuePermissionCode: Option[Int]) + * Represents a matching value in a search result. + * + * @param valueTypeIri the type of the value that matched. + * @param propertyIri the IRI of the property that points to the value. + * @param propertyLabel the label of the property that points to the value. + * @param literal the literal that matched. + * @param valuePermissionCode the user's permission code on the value. + */ + private case class MatchingValue( + valueTypeIri: IRI, + propertyIri: IRI, + propertyLabel: String, + literal: String, + valuePermissionCode: Option[Int] + ) val valueUtilV1 = new ValueUtilV1(settings) /** - * Receives a message of type [[SearchResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message of type [[SearchResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: SearchResponderRequestV1) = msg match { case searchGetRequest: FulltextSearchGetRequestV1 => fulltextSearchV1(searchGetRequest) case searchGetRequest: ExtendedSearchGetRequestV1 => extendedSearchV1(searchGetRequest) @@ -149,11 +151,11 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Performs a fulltext search (simple search) and returns a [[SearchGetResponseV1]] in Knora API v1 format. - * - * @param searchGetRequest the user search request. - * @return a [[SearchGetResponseV1]] containing the search results. - */ + * Performs a fulltext search (simple search) and returns a [[SearchGetResponseV1]] in Knora API v1 format. + * + * @param searchGetRequest the user search request. + * @return a [[SearchGetResponseV1]] containing the search results. + */ private def fulltextSearchV1(searchGetRequest: FulltextSearchGetRequestV1): Future[SearchGetResponseV1] = { val userProfileV1 = searchGetRequest.userProfile.asUserProfileV1 @@ -175,7 +177,8 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde projectIriOption = searchGetRequest.filterByProject, restypeIriOption = searchGetRequest.filterByRestype ) - .toString()) + .toString() + ) // _ = println("================" + pagingSparql) @@ -204,127 +207,129 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde // Convert the query result rows into SearchResultRowV1 objects. subjects: Vector[SearchResultRowV1] = groupedByResourceIri - .foldLeft(Vector.empty[SearchResultRowV1]) { - case (subjectsAcc, (resourceIri, rows)) => - val firstRowMap = rows.head.rowMap - - // Does the user have permission to see the resource? - - val resourceCreator = firstRowMap("resourceCreator") - val resourceProject = firstRowMap("resourceProject") - val resourceProjectShortcode = resourceIri.toSmartIri.getProjectCode.getOrElse( - throw InconsistentRepositoryDataException(s"Invalid resource IRI: $resourceIri")) - val resourcePermissions = firstRowMap("resourcePermissions") - - val resourcePermissionCode: Option[Int] = PermissionUtilADM.getUserPermissionV1( - entityIri = resourceIri, - entityCreator = resourceCreator, - entityProject = resourceProject, - entityPermissionLiteral = resourcePermissions, - userProfile = userProfileV1 - ) + .foldLeft(Vector.empty[SearchResultRowV1]) { case (subjectsAcc, (resourceIri, rows)) => + val firstRowMap = rows.head.rowMap - if (resourcePermissionCode.nonEmpty) { - // Yes. Get more information about the resource. + // Does the user have permission to see the resource? - val resourceClassIri = firstRowMap("resourceClass") - val resourceEntityInfo: ClassInfoV1 = entityInfoResponse.resourceClassInfoMap(resourceClassIri) - val resourceClassLabel = resourceEntityInfo.getPredicateObject( - predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(searchGetRequest.userProfile.lang, settings.fallbackLanguage) - ) - val resourceClassIcon = resourceEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) - val resourceLabel = firstRowMap.getOrElse( - "resourceLabel", - throw InconsistentRepositoryDataException(s"Resource $resourceIri has no rdfs:label")) - - // Collect the matching values in the resource. - val mapOfMatchingValues: Map[IRI, MatchingValue] = - rows.filter(_.rowMap.get("valueObject").nonEmpty).foldLeft(Map.empty[IRI, MatchingValue]) { - case (valuesAcc, row) => - // Convert the permissions on the matching value object into a ValueProps. - val valueIri = row.rowMap("valueObject") - val literal = row.rowMap("literal") - val valueCreator = row.rowMap("valueCreator") - val valuePermissionsLiteral = row.rowMap("valuePermissions") - val valuePermissionCode = PermissionUtilADM.getUserPermissionV1( - entityIri = valueIri, - entityCreator = valueCreator, - entityProject = resourceProject, - entityPermissionLiteral = valuePermissionsLiteral, - userProfile = userProfileV1 - ) + val resourceCreator = firstRowMap("resourceCreator") + val resourceProject = firstRowMap("resourceProject") + val resourceProjectShortcode = resourceIri.toSmartIri.getProjectCode.getOrElse( + throw InconsistentRepositoryDataException(s"Invalid resource IRI: $resourceIri") + ) + val resourcePermissions = firstRowMap("resourcePermissions") + + val resourcePermissionCode: Option[Int] = PermissionUtilADM.getUserPermissionV1( + entityIri = resourceIri, + entityCreator = resourceCreator, + entityProject = resourceProject, + entityPermissionLiteral = resourcePermissions, + userProfile = userProfileV1 + ) - val value: Option[(IRI, MatchingValue)] = valuePermissionCode.map { permissionCode => - val propertyIri = row.rowMap("resourceProperty") - val propertyLabel = entityInfoResponse - .propertyInfoMap(propertyIri) - .getPredicateObject(OntologyConstants.Rdfs.Label, - preferredLangs = Some(searchGetRequest.userProfile.lang, - settings.fallbackLanguage)) match { - case Some(label) => label - case None => - throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdfs:label") - } + if (resourcePermissionCode.nonEmpty) { + // Yes. Get more information about the resource. - valueIri -> MatchingValue( - valueTypeIri = row.rowMap("valueObjectType"), - propertyIri = propertyIri, - propertyLabel = propertyLabel, - literal = literal, - valuePermissionCode = valuePermissionCode - ) + val resourceClassIri = firstRowMap("resourceClass") + val resourceEntityInfo: ClassInfoV1 = entityInfoResponse.resourceClassInfoMap(resourceClassIri) + val resourceClassLabel = resourceEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(searchGetRequest.userProfile.lang, settings.fallbackLanguage) + ) + val resourceClassIcon = resourceEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) + val resourceLabel = firstRowMap.getOrElse( + "resourceLabel", + throw InconsistentRepositoryDataException(s"Resource $resourceIri has no rdfs:label") + ) + + // Collect the matching values in the resource. + val mapOfMatchingValues: Map[IRI, MatchingValue] = + rows.filter(_.rowMap.get("valueObject").nonEmpty).foldLeft(Map.empty[IRI, MatchingValue]) { + case (valuesAcc, row) => + // Convert the permissions on the matching value object into a ValueProps. + val valueIri = row.rowMap("valueObject") + val literal = row.rowMap("literal") + val valueCreator = row.rowMap("valueCreator") + val valuePermissionsLiteral = row.rowMap("valuePermissions") + val valuePermissionCode = PermissionUtilADM.getUserPermissionV1( + entityIri = valueIri, + entityCreator = valueCreator, + entityProject = resourceProject, + entityPermissionLiteral = valuePermissionsLiteral, + userProfile = userProfileV1 + ) + + val value: Option[(IRI, MatchingValue)] = valuePermissionCode.map { permissionCode => + val propertyIri = row.rowMap("resourceProperty") + val propertyLabel = entityInfoResponse + .propertyInfoMap(propertyIri) + .getPredicateObject( + OntologyConstants.Rdfs.Label, + preferredLangs = Some(searchGetRequest.userProfile.lang, settings.fallbackLanguage) + ) match { + case Some(label) => label + case None => + throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdfs:label") } - valuesAcc ++ value - } + valueIri -> MatchingValue( + valueTypeIri = row.rowMap("valueObjectType"), + propertyIri = propertyIri, + propertyLabel = propertyLabel, + literal = literal, + valuePermissionCode = valuePermissionCode + ) + } - // Sort by value IRI, then by property IRI, so the results are consistent between requests. - val vectorOfMatchingValues: Vector[(IRI, MatchingValue)] = mapOfMatchingValues.toVector - val matchingValuesSortedByValueIri: Vector[MatchingValue] = vectorOfMatchingValues.sortBy(_._1).map(_._2) - val matchingValues: Vector[MatchingValue] = matchingValuesSortedByValueIri.sortBy(_.propertyIri) + valuesAcc ++ value + } - // Does the user have permission to see at least one matching value in the resource, or did the resource's label match? - if (matchingValues.nonEmpty || rows.exists(_.rowMap.get("valueObject").isEmpty)) { - // Yes. Make a search result for the resource. + // Sort by value IRI, then by property IRI, so the results are consistent between requests. + val vectorOfMatchingValues: Vector[(IRI, MatchingValue)] = mapOfMatchingValues.toVector + val matchingValuesSortedByValueIri: Vector[MatchingValue] = vectorOfMatchingValues.sortBy(_._1).map(_._2) + val matchingValues: Vector[MatchingValue] = matchingValuesSortedByValueIri.sortBy(_.propertyIri) - val resourceClassIconURL = resourceClassIcon.map { resClassIcon => - valueUtilV1.makeResourceClassIconURL(resourceClassIri, resClassIcon) - } + // Does the user have permission to see at least one matching value in the resource, or did the resource's label match? + if (matchingValues.nonEmpty || rows.exists(_.rowMap.get("valueObject").isEmpty)) { + // Yes. Make a search result for the resource. - subjectsAcc :+ SearchResultRowV1( - obj_id = resourceIri, - preview_path = firstRowMap.get("previewPath") match { - case Some(path) => - Some(valueUtilV1.makeSipiImagePreviewGetUrlFromFilename(resourceProjectShortcode, path)) - case None => - // If there is no preview image, use the resource class icon from the ontology. - resourceClassIconURL - }, - iconsrc = resourceClassIconURL, - icontitle = resourceClassLabel, - iconlabel = resourceClassLabel, - valuetype_id = OntologyConstants.Rdfs.Label +: matchingValues.map(_.valueTypeIri), - valuelabel = "Label" +: matchingValues.map(_.propertyLabel), - value = resourceLabel +: matchingValues.map(_.literal), - preview_nx = firstRowMap.get("previewDimX") match { - case Some(previewDimX) => previewDimX.toInt - case None => settings.defaultIconSizeDimX - }, - preview_ny = firstRowMap.get("previewDimY") match { - case Some(previewDimY) => previewDimY.toInt - case None => settings.defaultIconSizeDimY - }, - rights = resourcePermissionCode - ) - } else { - // The user doesn't have permission to see any of the matching values. - subjectsAcc + val resourceClassIconURL = resourceClassIcon.map { resClassIcon => + valueUtilV1.makeResourceClassIconURL(resourceClassIri, resClassIcon) } + + subjectsAcc :+ SearchResultRowV1( + obj_id = resourceIri, + preview_path = firstRowMap.get("previewPath") match { + case Some(path) => + Some(valueUtilV1.makeSipiImagePreviewGetUrlFromFilename(resourceProjectShortcode, path)) + case None => + // If there is no preview image, use the resource class icon from the ontology. + resourceClassIconURL + }, + iconsrc = resourceClassIconURL, + icontitle = resourceClassLabel, + iconlabel = resourceClassLabel, + valuetype_id = OntologyConstants.Rdfs.Label +: matchingValues.map(_.valueTypeIri), + valuelabel = "Label" +: matchingValues.map(_.propertyLabel), + value = resourceLabel +: matchingValues.map(_.literal), + preview_nx = firstRowMap.get("previewDimX") match { + case Some(previewDimX) => previewDimX.toInt + case None => settings.defaultIconSizeDimX + }, + preview_ny = firstRowMap.get("previewDimY") match { + case Some(previewDimY) => previewDimY.toInt + case None => settings.defaultIconSizeDimY + }, + rights = resourcePermissionCode + ) } else { - // The user doesn't have permission to see the resource. + // The user doesn't have permission to see any of the matching values. subjectsAcc } + } else { + // The user doesn't have permission to see the resource. + subjectsAcc + } } .sortBy(_.obj_id) // Sort the matching resources by resource IRI so paging works. @@ -343,11 +348,11 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Performs an extended search (structured search) and returns a [[SearchGetResponseV1]] in Knora API v1 format. - * - * @param searchGetRequest the user search request - * @return a [[SearchGetResponseV1]] containing the search results. - */ + * Performs an extended search (structured search) and returns a [[SearchGetResponseV1]] in Knora API v1 format. + * + * @param searchGetRequest the user search request + * @return a [[SearchGetResponseV1]] containing the search results. + */ private def extendedSearchV1(searchGetRequest: ExtendedSearchGetRequestV1): Future[SearchGetResponseV1] = { import org.knora.webapi.messages.StringFormatter @@ -358,7 +363,8 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde // get information about all the properties involved propertyInfo: EntityInfoGetResponseV1 <- (responderManager ? EntityInfoGetRequestV1( propertyIris = searchGetRequest.propertyIri.toSet, - userProfile = searchGetRequest.userProfile)).mapTo[EntityInfoGetResponseV1] + userProfile = searchGetRequest.userProfile + )).mapTo[EntityInfoGetResponseV1] /* * handle parallel lists here: propertyIri, comparisonOperator, SearchValue @@ -368,196 +374,205 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde * * http://stackoverflow.com/questions/1157564/zipwith-mapping-over-multiple-seq-in-scala */ - searchCriteria: Seq[SearchCriterion] = (searchGetRequest.propertyIri, - searchGetRequest.compareProps, - searchGetRequest.searchValue).zipped.map( - (prop, compop, searchval) => { - val propertyEntityInfo = propertyInfo.propertyInfoMap(prop) - - // If the property is a linking property, we pretend its knora-base:objectClassConstraint is knora-base:Resource, so validTypeCompopCombos will work. - val propertyObjectClassConstraint: IRI = if (propertyEntityInfo.isLinkProp) { - OntologyConstants.KnoraBase.Resource - } else { - propertyEntityInfo - .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) - .getOrElse( - throw InconsistentRepositoryDataException(s"Property $prop has no knora-base:objectClassConstraint")) - } + searchCriteria: Seq[SearchCriterion] = ( + searchGetRequest.propertyIri, + searchGetRequest.compareProps, + searchGetRequest.searchValue + ).zipped.map { (prop, compop, searchval) => + val propertyEntityInfo = propertyInfo.propertyInfoMap(prop) + + // If the property is a linking property, we pretend its knora-base:objectClassConstraint is knora-base:Resource, so validTypeCompopCombos will work. + val propertyObjectClassConstraint: IRI = if (propertyEntityInfo.isLinkProp) { + OntologyConstants.KnoraBase.Resource + } else { + propertyEntityInfo + .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) + .getOrElse( + throw InconsistentRepositoryDataException(s"Property $prop has no knora-base:objectClassConstraint") + ) + } - // Ensure that the property's objectClassConstraint is valid, and that the specified operator can be - // used with it. - validTypeCompopCombos.get(propertyObjectClassConstraint) match { - case Some(validOps) => - if (!validOps.contains(compop)) { - // The class specified in the property's objectClassConstraint can't be used with the specified operator. - throw BadRequestException(s"Operator $compop cannot be used with property $prop") - } + // Ensure that the property's objectClassConstraint is valid, and that the specified operator can be + // used with it. + validTypeCompopCombos.get(propertyObjectClassConstraint) match { + case Some(validOps) => + if (!validOps.contains(compop)) { + // The class specified in the property's objectClassConstraint can't be used with the specified operator. + throw BadRequestException(s"Operator $compop cannot be used with property $prop") + } - case None => - // The class specified in the property's objectClassConstraint is invalid. - throw BadRequestException( - s"Property $prop has an invalid knora-base:objectClassConstraint: $propertyObjectClassConstraint") - } + case None => + // The class specified in the property's objectClassConstraint is invalid. + throw BadRequestException( + s"Property $prop has an invalid knora-base:objectClassConstraint: $propertyObjectClassConstraint" + ) + } - val searchParamWithoutValue = SearchCriterion( - propertyIri = prop, - comparisonOperator = compop, - valueType = propertyObjectClassConstraint - ) + val searchParamWithoutValue = SearchCriterion( + propertyIri = prop, + comparisonOperator = compop, + valueType = propertyObjectClassConstraint + ) - // check and convert the searchval if necessary (e.g. check if a given string is numeric or convert a date string to a date) + // check and convert the searchval if necessary (e.g. check if a given string is numeric or convert a date string to a date) - if (compop == SearchComparisonOperatorV1.EXISTS) { - // EXISTS doesn't need the searchval at all. - searchParamWithoutValue - } else { - propertyObjectClassConstraint match { - case OntologyConstants.KnoraBase.DateValue => - // - // It is a date, parse and convert it to JD - // + if (compop == SearchComparisonOperatorV1.EXISTS) { + // EXISTS doesn't need the searchval at all. + searchParamWithoutValue + } else { + propertyObjectClassConstraint match { + case OntologyConstants.KnoraBase.DateValue => + // + // It is a date, parse and convert it to JD + // - val datestring = - stringFormatter.validateDate(searchval, throw BadRequestException(s"Invalid date format: $searchval")) + val datestring = + stringFormatter.validateDate(searchval, throw BadRequestException(s"Invalid date format: $searchval")) - // parse date: Calendar:YYYY-MM-DD[:YYYY-MM-DD] - val parsedDate = datestring.split(StringFormatter.CalendarSeparator) - val calendar = KnoraCalendarV1.lookup(parsedDate(0)) + // parse date: Calendar:YYYY-MM-DD[:YYYY-MM-DD] + val parsedDate = datestring.split(StringFormatter.CalendarSeparator) + val calendar = KnoraCalendarV1.lookup(parsedDate(0)) - // val daysInMonth = Calendar.DAY_OF_MONTH // will be used to determine the number of days in the given month - // val monthsInYear = Calendar.MONTH // will be used to determine the number of months in the given year (generic for other calendars) + // val daysInMonth = Calendar.DAY_OF_MONTH // will be used to determine the number of days in the given month + // val monthsInYear = Calendar.MONTH // will be used to determine the number of months in the given year (generic for other calendars) - val (dateStart, dateEnd) = if (parsedDate.length > 2) { - // it is a period: 0 : cal | 1 : start | 2 : end + val (dateStart, dateEnd) = if (parsedDate.length > 2) { + // it is a period: 0 : cal | 1 : start | 2 : end - val periodStart = DateUtilV1.dateString2DateRange(parsedDate(1), calendar).start - val periodEnd = DateUtilV1.dateString2DateRange(parsedDate(2), calendar).end + val periodStart = DateUtilV1.dateString2DateRange(parsedDate(1), calendar).start + val periodEnd = DateUtilV1.dateString2DateRange(parsedDate(2), calendar).end - val start = DateUtilV1.convertDateToJulianDayNumber(periodStart) - val end = DateUtilV1.convertDateToJulianDayNumber(periodEnd) + val start = DateUtilV1.convertDateToJulianDayNumber(periodStart) + val end = DateUtilV1.convertDateToJulianDayNumber(periodEnd) - // check if end is bigger than start (the user could have submitted a period where start is bigger than end) - if (start > end) - throw BadRequestException(s"Invalid input for period: start is bigger than end: $searchval") + // check if end is bigger than start (the user could have submitted a period where start is bigger than end) + if (start > end) + throw BadRequestException(s"Invalid input for period: start is bigger than end: $searchval") - (start, end) - } else { - // no period: 0 : cal | 1 : start + (start, end) + } else { + // no period: 0 : cal | 1 : start - val dateRange = DateUtilV1.dateString2DateRange(parsedDate(1), calendar) + val dateRange = DateUtilV1.dateString2DateRange(parsedDate(1), calendar) - val start = DateUtilV1.convertDateToJulianDayNumber(dateRange.start) - val end = DateUtilV1.convertDateToJulianDayNumber(dateRange.end) + val start = DateUtilV1.convertDateToJulianDayNumber(dateRange.start) + val end = DateUtilV1.convertDateToJulianDayNumber(dateRange.end) - (start, end) - } + (start, end) + } - searchParamWithoutValue.copy( - dateStart = Some(dateStart), - dateEnd = Some(dateEnd) - ) + searchParamWithoutValue.copy( + dateStart = Some(dateStart), + dateEnd = Some(dateEnd) + ) - case OntologyConstants.KnoraBase.TextValue => - // http://www.morelab.deusto.es/code_injection/ - // http://stackoverflow.com/questions/29601839/prevent-sparql-injection-generic-solution-triplestore-independent - val searchString = stringFormatter - .toSparqlEncodedString(searchval, throw BadRequestException(s"Invalid search string: '$searchval'")) - - val (matchBooleanPositiveTerms, matchBooleanNegativeTerms) = - if (compop == SearchComparisonOperatorV1.MATCH_BOOLEAN) { - val terms = searchString.asInstanceOf[String].split("\\s+").toSet - val negativeTerms = terms.filter(_.startsWith("-")) - val positiveTerms = terms -- negativeTerms - val negativeTermsWithoutPrefixes = negativeTerms.map(_.stripPrefix("-")) - val positiveTermsWithoutPrefixes = positiveTerms.map(_.stripPrefix("+")) - (positiveTermsWithoutPrefixes, negativeTermsWithoutPrefixes) - } else { - (Set.empty[String], Set.empty[String]) - } + case OntologyConstants.KnoraBase.TextValue => + // http://www.morelab.deusto.es/code_injection/ + // http://stackoverflow.com/questions/29601839/prevent-sparql-injection-generic-solution-triplestore-independent + val searchString = stringFormatter + .toSparqlEncodedString(searchval, throw BadRequestException(s"Invalid search string: '$searchval'")) + + val (matchBooleanPositiveTerms, matchBooleanNegativeTerms) = + if (compop == SearchComparisonOperatorV1.MATCH_BOOLEAN) { + val terms = searchString.asInstanceOf[String].split("\\s+").toSet + val negativeTerms = terms.filter(_.startsWith("-")) + val positiveTerms = terms -- negativeTerms + val negativeTermsWithoutPrefixes = negativeTerms.map(_.stripPrefix("-")) + val positiveTermsWithoutPrefixes = positiveTerms.map(_.stripPrefix("+")) + (positiveTermsWithoutPrefixes, negativeTermsWithoutPrefixes) + } else { + (Set.empty[String], Set.empty[String]) + } - searchParamWithoutValue.copy( - searchValue = Some(searchString), - matchBooleanPositiveTerms = matchBooleanPositiveTerms, - matchBooleanNegativeTerms = matchBooleanNegativeTerms + searchParamWithoutValue.copy( + searchValue = Some(searchString), + matchBooleanPositiveTerms = matchBooleanPositiveTerms, + matchBooleanNegativeTerms = matchBooleanNegativeTerms + ) + + case OntologyConstants.KnoraBase.IntValue => + // check if string is an integer + val searchString = stringFormatter + .validateInt(searchval, throw BadRequestException(s"Given searchval is not an integer: $searchval")) + .toString + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case OntologyConstants.KnoraBase.DecimalValue => + // check if string is a decimal number + val searchString = stringFormatter + .validateBigDecimal( + searchval, + throw BadRequestException(s"Given searchval is not a decimal number: $searchval") ) + .toString + searchParamWithoutValue.copy(searchValue = Some(searchString)) - case OntologyConstants.KnoraBase.IntValue => - // check if string is an integer - val searchString = stringFormatter - .validateInt(searchval, throw BadRequestException(s"Given searchval is not an integer: $searchval")) - .toString - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.DecimalValue => - // check if string is a decimal number - val searchString = stringFormatter - .validateBigDecimal(searchval, - throw BadRequestException(s"Given searchval is not a decimal number: $searchval")) - .toString - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.TimeValue => - // check if string is an integer - val searchString = stringFormatter - .xsdDateTimeStampToInstant( - searchval, - throw BadRequestException(s"Given searchval is not a timestamp: $searchval")) - .toString - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.Resource => - // check if string is a valid IRI - val searchString = stringFormatter.validateAndEscapeIri( + case OntologyConstants.KnoraBase.TimeValue => + // check if string is an integer + val searchString = stringFormatter + .xsdDateTimeStampToInstant( searchval, - throw BadRequestException(s"Given searchval is not a valid IRI: $searchval")) - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.ColorValue => - // check if string is a hexadecimal RGB-color value - val searchString = - stringFormatter.validateColor(searchval, - throw BadRequestException(s"Invalid color format: $searchval")) - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.GeomValue => - // this only will be used with compop EXISTS - searchParamWithoutValue.copy(searchValue = Some("")) - - case OntologyConstants.KnoraBase.GeonameValue => - // sanitize Geoname search string - val searchString = stringFormatter - .toSparqlEncodedString(searchval, throw BadRequestException(s"Invalid Geoname search string: '$searchval'")) - - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.UriValue => - // validate URI - val searchString = stringFormatter.validateAndEscapeIri( + throw BadRequestException(s"Given searchval is not a timestamp: $searchval") + ) + .toString + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case OntologyConstants.KnoraBase.Resource => + // check if string is a valid IRI + val searchString = stringFormatter.validateAndEscapeIri( + searchval, + throw BadRequestException(s"Given searchval is not a valid IRI: $searchval") + ) + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case OntologyConstants.KnoraBase.ColorValue => + // check if string is a hexadecimal RGB-color value + val searchString = + stringFormatter.validateColor(searchval, throw BadRequestException(s"Invalid color format: $searchval")) + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case OntologyConstants.KnoraBase.GeomValue => + // this only will be used with compop EXISTS + searchParamWithoutValue.copy(searchValue = Some("")) + + case OntologyConstants.KnoraBase.GeonameValue => + // sanitize Geoname search string + val searchString = stringFormatter + .toSparqlEncodedString( searchval, - throw BadRequestException(s"Invalid URI: $searchval")) - searchParamWithoutValue.copy(searchValue = Some(searchString)) + throw BadRequestException(s"Invalid Geoname search string: '$searchval'") + ) + + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case OntologyConstants.KnoraBase.UriValue => + // validate URI + val searchString = + stringFormatter.validateAndEscapeIri(searchval, throw BadRequestException(s"Invalid URI: $searchval")) + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case OntologyConstants.KnoraBase.ListValue => + // check if string represents a node in a list + val searchString = stringFormatter.validateAndEscapeIri( + searchval, + throw BadRequestException(s"Given searchval is not a formally valid IRI $searchval") + ) + searchParamWithoutValue.copy(searchValue = Some(searchString)) - case OntologyConstants.KnoraBase.ListValue => - // check if string represents a node in a list - val searchString = stringFormatter.validateAndEscapeIri( + case OntologyConstants.KnoraBase.BooleanValue => + // check if searchVal is a Boolan value + val searchString = stringFormatter + .validateBoolean( searchval, - throw BadRequestException(s"Given searchval is not a formally valid IRI $searchval")) - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case OntologyConstants.KnoraBase.BooleanValue => - // check if searchVal is a Boolan value - val searchString = stringFormatter - .validateBoolean( - searchval, - throw BadRequestException(s"Given searchval is not a valid Boolean value: $searchval")) - .toString - searchParamWithoutValue.copy(searchValue = Some(searchString)) - - case other => throw BadRequestException(s"The value type for the given property $prop is unknown.") - } + throw BadRequestException(s"Given searchval is not a valid Boolean value: $searchval") + ) + .toString + searchParamWithoutValue.copy(searchValue = Some(searchString)) + + case other => throw BadRequestException(s"The value type for the given property $prop is unknown.") } } - ) + } // Get the search results. searchSparql = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -595,169 +610,172 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde // Convert the query result rows into SearchResultRowV1 objects. subjects: Vector[SearchResultRowV1] = groupedByResourceIri - .foldLeft(Vector.empty[SearchResultRowV1]) { - case (subjectsAcc, (resourceIri, rows)) => - val firstRowMap = rows.head.rowMap - - // Does the user have permission to see the resource? - - val resourceCreator = firstRowMap("resourceCreator") - val resourceProject = firstRowMap("resourceProject") - val resourceProjectShortcode = resourceIri.toSmartIri.getProjectCode.getOrElse( - throw InconsistentRepositoryDataException(s"Invalid resource IRI: $resourceIri")) - val resourcePermissions = firstRowMap("resourcePermissions") - - val resourcePermissionCode: Option[Int] = PermissionUtilADM.getUserPermissionV1( - entityIri = resourceIri, - entityCreator = resourceCreator, - entityProject = resourceProject, - entityPermissionLiteral = resourcePermissions, - userProfile = userProfileV1 + .foldLeft(Vector.empty[SearchResultRowV1]) { case (subjectsAcc, (resourceIri, rows)) => + val firstRowMap = rows.head.rowMap + + // Does the user have permission to see the resource? + + val resourceCreator = firstRowMap("resourceCreator") + val resourceProject = firstRowMap("resourceProject") + val resourceProjectShortcode = resourceIri.toSmartIri.getProjectCode.getOrElse( + throw InconsistentRepositoryDataException(s"Invalid resource IRI: $resourceIri") + ) + val resourcePermissions = firstRowMap("resourcePermissions") + + val resourcePermissionCode: Option[Int] = PermissionUtilADM.getUserPermissionV1( + entityIri = resourceIri, + entityCreator = resourceCreator, + entityProject = resourceProject, + entityPermissionLiteral = resourcePermissions, + userProfile = userProfileV1 + ) + + if (resourcePermissionCode.nonEmpty) { + // Yes. Get more information about the resource. + + val resourceClassIri = firstRowMap("resourceClass") + val resourceEntityInfo = entityInfoResponse.resourceClassInfoMap(resourceClassIri) + val resourceClassLabel = resourceEntityInfo.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(searchGetRequest.userProfile.lang, settings.fallbackLanguage) + ) + val resourceClassIcon = resourceEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) + val resourceLabel = firstRowMap.getOrElse( + "resourceLabel", + throw InconsistentRepositoryDataException(s"Resource $resourceIri has no rdfs:label") ) - if (resourcePermissionCode.nonEmpty) { - // Yes. Get more information about the resource. - - val resourceClassIri = firstRowMap("resourceClass") - val resourceEntityInfo = entityInfoResponse.resourceClassInfoMap(resourceClassIri) - val resourceClassLabel = resourceEntityInfo.getPredicateObject( - predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(searchGetRequest.userProfile.lang, settings.fallbackLanguage)) - val resourceClassIcon = resourceEntityInfo.getPredicateObject(OntologyConstants.KnoraBase.ResourceIcon) - val resourceLabel = firstRowMap.getOrElse( - "resourceLabel", - throw InconsistentRepositoryDataException(s"Resource $resourceIri has no rdfs:label")) - - // If there were search criteria referring to values, collect the matching values in the resource. - val matchingValues: Vector[MatchingValue] = if (searchCriteria.nonEmpty) { - // Construct a Map of value IRIs to MatchingValue objects found in the resource. - val mapOfMatchingValues: Map[IRI, MatchingValue] = rows.foldLeft(Map.empty[IRI, MatchingValue]) { - case (valuesAcc: Map[IRI, MatchingValue], row: VariableResultsRow) => - // For each row, get the matching value for each search criterion. - val valuesInRow: Seq[(IRI, MatchingValue)] = searchCriteria.zipWithIndex.map { - case (searchCriterion, index) => - val valueIri = row.rowMap(s"valueObject$index") - val literal = row.rowMap(s"literal$index") - val valuePermissionLiteral = row.rowMap(s"valuePermissions$index") - val valueCreator = row.rowMap(s"valueCreator$index") - - // Is the matching value object a LinkValue? - val valuePermissionCode = - if (searchCriterion.valueType == OntologyConstants.KnoraBase.Resource) { - // Yes. - val linkValuePermissionCode = PermissionUtilADM.getUserPermissionV1( - entityIri = valueIri, - entityCreator = valueCreator, - entityProject = resourceProject, - entityPermissionLiteral = valuePermissionLiteral, - userProfile = userProfileV1 - ) - - // Get the permission code for the target resource. - val targetResourceIri = row.rowMap(s"targetResource$index") - val targetResourceCreator = row.rowMap(s"targetResourceCreator$index") - val targetResourceProject = row.rowMap(s"targetResourceProject$index") - val targetResourcePermissionLiteral = row.rowMap(s"targetResourcePermissions$index") - - val targetResourcePermissionCode = PermissionUtilADM.getUserPermissionV1( - entityIri = targetResourceIri, - entityCreator = targetResourceCreator, - entityProject = targetResourceProject, - entityPermissionLiteral = targetResourcePermissionLiteral, - userProfile = userProfileV1 - ) - - // Only allow the user to see the match if they have view permission on both the link value and the target resource. - Seq(linkValuePermissionCode, targetResourcePermissionCode).min - } else { - // The matching object is an ordinary value, not a LinkValue. - PermissionUtilADM.getUserPermissionV1( - entityIri = valueIri, - entityCreator = valueCreator, - entityProject = resourceProject, - entityPermissionLiteral = valuePermissionLiteral, - userProfile = userProfileV1 - ) - } - - val propertyIri = searchCriterion.propertyIri - val propertyLabel = propertyInfo - .propertyInfoMap(propertyIri) - .getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(searchGetRequest.userProfile.lang, - settings.fallbackLanguage)) match { - case Some(label) => label - case None => - throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdfs:label") + // If there were search criteria referring to values, collect the matching values in the resource. + val matchingValues: Vector[MatchingValue] = if (searchCriteria.nonEmpty) { + // Construct a Map of value IRIs to MatchingValue objects found in the resource. + val mapOfMatchingValues: Map[IRI, MatchingValue] = rows.foldLeft(Map.empty[IRI, MatchingValue]) { + case (valuesAcc: Map[IRI, MatchingValue], row: VariableResultsRow) => + // For each row, get the matching value for each search criterion. + val valuesInRow: Seq[(IRI, MatchingValue)] = searchCriteria.zipWithIndex.map { + case (searchCriterion, index) => + val valueIri = row.rowMap(s"valueObject$index") + val literal = row.rowMap(s"literal$index") + val valuePermissionLiteral = row.rowMap(s"valuePermissions$index") + val valueCreator = row.rowMap(s"valueCreator$index") + + // Is the matching value object a LinkValue? + val valuePermissionCode = + if (searchCriterion.valueType == OntologyConstants.KnoraBase.Resource) { + // Yes. + val linkValuePermissionCode = PermissionUtilADM.getUserPermissionV1( + entityIri = valueIri, + entityCreator = valueCreator, + entityProject = resourceProject, + entityPermissionLiteral = valuePermissionLiteral, + userProfile = userProfileV1 + ) + + // Get the permission code for the target resource. + val targetResourceIri = row.rowMap(s"targetResource$index") + val targetResourceCreator = row.rowMap(s"targetResourceCreator$index") + val targetResourceProject = row.rowMap(s"targetResourceProject$index") + val targetResourcePermissionLiteral = row.rowMap(s"targetResourcePermissions$index") + + val targetResourcePermissionCode = PermissionUtilADM.getUserPermissionV1( + entityIri = targetResourceIri, + entityCreator = targetResourceCreator, + entityProject = targetResourceProject, + entityPermissionLiteral = targetResourcePermissionLiteral, + userProfile = userProfileV1 + ) + + // Only allow the user to see the match if they have view permission on both the link value and the target resource. + Seq(linkValuePermissionCode, targetResourcePermissionCode).min + } else { + // The matching object is an ordinary value, not a LinkValue. + PermissionUtilADM.getUserPermissionV1( + entityIri = valueIri, + entityCreator = valueCreator, + entityProject = resourceProject, + entityPermissionLiteral = valuePermissionLiteral, + userProfile = userProfileV1 + ) } - valueIri -> MatchingValue( - valueTypeIri = searchCriterion.valueType, - propertyIri = propertyIri, - propertyLabel = propertyLabel, - literal = literal, - valuePermissionCode = valuePermissionCode - ) - } + val propertyIri = searchCriterion.propertyIri + val propertyLabel = propertyInfo + .propertyInfoMap(propertyIri) + .getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(searchGetRequest.userProfile.lang, settings.fallbackLanguage) + ) match { + case Some(label) => label + case None => + throw InconsistentRepositoryDataException(s"Property $propertyIri has no rdfs:label") + } - // Filter out the values that the user doesn't have permission to see. - val filteredValues: Seq[(IRI, MatchingValue)] = valuesInRow.filter { - case (matchingValueIri, matchingValue) => matchingValue.valuePermissionCode.nonEmpty - } + valueIri -> MatchingValue( + valueTypeIri = searchCriterion.valueType, + propertyIri = propertyIri, + propertyLabel = propertyLabel, + literal = literal, + valuePermissionCode = valuePermissionCode + ) + } - valuesAcc ++ filteredValues - } + // Filter out the values that the user doesn't have permission to see. + val filteredValues: Seq[(IRI, MatchingValue)] = valuesInRow.filter { + case (matchingValueIri, matchingValue) => matchingValue.valuePermissionCode.nonEmpty + } - // Sort by value IRI, then by property IRI, so the results are consistent between requests. - val vectorOfMatchingValues: Vector[(IRI, MatchingValue)] = mapOfMatchingValues.toVector - val matchingValuesSortedByValueIri: Vector[MatchingValue] = - vectorOfMatchingValues.sortBy(_._1).map(_._2) - matchingValuesSortedByValueIri.sortBy(_.propertyIri) - } else { - Vector.empty[MatchingValue] + valuesAcc ++ filteredValues } - // Does the user have permission to see at least one matching value in the resource, or were there no search criteria referring to values? - if (matchingValues.nonEmpty || searchCriteria.isEmpty) { - // Yes. Make a search result for the resource. + // Sort by value IRI, then by property IRI, so the results are consistent between requests. + val vectorOfMatchingValues: Vector[(IRI, MatchingValue)] = mapOfMatchingValues.toVector + val matchingValuesSortedByValueIri: Vector[MatchingValue] = + vectorOfMatchingValues.sortBy(_._1).map(_._2) + matchingValuesSortedByValueIri.sortBy(_.propertyIri) + } else { + Vector.empty[MatchingValue] + } - val resourceClassIconURL = resourceClassIcon.map { resClassIcon => - valueUtilV1.makeResourceClassIconURL(resourceClassIri, resClassIcon) - } + // Does the user have permission to see at least one matching value in the resource, or were there no search criteria referring to values? + if (matchingValues.nonEmpty || searchCriteria.isEmpty) { + // Yes. Make a search result for the resource. - subjectsAcc :+ SearchResultRowV1( - obj_id = resourceIri, - preview_path = firstRowMap.get("previewPath") match { - case Some(path) => - Some(valueUtilV1.makeSipiImagePreviewGetUrlFromFilename(resourceProjectShortcode, path)) - case None => - // If there is no preview image, use the resource class icon from the ontology. - resourceClassIconURL - }, - iconsrc = resourceClassIconURL, - icontitle = resourceClassLabel, - iconlabel = resourceClassLabel, - valuetype_id = OntologyConstants.Rdfs.Label +: matchingValues.map(_.valueTypeIri), - valuelabel = "Label" +: matchingValues.map(_.propertyLabel), - value = resourceLabel +: matchingValues.map(_.literal), - preview_nx = firstRowMap.get("previewDimX") match { - case Some(previewDimX) => previewDimX.toInt - case None => settings.defaultIconSizeDimX - }, - preview_ny = firstRowMap.get("previewDimY") match { - case Some(previewDimY) => previewDimY.toInt - case None => settings.defaultIconSizeDimY - }, - rights = resourcePermissionCode - ) - } else { - // The user doesn't have permission to see any of the matching values. - subjectsAcc + val resourceClassIconURL = resourceClassIcon.map { resClassIcon => + valueUtilV1.makeResourceClassIconURL(resourceClassIri, resClassIcon) } + + subjectsAcc :+ SearchResultRowV1( + obj_id = resourceIri, + preview_path = firstRowMap.get("previewPath") match { + case Some(path) => + Some(valueUtilV1.makeSipiImagePreviewGetUrlFromFilename(resourceProjectShortcode, path)) + case None => + // If there is no preview image, use the resource class icon from the ontology. + resourceClassIconURL + }, + iconsrc = resourceClassIconURL, + icontitle = resourceClassLabel, + iconlabel = resourceClassLabel, + valuetype_id = OntologyConstants.Rdfs.Label +: matchingValues.map(_.valueTypeIri), + valuelabel = "Label" +: matchingValues.map(_.propertyLabel), + value = resourceLabel +: matchingValues.map(_.literal), + preview_nx = firstRowMap.get("previewDimX") match { + case Some(previewDimX) => previewDimX.toInt + case None => settings.defaultIconSizeDimX + }, + preview_ny = firstRowMap.get("previewDimY") match { + case Some(previewDimY) => previewDimY.toInt + case None => settings.defaultIconSizeDimY + }, + rights = resourcePermissionCode + ) } else { - // The user doesn't have permission to see the resource. + // The user doesn't have permission to see any of the matching values. subjectsAcc } + } else { + // The user doesn't have permission to see the resource. + subjectsAcc + } } .sortBy(_.obj_id) // Sort the matching resources by resource IRI so paging works. @@ -777,13 +795,13 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Creates a list of available search result pages. - * - * @param offset the requested result offset. - * @param limit the maximum number of results per page. - * @param resultCount the total number of results found. - * @return a list of [[SearchResultPage]] objects. - */ + * Creates a list of available search result pages. + * + * @param offset the requested result offset. + * @param limit the maximum number of results per page. + * @param resultCount the total number of results found. + * @return a list of [[SearchResultPage]] objects. + */ private def makePaging(offset: Int, limit: Int, resultCount: Int): Seq[SearchResultPage] = { val pageRemainder = resultCount % limit val numPages = (resultCount / limit) + (if (pageRemainder > 0) 1 else 0) @@ -804,10 +822,10 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Checks the requested search result limit to ensure it's within acceptable bounds. - * - * @return the corrected search result limit. - */ + * Checks the requested search result limit to ensure it's within acceptable bounds. + * + * @return the corrected search result limit. + */ private def checkLimit(limit: Int): Int = { if (limit <= 0) { throw BadRequestException("Search limit must be greater than 0") @@ -817,17 +835,15 @@ class SearchResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Given a list of search results, finds the maximum X and Y dimensions of their preview images. - * - * @param subjects a list of search results. - * @return the maximum X and Y dimensions of the preview images of the search results. - */ - private def findMaxPreviewDimensions(subjects: Seq[SearchResultRowV1]): (Int, Int) = { - subjects.foldLeft((0, 0)) { - case ((accX, accY), searchResultRow) => - val newMaxX = if (searchResultRow.preview_nx > accX) searchResultRow.preview_nx else accX - val newMaxY = if (searchResultRow.preview_ny > accY) searchResultRow.preview_ny else accY - (newMaxX, newMaxY) + * Given a list of search results, finds the maximum X and Y dimensions of their preview images. + * + * @param subjects a list of search results. + * @return the maximum X and Y dimensions of the preview images of the search results. + */ + private def findMaxPreviewDimensions(subjects: Seq[SearchResultRowV1]): (Int, Int) = + subjects.foldLeft((0, 0)) { case ((accX, accY), searchResultRow) => + val newMaxX = if (searchResultRow.preview_nx > accX) searchResultRow.preview_nx else accX + val newMaxY = if (searchResultRow.preview_ny > accY) searchResultRow.preview_ny else accY + (newMaxX, newMaxY) } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/StandoffResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/StandoffResponderV1.scala index 50d89a1722..55f3073a51 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/StandoffResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/StandoffResponderV1.scala @@ -42,13 +42,13 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import scala.concurrent.Future /** - * Responds to requests relating to the creation of mappings from XML elements and attributes to standoff classes and properties. - */ + * Responds to requests relating to the creation of mappings from XML elements and attributes to standoff classes and properties. + */ class StandoffResponderV1(responderData: ResponderData) extends Responder(responderData) { /** - * Receives a message of type [[StandoffResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message of type [[StandoffResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: StandoffResponderRequestV1) = msg match { case CreateMappingRequestV1(xml, label, projectIri, mappingName, featureFactoryConfig, userProfile, uuid) => createMappingV1(xml, label, projectIri, mappingName, featureFactoryConfig, userProfile, uuid) @@ -62,16 +62,18 @@ class StandoffResponderV1(responderData: ResponderData) extends Responder(respon val xsltCacheName = "xsltCache" /** - * Retrieves a `knora-base:XSLTransformation` in the triplestore and requests the corresponding XSL file from Sipi. - * - * @param xslTransformationIri The IRI of the resource representing the XSL Transformation (a [[org.knora.webapi.messages.OntologyConstants.KnoraBase.XSLTransformation]]). - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile The client making the request. - * @return a [[GetXSLTransformationResponseV1]]. - */ - private def getXSLTransformation(xslTransformationIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[GetXSLTransformationResponseV1] = { + * Retrieves a `knora-base:XSLTransformation` in the triplestore and requests the corresponding XSL file from Sipi. + * + * @param xslTransformationIri The IRI of the resource representing the XSL Transformation (a [[org.knora.webapi.messages.OntologyConstants.KnoraBase.XSLTransformation]]). + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile The client making the request. + * @return a [[GetXSLTransformationResponseV1]]. + */ + private def getXSLTransformation( + xslTransformationIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[GetXSLTransformationResponseV1] = { val xslTransformationFuture = for { xsltTransformation <- (responderManager ? GetXSLTransformationRequestV2( @@ -79,10 +81,9 @@ class StandoffResponderV1(responderData: ResponderData) extends Responder(respon featureFactoryConfig = featureFactoryConfig, requestingUser = userProfile )).mapTo[GetXSLTransformationResponseV2] - } yield - GetXSLTransformationResponseV1( - xslt = xsltTransformation.xslt - ) + } yield GetXSLTransformationResponseV1( + xslt = xsltTransformation.xslt + ) xslTransformationFuture.recover { case notFound: NotFoundException => @@ -93,20 +94,22 @@ class StandoffResponderV1(responderData: ResponderData) extends Responder(respon } /** - * Creates a mapping between XML elements and attributes to standoff classes and properties. - * The mapping is used to convert XML documents to [[TextValueV1]] and back. - * - * @param xml the provided mapping. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the client that made the request. - */ - private def createMappingV1(xml: String, - label: String, - projectIri: IRI, - mappingName: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM, - apiRequestID: UUID): Future[CreateMappingResponseV1] = { + * Creates a mapping between XML elements and attributes to standoff classes and properties. + * The mapping is used to convert XML documents to [[TextValueV1]] and back. + * + * @param xml the provided mapping. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the client that made the request. + */ + private def createMappingV1( + xml: String, + label: String, + projectIri: IRI, + mappingName: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM, + apiRequestID: UUID + ): Future[CreateMappingResponseV1] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -124,47 +127,45 @@ class StandoffResponderV1(responderData: ResponderData) extends Responder(respon for { mappingResponse <- (responderManager ? createMappingRequest).mapTo[CreateMappingResponseV2] - } yield - CreateMappingResponseV1( - mappingResponse.mappingIri - ) + } yield CreateMappingResponseV1( + mappingResponse.mappingIri + ) } /** - * The name of the mapping cache. - */ + * The name of the mapping cache. + */ val mappingCacheName = "mappingCache" /** - * Gets a mapping either from the cache or by making a request to the triplestore. - * - * @param mappingIri the IRI of the mapping to retrieve. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the user making the request. - * @return a [[MappingXMLtoStandoff]]. - */ - private def getMappingV1(mappingIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[GetMappingResponseV1] = { - + * Gets a mapping either from the cache or by making a request to the triplestore. + * + * @param mappingIri the IRI of the mapping to retrieve. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the user making the request. + * @return a [[MappingXMLtoStandoff]]. + */ + private def getMappingV1( + mappingIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[GetMappingResponseV1] = for { mappingResponse: GetMappingResponseV2 <- (responderManager ? GetMappingRequestV2( mappingIri = mappingIri, featureFactoryConfig = featureFactoryConfig, requestingUser = userProfile )).mapTo[GetMappingResponseV2] - } yield - GetMappingResponseV1( - mappingIri = mappingResponse.mappingIri, - mapping = mappingResponse.mapping, - standoffEntities = StandoffEntityInfoGetResponseV1( - standoffClassInfoMap = - ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(mappingResponse.standoffEntities.standoffClassInfoMap), - standoffPropertyInfoMap = - ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(mappingResponse.standoffEntities.standoffPropertyInfoMap) - ) + } yield GetMappingResponseV1( + mappingIri = mappingResponse.mappingIri, + mapping = mappingResponse.mapping, + standoffEntities = StandoffEntityInfoGetResponseV1( + standoffClassInfoMap = + ConvertOntologyClassV2ToV1.classInfoMapV2ToV1(mappingResponse.standoffEntities.standoffClassInfoMap), + standoffPropertyInfoMap = + ConvertOntologyClassV2ToV1.propertyInfoMapV2ToV1(mappingResponse.standoffEntities.standoffPropertyInfoMap) ) + ) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/UsersResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/UsersResponderV1.scala index 2e424a69bf..f07c458154 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/UsersResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/UsersResponderV1.scala @@ -41,8 +41,8 @@ import org.knora.webapi.util.cache.CacheUtil import scala.concurrent.Future /** - * Provides information about Knora users to other responders. - */ + * Provides information about Knora users to other responders. + */ class UsersResponderV1(responderData: ResponderData) extends Responder(responderData) { // The IRI used to lock user creation and update @@ -51,8 +51,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder val USER_PROFILE_CACHE_NAME = "userProfileCache" /** - * Receives a message of type [[UsersResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message of type [[UsersResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: UsersResponderRequestV1) = msg match { case UsersGetV1(userProfile) => usersGetV1(userProfile) case UsersGetRequestV1(userProfileV1) => usersGetRequestV1(userProfileV1) @@ -75,14 +75,12 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } /** - * Gets all the users and returns them as a sequence of [[UserDataV1]]. - * - * @return all the users as a sequence of [[UserDataV1]]. - */ - private def usersGetV1(userProfileV1: UserProfileV1): Future[Seq[UserDataV1]] = { - + * Gets all the users and returns them as a sequence of [[UserDataV1]]. + * + * @return all the users as a sequence of [[UserDataV1]]. + */ + private def usersGetV1(userProfileV1: UserProfileV1): Future[Seq[UserDataV1]] = //log.debug("usersGetV1") - for { _ <- Future( if (!userProfileV1.permissionData.isSystemAdmin) { @@ -95,7 +93,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder .getUsers( triplestore = settings.triplestoreType ) - .toString()) + .toString() + ) usersResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -106,31 +105,29 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder (userIri, rows.map(row => (row.rowMap("p"), row.rowMap("o"))).toMap) } - users = usersWithProperties.map { - case (userIri: IRI, propsMap: Map[String, String]) => - UserDataV1( - lang = propsMap.get(OntologyConstants.KnoraAdmin.PreferredLanguage) match { - case Some(langList) => langList - case None => settings.fallbackLanguage - }, - user_id = Some(userIri), - email = propsMap.get(OntologyConstants.KnoraAdmin.Email), - firstname = propsMap.get(OntologyConstants.KnoraAdmin.GivenName), - lastname = propsMap.get(OntologyConstants.KnoraAdmin.FamilyName), - status = propsMap.get(OntologyConstants.KnoraAdmin.Status).map(_.toBoolean) - ) + users = usersWithProperties.map { case (userIri: IRI, propsMap: Map[String, String]) => + UserDataV1( + lang = propsMap.get(OntologyConstants.KnoraAdmin.PreferredLanguage) match { + case Some(langList) => langList + case None => settings.fallbackLanguage + }, + user_id = Some(userIri), + email = propsMap.get(OntologyConstants.KnoraAdmin.Email), + firstname = propsMap.get(OntologyConstants.KnoraAdmin.GivenName), + lastname = propsMap.get(OntologyConstants.KnoraAdmin.FamilyName), + status = propsMap.get(OntologyConstants.KnoraAdmin.Status).map(_.toBoolean) + ) }.toSeq } yield users - } /** - * Gets all the users and returns them as a [[UsersGetResponseV1]]. - * - * @param userProfileV1 the type of the requested profile (restricted of full). - * @return all the users as a [[UsersGetResponseV1]]. - */ - private def usersGetRequestV1(userProfileV1: UserProfileV1): Future[UsersGetResponseV1] = { + * Gets all the users and returns them as a [[UsersGetResponseV1]]. + * + * @param userProfileV1 the type of the requested profile (restricted of full). + * @return all the users as a [[UsersGetResponseV1]]. + */ + private def usersGetRequestV1(userProfileV1: UserProfileV1): Future[UsersGetResponseV1] = for { maybeUsersListToReturn <- usersGetV1(userProfileV1) result = maybeUsersListToReturn match { @@ -138,17 +135,15 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder case _ => throw NotFoundException(s"No users found") } } yield result - } /** - * Gets basic information about a Knora user, and returns it in a [[UserDataV1]]. - * - * @param userIri the IRI of the user. - * @return a [[UserDataV1]] describing the user. - */ - private def userDataByIriGetV1(userIri: IRI, short: Boolean): Future[Option[UserDataV1]] = { + * Gets basic information about a Knora user, and returns it in a [[UserDataV1]]. + * + * @param userIri the IRI of the user. + * @return a [[UserDataV1]] describing the user. + */ + private def userDataByIriGetV1(userIri: IRI, short: Boolean): Future[Option[UserDataV1]] = //log.debug("userDataByIriGetV1 - userIri: {}", userIri) - for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -156,7 +151,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, userIri = userIri ) - .toString()) + .toString() + ) // _ = log.debug("userDataByIRIGetV1 - sparqlQueryString: {}", sparqlQueryString) @@ -168,22 +164,21 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } yield maybeUserDataV1 - } - /** - * Gets information about a Knora user, and returns it in a [[UserProfileV1]]. If possible, tries to retrieve the - * user profile from cache. If not, it retrieves it from the triplestore and writes it to the cache. - * - * @param userIri the IRI of the user. - * @param profileType the type of the requested profile (restricted of full). - * @param featureFactoryConfig the feature factory configuration. - * @return a [[UserProfileV1]] describing the user. - */ - private def userProfileByIRIGetV1(userIri: IRI, - profileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig): Future[Option[UserProfileV1]] = { + * Gets information about a Knora user, and returns it in a [[UserProfileV1]]. If possible, tries to retrieve the + * user profile from cache. If not, it retrieves it from the triplestore and writes it to the cache. + * + * @param userIri the IRI of the user. + * @param profileType the type of the requested profile (restricted of full). + * @param featureFactoryConfig the feature factory configuration. + * @return a [[UserProfileV1]] describing the user. + */ + private def userProfileByIRIGetV1( + userIri: IRI, + profileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Option[UserProfileV1]] = // log.debug(s"userProfileByIRIGetV1: userIri = $userIRI', clean = '$profileType'") - CacheUtil.get[UserProfileV1](USER_PROFILE_CACHE_NAME, userIri) match { case Some(userProfile) => // found a user profile in the cache @@ -198,7 +193,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, userIri = userIri ) - .toString()) + .toString() + ) // _ = log.debug(s"userProfileByIRIGetV1 - sparqlQueryString: {}", sparqlQueryString) @@ -218,21 +214,22 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder // _ = log.debug("userProfileByIRIGetV1 - maybeUserProfileV1: {}", MessageUtil.toSource(maybeUserProfileV1)) } yield result // UserProfileV1(userData, groups, projects_info, sessionId, isSystemUser, permissionData) } - } /** - * Gets information about a Knora user, and returns it as a [[UserProfileResponseV1]]. - * - * @param userIRI the IRI of the user. - * @param profileType the type of the requested profile (restriced or full). - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the requesting user's profile. - * @return a [[UserProfileResponseV1]] - */ - private def userProfileByIRIGetRequestV1(userIRI: IRI, - profileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserProfileV1): Future[UserProfileResponseV1] = { + * Gets information about a Knora user, and returns it as a [[UserProfileResponseV1]]. + * + * @param userIRI the IRI of the user. + * @param profileType the type of the requested profile (restriced or full). + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the requesting user's profile. + * @return a [[UserProfileResponseV1]] + */ + private def userProfileByIRIGetRequestV1( + userIRI: IRI, + profileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserProfileV1 + ): Future[UserProfileResponseV1] = for { _ <- Future( if (!userProfile.permissionData.isSystemAdmin && !userProfile.userData.user_id.contains(userIRI)) { @@ -250,22 +247,22 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder case None => throw NotFoundException(s"User '$userIRI' not found") } } yield result - } /** - * Gets information about a Knora user, and returns it in a [[UserProfileV1]]. If possible, tries to retrieve the user profile - * from cache. If not, it retrieves it from the triplestore and writes it to the cache. - * - * @param email the email of the user. - * @param profileType the type of the requested profile (restricted or full). - * @param featureFactoryConfig the feature factory configuration. - * @return a [[UserProfileV1]] describing the user. - */ - private def userProfileByEmailGetV1(email: String, - profileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig): Future[Option[UserProfileV1]] = { + * Gets information about a Knora user, and returns it in a [[UserProfileV1]]. If possible, tries to retrieve the user profile + * from cache. If not, it retrieves it from the triplestore and writes it to the cache. + * + * @param email the email of the user. + * @param profileType the type of the requested profile (restricted or full). + * @param featureFactoryConfig the feature factory configuration. + * @return a [[UserProfileV1]] describing the user. + */ + private def userProfileByEmailGetV1( + email: String, + profileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Option[UserProfileV1]] = // log.debug(s"userProfileByEmailGetV1: username = '{}', type = '{}'", email, profileType) - CacheUtil.get[UserProfileV1](USER_PROFILE_CACHE_NAME, email) match { case Some(userProfile) => // found a user profile in the cache @@ -280,7 +277,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, email = email ) - .toString()) + .toString() + ) //_ = log.debug(s"userProfileByEmailGetV1 - sparqlQueryString: $sparqlQueryString") userDataQueryResponse <- (storeManager ? SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] @@ -302,22 +300,22 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } yield result // UserProfileV1(userDataV1, groupIris, projectIris) } - } - /** - * Gets information about a Knora user, and returns it as a [[UserProfileResponseV1]]. - * - * @param email the email of the user. - * @param profileType the type of the requested profile (restricted or full). - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the requesting user's profile. - * @return a [[UserProfileResponseV1]] - * @throws NotFoundException if the user with the supplied email is not found. - */ - private def userProfileByEmailGetRequestV1(email: String, - profileType: UserProfileType, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserProfileV1): Future[UserProfileResponseV1] = { + * Gets information about a Knora user, and returns it as a [[UserProfileResponseV1]]. + * + * @param email the email of the user. + * @param profileType the type of the requested profile (restricted or full). + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the requesting user's profile. + * @return a [[UserProfileResponseV1]] + * @throws NotFoundException if the user with the supplied email is not found. + */ + private def userProfileByEmailGetRequestV1( + email: String, + profileType: UserProfileType, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserProfileV1 + ): Future[UserProfileResponseV1] = for { maybeUserProfileToReturn <- userProfileByEmailGetV1( email = email, @@ -330,19 +328,20 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder case None => throw NotFoundException(s"User '$email' not found") } } yield result - } /** - * Returns the user's project memberships, where the result contains the IRIs of the projects the user is member of. - * - * @param userIri the user's IRI. - * @param userProfileV1 the user profile of the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[UserProjectMembershipsGetResponseV1]]. - */ - def userProjectMembershipsGetRequestV1(userIri: IRI, - userProfileV1: UserProfileV1, - apiRequestID: UUID): Future[UserProjectMembershipsGetResponseV1] = { + * Returns the user's project memberships, where the result contains the IRIs of the projects the user is member of. + * + * @param userIri the user's IRI. + * @param userProfileV1 the user profile of the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[UserProjectMembershipsGetResponseV1]]. + */ + def userProjectMembershipsGetRequestV1( + userIri: IRI, + userProfileV1: UserProfileV1, + apiRequestID: UUID + ): Future[UserProjectMembershipsGetResponseV1] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -350,7 +349,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, userIri = userIri ) - .toString()) + .toString() + ) //_ = log.debug("userDataByIRIGetV1 - sparqlQueryString: {}", sparqlQueryString) @@ -368,20 +368,21 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder // _ = log.debug("userProjectMembershipsGetRequestV1 - userIri: {}, projectIris: {}", userIri, projectIris) } yield UserProjectMembershipsGetResponseV1(projects = projectIris) - } /** - * Returns the user's project admin group memberships, where the result contains the IRIs of the projects the user - * is a member of the project admin group. - * - * @param userIri the user's IRI. - * @param userProfileV1 the user profile of the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[UserProjectMembershipsGetResponseV1]]. - */ - def userProjectAdminMembershipsGetRequestV1(userIri: IRI, - userProfileV1: UserProfileV1, - apiRequestID: UUID): Future[UserProjectAdminMembershipsGetResponseV1] = { + * Returns the user's project admin group memberships, where the result contains the IRIs of the projects the user + * is a member of the project admin group. + * + * @param userIri the user's IRI. + * @param userProfileV1 the user profile of the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[UserProjectMembershipsGetResponseV1]]. + */ + def userProjectAdminMembershipsGetRequestV1( + userIri: IRI, + userProfileV1: UserProfileV1, + apiRequestID: UUID + ): Future[UserProjectAdminMembershipsGetResponseV1] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -389,7 +390,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, userIri = userIri ) - .toString()) + .toString() + ) //_ = log.debug("userDataByIRIGetV1 - sparqlQueryString: {}", sparqlQueryString) @@ -407,20 +409,20 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder // _ = log.debug("userProjectAdminMembershipsGetRequestV1 - userIri: {}, projectIris: {}", userIri, projectIris) } yield UserProjectAdminMembershipsGetResponseV1(projects = projectIris) - } /** - * Returns the user's custom (without ProjectMember and ProjectAdmin) group memberships - * - * @param userIri the user's IRI. - * @param userProfileV1 the user profile of the requesting user. - * @param apiRequestID the unique api request ID. - * @return a [[UserGroupMembershipsGetResponseV1]] - */ - def userGroupMembershipsGetRequestV1(userIri: IRI, - userProfileV1: UserProfileV1, - apiRequestID: UUID): Future[UserGroupMembershipsGetResponseV1] = { - + * Returns the user's custom (without ProjectMember and ProjectAdmin) group memberships + * + * @param userIri the user's IRI. + * @param userProfileV1 the user profile of the requesting user. + * @param apiRequestID the unique api request ID. + * @return a [[UserGroupMembershipsGetResponseV1]] + */ + def userGroupMembershipsGetRequestV1( + userIri: IRI, + userProfileV1: UserProfileV1, + apiRequestID: UUID + ): Future[UserGroupMembershipsGetResponseV1] = for { sparqlQueryString <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -428,7 +430,8 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder triplestore = settings.triplestoreType, userIri = userIri ) - .toString()) + .toString() + ) //_ = log.debug("userDataByIRIGetV1 - sparqlQueryString: {}", sparqlQueryString) @@ -447,30 +450,28 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } yield UserGroupMembershipsGetResponseV1(groups = groupIris) - } - //////////////////// // Helper Methods // //////////////////// /** - * Helper method used to create a [[UserDataV1]] from the [[SparqlSelectResult]] containing user data. - * - * @param userDataQueryResponse a [[SparqlSelectResult]] containing user data. - * @param short denotes if all information should be returned. If short == true, then no token and password should be returned. - * @return a [[UserDataV1]] containing the user's basic data. - */ - private def userDataQueryResponse2UserDataV1(userDataQueryResponse: SparqlSelectResult, - short: Boolean): Future[Option[UserDataV1]] = { - + * Helper method used to create a [[UserDataV1]] from the [[SparqlSelectResult]] containing user data. + * + * @param userDataQueryResponse a [[SparqlSelectResult]] containing user data. + * @param short denotes if all information should be returned. If short == true, then no token and password should be returned. + * @return a [[UserDataV1]] containing the user's basic data. + */ + private def userDataQueryResponse2UserDataV1( + userDataQueryResponse: SparqlSelectResult, + short: Boolean + ): Future[Option[UserDataV1]] = // log.debug("userDataQueryResponse2UserDataV1 - " + MessageUtil.toSource(userDataQueryResponse)) - if (userDataQueryResponse.results.bindings.nonEmpty) { val returnedUserIri = userDataQueryResponse.getFirstRow.rowMap("s") val groupedUserData: Map[String, Seq[String]] = - userDataQueryResponse.results.bindings.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) + userDataQueryResponse.results.bindings.groupBy(_.rowMap("p")).map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) } // _ = log.debug(s"userDataQueryResponse2UserProfileV1 - groupedUserData: ${MessageUtil.toSource(groupedUserData)}") @@ -494,27 +495,25 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } else { FastFuture.successful(None) } - } /** - * Helper method used to create a [[UserProfileV1]] from the [[SparqlSelectResult]] containing user data. - * - * @param userDataQueryResponse a [[SparqlSelectResult]] containing user data. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[UserProfileV1]] containing the user's data. - */ + * Helper method used to create a [[UserProfileV1]] from the [[SparqlSelectResult]] containing user data. + * + * @param userDataQueryResponse a [[SparqlSelectResult]] containing user data. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[UserProfileV1]] containing the user's data. + */ private def userDataQueryResponse2UserProfileV1( - userDataQueryResponse: SparqlSelectResult, - featureFactoryConfig: FeatureFactoryConfig): Future[Option[UserProfileV1]] = { - + userDataQueryResponse: SparqlSelectResult, + featureFactoryConfig: FeatureFactoryConfig + ): Future[Option[UserProfileV1]] = // log.debug("userDataQueryResponse2UserProfileV1 - userDataQueryResponse: {}", MessageUtil.toSource(userDataQueryResponse)) - if (userDataQueryResponse.results.bindings.nonEmpty) { val returnedUserIri = userDataQueryResponse.getFirstRow.rowMap("s") val groupedUserData: Map[String, Seq[String]] = - userDataQueryResponse.results.bindings.groupBy(_.rowMap("p")).map { - case (predicate, rows) => predicate -> rows.map(_.rowMap("o")) + userDataQueryResponse.results.bindings.groupBy(_.rowMap("p")).map { case (predicate, rows) => + predicate -> rows.map(_.rowMap("o")) } // log.debug("userDataQueryResponse2UserProfileV1 - groupedUserData: {}", MessageUtil.toSource(groupedUserData)) @@ -597,71 +596,70 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } else { FastFuture.successful(None) } - } /** - * Helper method for checking if a user exists. - * - * @param userIri the IRI of the user. - * @return a [[Boolean]]. - */ - def userExists(userIri: IRI): Future[Boolean] = { + * Helper method for checking if a user exists. + * + * @param userIri the IRI of the user. + * @return a [[Boolean]]. + */ + def userExists(userIri: IRI): Future[Boolean] = for { askString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v1.txt.checkUserExists(userIri = userIri).toString) + org.knora.webapi.messages.twirl.queries.sparql.v1.txt.checkUserExists(userIri = userIri).toString + ) // _ = log.debug("userExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkUserExistsResponse.result } yield result - } /** - * Helper method for checking if a project exists. - * - * @param projectIri the IRI of the project. - * @return a [[Boolean]]. - */ - def projectExists(projectIri: IRI): Future[Boolean] = { + * Helper method for checking if a project exists. + * + * @param projectIri the IRI of the project. + * @return a [[Boolean]]. + */ + def projectExists(projectIri: IRI): Future[Boolean] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkProjectExistsByIri(projectIri = projectIri) - .toString) + .toString + ) // _ = log.debug("projectExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkUserExistsResponse.result } yield result - } /** - * Helper method for checking if a group exists. - * - * @param groupIri the IRI of the group. - * @return a [[Boolean]]. - */ - def groupExists(groupIri: IRI): Future[Boolean] = { + * Helper method for checking if a group exists. + * + * @param groupIri the IRI of the group. + * @return a [[Boolean]]. + */ + def groupExists(groupIri: IRI): Future[Boolean] = for { askString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkGroupExistsByIri(groupIri = groupIri).toString) + org.knora.webapi.messages.twirl.queries.sparql.admin.txt.checkGroupExistsByIri(groupIri = groupIri).toString + ) // _ = log.debug("groupExists - query: {}", askString) checkUserExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] result = checkUserExistsResponse.result } yield result - } /** - * Writes the user profile to cache. - * - * @param userProfile a [[UserProfileV1]]. - * @return true if writing was successful. - * @throws ApplicationCacheException when there is a problem with writing the user's profile to cache. - */ + * Writes the user profile to cache. + * + * @param userProfile a [[UserProfileV1]]. + * @return true if writing was successful. + * @throws ApplicationCacheException when there is a problem with writing the user's profile to cache. + */ private def writeUserProfileV1ToCache(userProfile: UserProfileV1): Boolean = { val iri = if (userProfile.userData.user_id.nonEmpty) { @@ -691,11 +689,11 @@ class UsersResponderV1(responderData: ResponderData) extends Responder(responder } /** - * Removes the user profile from cache. - * - * @param userIri the user's IRI und which a profile could be cached. - * @param email the user's email under which a profile could be cached. - */ + * Removes the user profile from cache. + * + * @param userIri the user's IRI und which a profile could be cached. + * @param email the user's email under which a profile could be cached. + */ private def invalidateCachedUserProfileV1(userIri: Option[IRI] = None, email: Option[String] = None): Unit = { if (userIri.nonEmpty) { diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/ValuesResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/ValuesResponderV1.scala index d5492cfa14..22fc6fd862 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/ValuesResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/ValuesResponderV1.scala @@ -61,15 +61,15 @@ import scala.annotation.tailrec import scala.concurrent.Future /** - * Updates Knora values. - */ + * Updates Knora values. + */ class ValuesResponderV1(responderData: ResponderData) extends Responder(responderData) { // Converts SPARQL query results to ApiValueV1 objects. val valueUtilV1 = new ValueUtilV1(settings) /** - * Receives a message of type [[ValuesResponderRequestV1]], and returns an appropriate response message. - */ + * Receives a message of type [[ValuesResponderRequestV1]], and returns an appropriate response message. + */ def receive(msg: ValuesResponderRequestV1) = msg match { case ValueGetRequestV1(valueIri, featureFactoryConfig, userProfile) => getValueResponseV1(valueIri, featureFactoryConfig, userProfile) @@ -93,16 +93,18 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Methods for generating complete API responses. /** - * Queries a `knora-base:Value` and returns a [[ValueGetResponseV1]] describing it. - * - * @param valueIri the IRI of the value to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ValueGetResponseV1]]. - */ - private def getValueResponseV1(valueIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ValueGetResponseV1] = { + * Queries a `knora-base:Value` and returns a [[ValueGetResponseV1]] describing it. + * + * @param valueIri the IRI of the value to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ValueGetResponseV1]]. + */ + private def getValueResponseV1( + valueIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ValueGetResponseV1] = for { maybeValueQueryResult <- findValue( valueIri = valueIri, @@ -123,38 +125,36 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case Some(up) => up case None => throw NotFoundException(s"User ${valueQueryResult.creatorIri} not found") } - } yield - ValueGetResponseV1( - valuetype = valueQueryResult.value.valueTypeIri, - rights = valueQueryResult.permissionCode, - value = valueQueryResult.value, - valuecreator = valueCreatorProfile.userData.email.get, - valuecreatorname = valueCreatorProfile.userData.fullname.get, - valuecreationdate = valueQueryResult.creationDate, - comment = valueQueryResult.comment - ) + } yield ValueGetResponseV1( + valuetype = valueQueryResult.value.valueTypeIri, + rights = valueQueryResult.permissionCode, + value = valueQueryResult.value, + valuecreator = valueCreatorProfile.userData.email.get, + valuecreatorname = valueCreatorProfile.userData.fullname.get, + valuecreationdate = valueQueryResult.creationDate, + comment = valueQueryResult.comment + ) case None => throw NotFoundException(s"Value $valueIri not found (it may have been deleted)") } } yield response - } /** - * Creates a new value of a resource property (as opposed to a new version of an existing value). - * - * @param createValueRequest the request message. - * @return a [[CreateValueResponseV1]] if the update was successful. - */ + * Creates a new value of a resource property (as opposed to a new version of an existing value). + * + * @param createValueRequest the request message. + * @return a [[CreateValueResponseV1]] if the update was successful. + */ private def createValueV1(createValueRequest: CreateValueRequestV1): Future[CreateValueResponseV1] = { /** - * Creates a [[Future]] that does pre-update checks and performs the update. This function will be - * called by [[IriLocker]] once it has acquired an update lock on the resource. - * - * @param userIri the IRI of the user making the request. - * @return a [[Future]] that does pre-update checks and performs the update. - */ + * Creates a [[Future]] that does pre-update checks and performs the update. This function will be + * called by [[IriLocker]] once it has acquired an update lock on the resource. + * + * @param userIri the IRI of the user making the request. + * @return a [[Future]] that does pre-update checks and performs the update. + */ def makeTaskFuture(userIri: IRI): Future[CreateValueResponseV1] = for { // Check that the submitted value has the correct type for the property. @@ -168,7 +168,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde .getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint) .getOrElse { throw InconsistentRepositoryDataException( - s"Property ${createValueRequest.propertyIri} has no knora-base:objectClassConstraint") + s"Property ${createValueRequest.propertyIri} has no knora-base:objectClassConstraint" + ) } // Check that the object of the property (the value to be created, or the target of the link to be created) will have @@ -193,11 +194,15 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde resourcePermissionCode: Option[Int] = resourceFullResponse.resdata.flatMap(resdata => resdata.rights) - _ = if (!PermissionUtilADM.impliesPermissionCodeV1(userHasPermissionCode = resourcePermissionCode, - userNeedsPermission = - OntologyConstants.KnoraBase.ModifyPermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = resourcePermissionCode, + userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to modify resource ${createValueRequest.resourceIri}") + s"User $userIri does not have permission to modify resource ${createValueRequest.resourceIri}" + ) } // Ensure that creating the value would not violate the resource's cardinality restrictions or create a duplicate value. @@ -207,7 +212,9 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // the property, this could be because the property isn't allowed for the resource, or because it's allowed, has a // cardinality of MustHaveOne or MayHaveOne, and already has a value that the user isn't allowed to see. We'll have to // implement this in a different way in API v2. - cardinalityOK = resourceFullResponse.props.flatMap(_.properties.find(_.pid == createValueRequest.propertyIri)) match { + cardinalityOK = resourceFullResponse.props.flatMap( + _.properties.find(_.pid == createValueRequest.propertyIri) + ) match { case Some(prop: PropertyV1) => if (prop.values.exists(apiValueV1 => createValueRequest.value.isDuplicateOfOtherValue(apiValueV1))) { throw DuplicateValueException() @@ -222,21 +229,26 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde _ = if (!cardinalityOK) { throw OntologyConstraintException( - s"Cardinality restrictions do not allow a value to be added for property ${createValueRequest.propertyIri} of resource ${createValueRequest.resourceIri}") + s"Cardinality restrictions do not allow a value to be added for property ${createValueRequest.propertyIri} of resource ${createValueRequest.resourceIri}" + ) } // Get the IRI of project of the containing resource. projectIri: IRI = resourceFullResponse.resinfo .getOrElse( throw InconsistentRepositoryDataException( - s"Did not find resource info for resource ${createValueRequest.resourceIri}")) + s"Did not find resource info for resource ${createValueRequest.resourceIri}" + ) + ) .project_id // Get the resource class of the containing resource resourceClassIri: IRI = resourceFullResponse.resinfo .getOrElse( throw InconsistentRepositoryDataException( - s"Did not find resource info for resource ${createValueRequest.resourceIri}")) + s"Did not find resource info for resource ${createValueRequest.resourceIri}" + ) + ) .restype_id defaultObjectAccessPermissions <- { @@ -309,55 +321,59 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Generates SPARQL for creating multiple values in a new, empty resource, using an existing transaction. - * The resource ''must'' be a new, empty resource, i.e. it must have no values. All pre-update checks must already - * have been performed. Specifically, this method assumes that: - * - * - The requesting user has permission to add values to the resource. - * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed to point to it. - * - The resource has a suitable cardinality for each submitted value. - * - All required values are provided. - * - * @param createMultipleValuesRequest the request message. - * @return a [[GenerateSparqlToCreateMultipleValuesResponseV1]]. - */ - private def createMultipleValuesV1(createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV1) - : Future[GenerateSparqlToCreateMultipleValuesResponseV1] = { + * Generates SPARQL for creating multiple values in a new, empty resource, using an existing transaction. + * The resource ''must'' be a new, empty resource, i.e. it must have no values. All pre-update checks must already + * have been performed. Specifically, this method assumes that: + * + * - The requesting user has permission to add values to the resource. + * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed to point to it. + * - The resource has a suitable cardinality for each submitted value. + * - All required values are provided. + * + * @param createMultipleValuesRequest the request message. + * @return a [[GenerateSparqlToCreateMultipleValuesResponseV1]]. + */ + private def createMultipleValuesV1( + createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV1 + ): Future[GenerateSparqlToCreateMultipleValuesResponseV1] = { /** - * Creates a [[Future]] that performs the update. This function will be called by [[IriLocker]] once it - * has acquired an update lock on the resource. - * - * @param userIri the IRI of the user making the request. - * @return a [[Future]] that does pre-update checks and performs the update. - */ + * Creates a [[Future]] that performs the update. This function will be called by [[IriLocker]] once it + * has acquired an update lock on the resource. + * + * @param userIri the IRI of the user making the request. + * @return a [[Future]] that does pre-update checks and performs the update. + */ def makeTaskFuture(userIri: IRI): Future[GenerateSparqlToCreateMultipleValuesResponseV1] = { /** - * Assists in the numbering of values to be created. - * - * @param createValueV1WithComment the value to be created. - * @param valueIndex the index of the value in the sequence of all values to be created. This will be used - * to generate unique SPARQL variable names. - * @param valueHasOrder the index of the value in the sequence of values to be created for a particular property. - * This will be used to generate `knora-base:valueHasOrder`. - */ - case class NumberedValueToCreate(createValueV1WithComment: CreateValueV1WithComment, - valueIndex: Int, - valueHasOrder: Int) + * Assists in the numbering of values to be created. + * + * @param createValueV1WithComment the value to be created. + * @param valueIndex the index of the value in the sequence of all values to be created. This will be used + * to generate unique SPARQL variable names. + * @param valueHasOrder the index of the value in the sequence of values to be created for a particular property. + * This will be used to generate `knora-base:valueHasOrder`. + */ + case class NumberedValueToCreate( + createValueV1WithComment: CreateValueV1WithComment, + valueIndex: Int, + valueHasOrder: Int + ) /** - * Assists in collecting generated SPARQL as well as other information about values to be created for - * a particular property. - * - * @param insertSparql statements to be included in the SPARQL INSERT clause. - * @param valuesToVerify information about each value to be created. - * @param valueIndexes the value index of each value described by this object (so they can be sorted). - */ - case class SparqlGenerationResultForProperty(insertSparql: Vector[String] = Vector.empty[String], - valuesToVerify: Vector[UnverifiedValueV1] = - Vector.empty[UnverifiedValueV1], - valueIndexes: Vector[Int] = Vector.empty[Int]) + * Assists in collecting generated SPARQL as well as other information about values to be created for + * a particular property. + * + * @param insertSparql statements to be included in the SPARQL INSERT clause. + * @param valuesToVerify information about each value to be created. + * @param valueIndexes the value index of each value described by this object (so they can be sorted). + */ + case class SparqlGenerationResultForProperty( + insertSparql: Vector[String] = Vector.empty[String], + valuesToVerify: Vector[UnverifiedValueV1] = Vector.empty[UnverifiedValueV1], + valueIndexes: Vector[Int] = Vector.empty[Int] + ) for { //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -412,7 +428,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // So now we can get the set of standoff link targets that are ordinary IRIs, and check that each of // them exists in the triplestore and is a knora-base:Resource. targetIrisThatAlreadyExist: Set[IRI] = targetIris.keySet.filterNot(iri => - stringFormatter.isStandoffLinkReferenceToClientIDForResource(iri)) + stringFormatter.isStandoffLinkReferenceToClientIDForResource(iri) + ) targetIriCheckResult <- checkStandoffResourceReferenceTargets( targetIris = targetIrisThatAlreadyExist, @@ -427,7 +444,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // If the target of a standoff link is a client ID for a resource, convert it to the corresponding real resource IRI. val realTargetIri = stringFormatter.toRealStandoffLinkTargetResourceIri( iri = targetIri, - clientResourceIDsToResourceIris = createMultipleValuesRequest.clientResourceIDsToResourceIris) + clientResourceIDsToResourceIris = createMultipleValuesRequest.clientResourceIDsToResourceIris + ) SparqlTemplateLinkUpdate( linkPropertyIri = OntologyConstants.KnoraBase.HasStandoffLinkTo.toSmartIri, @@ -435,7 +453,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde insertDirectLink = true, deleteDirectLink = false, linkValueExists = false, - linkTargetExists = true, // doesn't matter, the generateInsertStatementsForStandoffLinks template doesn't use it + linkTargetExists = + true, // doesn't matter, the generateInsertStatementsForStandoffLinks template doesn't use it newLinkValueIri = stringFormatter.makeRandomValueIri(createMultipleValuesRequest.resourceIri), linkTargetIri = realTargetIri, currentReferenceCount = 0, @@ -477,8 +496,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Generate knora-base:valueHasOrder for the values of each property. groupedNumberedValuesWithValueHasOrder: Map[IRI, Seq[NumberedValueToCreate]] = groupedNumberedValues.map { case (propertyIri, values) => - val valuesWithValueHasOrder = values.zipWithIndex.map { - case (numberedValueToCreate, valueHasOrder) => numberedValueToCreate.copy(valueHasOrder = valueHasOrder) + val valuesWithValueHasOrder = values.zipWithIndex.map { case (numberedValueToCreate, valueHasOrder) => + numberedValueToCreate.copy(valueHasOrder = valueHasOrder) } (propertyIri, valuesWithValueHasOrder) @@ -488,8 +507,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Generate SPARQL for each value of each property // Make a SparqlGenerationResultForProperty for each property. - sparqlGenerationResults: Map[IRI, SparqlGenerationResultForProperty] = groupedNumberedValuesWithValueHasOrder - .map { + sparqlGenerationResults: Map[IRI, SparqlGenerationResultForProperty] = + groupedNumberedValuesWithValueHasOrder.map { case (propertyIri: IRI, valuesToCreate: Seq[NumberedValueToCreate]) => val defaultPropertyAccessPermissions: String = createMultipleValuesRequest.defaultPropertyAccessPermissions(propertyIri) @@ -578,7 +597,10 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde propertyIri = propertyIri, value = valueWithRealStandoffLinkIris, newValueIri = newValueIri, - linkUpdates = Seq.empty[SparqlTemplateLinkUpdate], // This is empty because we have to generate SPARQL for standoff links separately. + linkUpdates = + Seq.empty[ + SparqlTemplateLinkUpdate + ], // This is empty because we have to generate SPARQL for standoff links separately. maybeComment = valueToCreate.createValueV1WithComment.comment, valueCreator = userIri, valuePermissions = defaultPropertyAccessPermissions, @@ -593,8 +615,10 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // in the SparqlGenerationResultForProperty. propertyAcc.copy( insertSparql = propertyAcc.insertSparql :+ insertSparql, - valuesToVerify = propertyAcc.valuesToVerify :+ UnverifiedValueV1(newValueIri = newValueIri, - value = updateValueV1), + valuesToVerify = propertyAcc.valuesToVerify :+ UnverifiedValueV1( + newValueIri = newValueIri, + value = updateValueV1 + ), valueIndexes = propertyAcc.valueIndexes :+ valueToCreate.valueIndex ) } @@ -620,11 +644,10 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case (propertyIri, results) => propertyIri -> results.valuesToVerify } - } yield - GenerateSparqlToCreateMultipleValuesResponseV1( - insertSparql = allInsertSparql, - unverifiedValues = allUnverifiedValues - ) + } yield GenerateSparqlToCreateMultipleValuesResponseV1( + insertSparql = allInsertSparql, + unverifiedValues = allUnverifiedValues + ) } for { @@ -647,14 +670,15 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Verifies the creation of multiple values. - * - * @param verifyRequest a [[VerifyMultipleValueCreationRequestV1]]. - * @return a [[VerifyMultipleValueCreationResponseV1]] if all values were created successfully, or a failed - * future if any values were not created. - */ + * Verifies the creation of multiple values. + * + * @param verifyRequest a [[VerifyMultipleValueCreationRequestV1]]. + * @return a [[VerifyMultipleValueCreationResponseV1]] if all values were created successfully, or a failed + * future if any values were not created. + */ private def verifyMultipleValueCreation( - verifyRequest: VerifyMultipleValueCreationRequestV1): Future[VerifyMultipleValueCreationResponseV1] = { + verifyRequest: VerifyMultipleValueCreationRequestV1 + ): Future[VerifyMultipleValueCreationResponseV1] = { // We have a Map of property IRIs to sequences of UnverifiedCreateValueResponseV1s. Query each value and // build a Map with the same structure, except that instead of UnverifiedCreateValueResponseV1s, it contains Futures // providing the results of querying the values. @@ -676,36 +700,39 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // when they're available. for { valueVerificationResponses: Map[IRI, Seq[CreateValueResponseV1]] <- ActorUtil.sequenceFutureSeqsInMap( - valueVerificationFutures) + valueVerificationFutures + ) } yield VerifyMultipleValueCreationResponseV1(verifiedValues = valueVerificationResponses) } /** - * Adds a new version of an existing file value. - * - * @param changeFileValueRequest a [[ChangeFileValueRequestV1]] sent by the values route. - * @return a [[ChangeFileValueResponseV1]] representing all the changed file values. - */ + * Adds a new version of an existing file value. + * + * @param changeFileValueRequest a [[ChangeFileValueRequestV1]] sent by the values route. + * @return a [[ChangeFileValueResponseV1]] representing all the changed file values. + */ private def changeFileValueV1(changeFileValueRequest: ChangeFileValueRequestV1): Future[ChangeFileValueResponseV1] = { /** - * Temporary structure to represent existing file values of a resource. - * - * @param property the property IRI (e.g., hasStillImageFileValueRepresentation) - * @param valueObjectIri the IRI of the value object. - * @param quality the quality of the file value - */ + * Temporary structure to represent existing file values of a resource. + * + * @param property the property IRI (e.g., hasStillImageFileValueRepresentation) + * @param valueObjectIri the IRI of the value object. + * @param quality the quality of the file value + */ case class CurrentFileValue(property: IRI, valueObjectIri: IRI, quality: Option[Int]) /** - * Changes a file value in the triplestore. - * - * @param changeFileValueRequest a [[ChangeFileValueRequestV1]] sent by the values route. - * @param projectADM the project in which the value is being updated. - * @return a [[ChangeFileValueResponseV1]] representing all the changed file values. - */ - def makeTaskFuture(changeFileValueRequest: ChangeFileValueRequestV1, - projectADM: ProjectADM): Future[ChangeFileValueResponseV1] = { + * Changes a file value in the triplestore. + * + * @param changeFileValueRequest a [[ChangeFileValueRequestV1]] sent by the values route. + * @param projectADM the project in which the value is being updated. + * @return a [[ChangeFileValueResponseV1]] representing all the changed file values. + */ + def makeTaskFuture( + changeFileValueRequest: ChangeFileValueRequestV1, + projectADM: ProjectADM + ): Future[ChangeFileValueResponseV1] = { val fileValueContent: FileValueContentV2 = changeFileValueRequest.file.toFileValueContentV2 // get the Iris of the current file value(s) @@ -727,7 +754,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // check that the resource to be updated exists and it is a subclass of knora-base:Representation _ = if (getFileValuesResponse.results.bindings.isEmpty) throw NotFoundException( - s"Value ${changeFileValueRequest.resourceIri} not found (it may have been deleted) or it is not a knora-base:Representation") + s"Value ${changeFileValueRequest.resourceIri} not found (it may have been deleted) or it is not a knora-base:Representation" + ) // get the property Iris, file value Iris and qualities attached to the resource fileValues: Seq[CurrentFileValue] = getFileValuesResponse.results.bindings.map { row: VariableResultsRow => @@ -750,19 +778,20 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde featureFactoryConfig = changeFileValueRequest.featureFactoryConfig, userProfile = changeFileValueRequest.userProfile, apiRequestID = changeFileValueRequest.apiRequestID // re-use the same id - )) + ) + ) changedLocation = response.value match { case fileValueV1: FileValueV1 => valueUtilV1.fileValueV12LocationV1(fileValueV1) case other => throw AssertionException( - s"Expected Sipi to change a file value, but it changed one of these: ${other.valueTypeIri}") + s"Expected Sipi to change a file value, but it changed one of these: ${other.valueTypeIri}" + ) } - } yield - ChangeFileValueResponseV1( - locations = Vector(changedLocation), - projectADM = projectADM - ) + } yield ChangeFileValueResponseV1( + locations = Vector(changedLocation), + projectADM = projectADM + ) ResourceUtilV2.doSipiPostUpdate( updateFuture = triplestoreUpdateFuture, @@ -805,24 +834,26 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Adds a new version of an existing value. - * - * @param changeValueRequest the request message. - * @return an [[ChangeValueResponseV1]] if the update was successful. - */ + * Adds a new version of an existing value. + * + * @param changeValueRequest the request message. + * @return an [[ChangeValueResponseV1]] if the update was successful. + */ private def changeValueV1(changeValueRequest: ChangeValueRequestV1): Future[ChangeValueResponseV1] = { /** - * Creates a [[Future]] that does pre-update checks and performs the update. This function will be - * called by [[IriLocker]] once it has acquired an update lock on the resource. - * - * @param userIri the IRI of the user making the request. - * @param findResourceWithValueResult a [[FindResourceWithValueResult]] indicating which resource contains the value - * to be updated. - * @return a [[Future]] that does pre-update checks and performs the update. - */ - def makeTaskFuture(userIri: IRI, - findResourceWithValueResult: FindResourceWithValueResult): Future[ChangeValueResponseV1] = { + * Creates a [[Future]] that does pre-update checks and performs the update. This function will be + * called by [[IriLocker]] once it has acquired an update lock on the resource. + * + * @param userIri the IRI of the user making the request. + * @param findResourceWithValueResult a [[FindResourceWithValueResult]] indicating which resource contains the value + * to be updated. + * @return a [[Future]] that does pre-update checks and performs the update. + */ + def makeTaskFuture( + userIri: IRI, + findResourceWithValueResult: FindResourceWithValueResult + ): Future[ChangeValueResponseV1] = { // If we're updating a link, findResourceWithValueResult will contain the IRI of the property that points to the // knora-base:LinkValue, but we'll need the IRI of the corresponding link property. val propertyIri = changeValueRequest.value match { @@ -861,13 +892,18 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } currentValueQueryResult = maybeCurrentValueQueryResult.getOrElse( - throw NotFoundException(s"Value ${changeValueRequest.valueIri} not found (it may have been deleted)")) + throw NotFoundException(s"Value ${changeValueRequest.valueIri} not found (it may have been deleted)") + ) - _ = if (!PermissionUtilADM.impliesPermissionCodeV1( - userHasPermissionCode = Some(currentValueQueryResult.permissionCode), - userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = Some(currentValueQueryResult.permissionCode), + userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to add a new version to value ${changeValueRequest.valueIri}") + s"User $userIri does not have permission to add a new version to value ${changeValueRequest.valueIri}" + ) } // Check that the submitted value has the correct type for the property. @@ -897,7 +933,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Check that the current value and the submitted value have the same type. _ = if (currentValueQueryResult.value.valueTypeIri != changeValueRequest.value.valueTypeIri) { throw BadRequestException( - s"Value ${changeValueRequest.valueIri} has type ${currentValueQueryResult.value.valueTypeIri}, but the submitted new version has type ${changeValueRequest.value.valueTypeIri}") + s"Value ${changeValueRequest.valueIri} has type ${currentValueQueryResult.value.valueTypeIri}, but the submitted new version has type ${changeValueRequest.value.valueTypeIri}" + ) } // Make sure the new version would not be redundant, given the current version. @@ -933,7 +970,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case None => // This shouldn't happen unless someone just changed the ontology. throw NotFoundException( - s"No information found about property $propertyIri for resource ${findResourceWithValueResult.resourceIri}") + s"No information found about property $propertyIri for resource ${findResourceWithValueResult.resourceIri}" + ) } } @@ -942,11 +980,14 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde resourceClassIri: IRI = resourceFullResponse.resinfo .getOrElse( throw InconsistentRepositoryDataException( - s"Did not find resource info for resource ${findResourceWithValueResult.resourceIri}")) + s"Did not find resource info for resource ${findResourceWithValueResult.resourceIri}" + ) + ) .restype_id _ = log.debug( - s"changeValueV1 - DefaultObjectAccessPermissionsStringForPropertyGetV1 - projectIri ${findResourceWithValueResult.projectIri}, propertyIri: ${findResourceWithValueResult.propertyIri}, permissions: ${changeValueRequest.userProfile.permissions} ") + s"changeValueV1 - DefaultObjectAccessPermissionsStringForPropertyGetV1 - projectIri ${findResourceWithValueResult.projectIri}, propertyIri: ${findResourceWithValueResult.propertyIri}, permissions: ${changeValueRequest.userProfile.permissions} " + ) defaultObjectAccessPermissions <- { responderManager ? DefaultObjectAccessPermissionsStringForPropertyGetADM( projectIri = findResourceWithValueResult.projectIri, @@ -978,11 +1019,15 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // We're updating a link. This means deleting an existing link and creating a new one, so // check that the user has permission to modify the resource. val resourcePermissionCode = resourceFullResponse.resdata.flatMap(resdata => resdata.rights) - if (!PermissionUtilADM.impliesPermissionCodeV1(userHasPermissionCode = resourcePermissionCode, - userNeedsPermission = - OntologyConstants.KnoraBase.ModifyPermission)) { + if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = resourcePermissionCode, + userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to modify resource ${findResourceWithValueResult.resourceIri}") + s"User $userIri does not have permission to modify resource ${findResourceWithValueResult.resourceIri}" + ) } // We'll need to create a new LinkValue. @@ -1007,13 +1052,13 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Give the new version the same permissions as the previous version. - val valuePermissions = currentValueQueryResult.permissionRelevantAssertions - .find { - case (p, o) => p == OntologyConstants.KnoraBase.HasPermissions - } + val valuePermissions = currentValueQueryResult.permissionRelevantAssertions.find { case (p, o) => + p == OntologyConstants.KnoraBase.HasPermissions + } .map(_._2) .getOrElse( - throw InconsistentRepositoryDataException(s"Value ${changeValueRequest.valueIri} has no permissions")) + throw InconsistentRepositoryDataException(s"Value ${changeValueRequest.valueIri} has no permissions") + ) changeOrdinaryValueV1AfterChecks( projectIri = currentValueQueryResult.projectIri, @@ -1058,14 +1103,16 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde private def changeCommentV1(changeCommentRequest: ChangeCommentRequestV1): Future[ChangeValueResponseV1] = { /** - * Creates a [[Future]] that does pre-update checks and performs the update. This function will be - * called by [[IriLocker]] once it has acquired an update lock on the resource. - * - * @param userIri the IRI of the user making the request. - * @return a [[Future]] that does pre-update checks and performs the update. - */ - def makeTaskFuture(userIri: IRI, - findResourceWithValueResult: FindResourceWithValueResult): Future[ChangeValueResponseV1] = { + * Creates a [[Future]] that does pre-update checks and performs the update. This function will be + * called by [[IriLocker]] once it has acquired an update lock on the resource. + * + * @param userIri the IRI of the user making the request. + * @return a [[Future]] that does pre-update checks and performs the update. + */ + def makeTaskFuture( + userIri: IRI, + findResourceWithValueResult: FindResourceWithValueResult + ): Future[ChangeValueResponseV1] = for { // Ensure that the user has permission to modify the value. maybeCurrentValueQueryResult: Option[ValueQueryResult] <- findValue( @@ -1075,13 +1122,18 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde ) currentValueQueryResult = maybeCurrentValueQueryResult.getOrElse( - throw NotFoundException(s"Value ${changeCommentRequest.valueIri} not found (it may have been deleted)")) + throw NotFoundException(s"Value ${changeCommentRequest.valueIri} not found (it may have been deleted)") + ) - _ = if (!PermissionUtilADM.impliesPermissionCodeV1( - userHasPermissionCode = Some(currentValueQueryResult.permissionCode), - userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = Some(currentValueQueryResult.permissionCode), + userNeedsPermission = OntologyConstants.KnoraBase.ModifyPermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to add a new version to value ${changeCommentRequest.valueIri}") + s"User $userIri does not have permission to add a new version to value ${changeCommentRequest.valueIri}" + ) } // currentValueQueryResult.comment is an Option[String] @@ -1135,14 +1187,12 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde featureFactoryConfig = changeCommentRequest.featureFactoryConfig, userProfile = changeCommentRequest.userProfile ) - } yield - ChangeValueResponseV1( - value = verifyUpdateResult.value, - comment = verifyUpdateResult.comment, - id = newValueIri, - rights = verifyUpdateResult.permissionCode - ) - } + } yield ChangeValueResponseV1( + value = verifyUpdateResult.value, + comment = verifyUpdateResult.comment, + id = newValueIri, + rights = verifyUpdateResult.permissionCode + ) for { // Don't allow anonymous users to update values. @@ -1168,24 +1218,26 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Marks a value as deleted. - * - * @param deleteValueRequest the request message. - * @return a [[DeleteValueResponseV1]]. - */ + * Marks a value as deleted. + * + * @param deleteValueRequest the request message. + * @return a [[DeleteValueResponseV1]]. + */ private def deleteValueV1(deleteValueRequest: DeleteValueRequestV1): Future[DeleteValueResponseV1] = { /** - * Creates a [[Future]] that does pre-update checks and performs the update. This function will be - * called by [[IriLocker]] once it has acquired an update lock on the resource. - * - * @param userIri the IRI of the user making the request. - * @param findResourceWithValueResult a [[FindResourceWithValueResult]] indicating which resource contains the value - * to be updated. - * @return a [[Future]] that does pre-update checks and performs the update. - */ - def makeTaskFuture(userIri: IRI, - findResourceWithValueResult: FindResourceWithValueResult): Future[DeleteValueResponseV1] = + * Creates a [[Future]] that does pre-update checks and performs the update. This function will be + * called by [[IriLocker]] once it has acquired an update lock on the resource. + * + * @param userIri the IRI of the user making the request. + * @param findResourceWithValueResult a [[FindResourceWithValueResult]] indicating which resource contains the value + * to be updated. + * @return a [[Future]] that does pre-update checks and performs the update. + */ + def makeTaskFuture( + userIri: IRI, + findResourceWithValueResult: FindResourceWithValueResult + ): Future[DeleteValueResponseV1] = for { // Ensure that the user has permission to mark the value as deleted. maybeCurrentValueQueryResult <- findValue( @@ -1195,13 +1247,18 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde ) currentValueQueryResult = maybeCurrentValueQueryResult.getOrElse( - throw NotFoundException(s"Value ${deleteValueRequest.valueIri} not found (it may have been deleted)")) + throw NotFoundException(s"Value ${deleteValueRequest.valueIri} not found (it may have been deleted)") + ) - _ = if (!PermissionUtilADM.impliesPermissionCodeV1( - userHasPermissionCode = Some(currentValueQueryResult.permissionCode), - userNeedsPermission = OntologyConstants.KnoraBase.DeletePermission)) { + _ = if ( + !PermissionUtilADM.impliesPermissionCodeV1( + userHasPermissionCode = Some(currentValueQueryResult.permissionCode), + userNeedsPermission = OntologyConstants.KnoraBase.DeletePermission + ) + ) { throw ForbiddenException( - s"User $userIri does not have permission to delete value ${deleteValueRequest.valueIri}") + s"User $userIri does not have permission to delete value ${deleteValueRequest.valueIri}" + ) } // Make a timestamp to indicate when the value was marked as deleted. @@ -1216,13 +1273,13 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Give the new version the same permissions as the previous version. - val valuePermissions: String = currentValueQueryResult.permissionRelevantAssertions - .find { - case (p, o) => p == OntologyConstants.KnoraBase.HasPermissions - } + val valuePermissions: String = currentValueQueryResult.permissionRelevantAssertions.find { case (p, o) => + p == OntologyConstants.KnoraBase.HasPermissions + } .map(_._2) .getOrElse( - throw InconsistentRepositoryDataException(s"Value ${deleteValueRequest.valueIri} has no permissions")) + throw InconsistentRepositoryDataException(s"Value ${deleteValueRequest.valueIri} has no permissions") + ) val linkPropertyIri = stringFormatter.linkValuePropertyIriToLinkPropertyIri(findResourceWithValueResult.propertyIri) @@ -1336,12 +1393,17 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde sparqlSelectResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] rows = sparqlSelectResponse.results.bindings - _ = if (rows.isEmpty || !stringFormatter.optionStringToBoolean( - rows.head.rowMap.get("isDeleted"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}"))) { + _ = if ( + rows.isEmpty || !stringFormatter.optionStringToBoolean( + rows.head.rowMap.get("isDeleted"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}" + ) + ) + ) { throw UpdateNotPerformedException( - s"The request to mark value ${deleteValueRequest.valueIri} (or a new version of that value) as deleted did not succeed. Please report this as a possible bug.") + s"The request to mark value ${deleteValueRequest.valueIri} (or a new version of that value) as deleted did not succeed. Please report this as a possible bug." + ) } } yield DeleteValueResponseV1(id = deletedValueIri) @@ -1368,28 +1430,31 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Gets the version history of a value. - * - * @param versionHistoryRequest a [[ValueVersionHistoryGetRequestV1]]. - * @return a [[ValueVersionHistoryGetResponseV1]]. - */ + * Gets the version history of a value. + * + * @param versionHistoryRequest a [[ValueVersionHistoryGetRequestV1]]. + * @return a [[ValueVersionHistoryGetResponseV1]]. + */ private def getValueVersionHistoryResponseV1( - versionHistoryRequest: ValueVersionHistoryGetRequestV1): Future[ValueVersionHistoryGetResponseV1] = { + versionHistoryRequest: ValueVersionHistoryGetRequestV1 + ): Future[ValueVersionHistoryGetResponseV1] = { val userProfileV1 = versionHistoryRequest.userProfile.asUserProfileV1 /** - * Recursively converts a [[Map]] of value version SPARQL query result rows into a [[Vector]] representing the value's version history, - * ordered from most recent to oldest. - * - * @param versionMap a [[Map]] of value version IRIs to the contents of SPARQL query result rows. - * @param startAtVersion the IRI of the version to start at. - * @param versionRowsVector a [[Vector]] containing the results of the previous recursive call, or an empty vector if this is the first call. - * @return a [[Vector]] in which the elements are SPARQL query result rows representing versions, ordered from most recent to oldest. - */ + * Recursively converts a [[Map]] of value version SPARQL query result rows into a [[Vector]] representing the value's version history, + * ordered from most recent to oldest. + * + * @param versionMap a [[Map]] of value version IRIs to the contents of SPARQL query result rows. + * @param startAtVersion the IRI of the version to start at. + * @param versionRowsVector a [[Vector]] containing the results of the previous recursive call, or an empty vector if this is the first call. + * @return a [[Vector]] in which the elements are SPARQL query result rows representing versions, ordered from most recent to oldest. + */ @tailrec - def versionMap2Vector(versionMap: Map[IRI, Map[String, String]], - startAtVersion: IRI, - versionRowsVector: Vector[Map[String, String]]): Vector[Map[String, String]] = { + def versionMap2Vector( + versionMap: Map[IRI, Map[String, String]], + startAtVersion: IRI, + versionRowsVector: Vector[Map[String, String]] + ): Vector[Map[String, String]] = { val startValue = versionMap(startAtVersion) val newVersionVector = versionRowsVector :+ startValue @@ -1416,7 +1481,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde _ = if (rows.isEmpty) { throw NotFoundException( - s"Value ${versionHistoryRequest.currentValueIri} is not the most recent version of an object of property ${versionHistoryRequest.propertyIri} for resource ${versionHistoryRequest.resourceIri}") + s"Value ${versionHistoryRequest.currentValueIri} is not the most recent version of an object of property ${versionHistoryRequest.propertyIri} for resource ${versionHistoryRequest.resourceIri}" + ) } // Convert the result rows to a map of value IRIs to result rows. @@ -1426,9 +1492,11 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde }.toMap // Order the result rows from most recent to oldest. - versionRowsVector = versionMap2Vector(versionMap, - versionHistoryRequest.currentValueIri, - Vector.empty[Map[String, String]]) + versionRowsVector = versionMap2Vector( + versionMap, + versionHistoryRequest.currentValueIri, + Vector.empty[Map[String, String]] + ) // Filter out the versions that the user doesn't have permission to see. filteredVersionRowsVector = versionRowsVector.filter { rowMap => @@ -1439,10 +1507,14 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Permission-checking on LinkValues is special, because they can be system-created rather than user-created. val valuePermissionCode = - if (stringFormatter.optionStringToBoolean( - rowMap.get("isLinkValue"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isLinkValue: ${rowMap.get("isLinkValue")}"))) { + if ( + stringFormatter.optionStringToBoolean( + rowMap.get("isLinkValue"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isLinkValue: ${rowMap.get("isLinkValue")}" + ) + ) + ) { // It's a LinkValue. PermissionUtilADM.getUserPermissionV1( entityIri = valueIri, @@ -1483,23 +1555,25 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Looks for a direct link connecting two resources, finds the corresponding `knora-base:LinkValue`, and returns - * a [[ValueGetResponseV1]] containing a [[LinkValueV1]] describing the `LinkValue`. Throws [[NotFoundException]] - * if no such `LinkValue` is found. - * - * @param subjectIri the IRI of the resource that is the source of the link. - * @param predicateIri the IRI of the property that links the two resources. - * @param objectIri the IRI of the resource that is the target of the link. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ValueGetResponseV1]] containing a [[LinkValueV1]]. - */ + * Looks for a direct link connecting two resources, finds the corresponding `knora-base:LinkValue`, and returns + * a [[ValueGetResponseV1]] containing a [[LinkValueV1]] describing the `LinkValue`. Throws [[NotFoundException]] + * if no such `LinkValue` is found. + * + * @param subjectIri the IRI of the resource that is the source of the link. + * @param predicateIri the IRI of the property that links the two resources. + * @param objectIri the IRI of the resource that is the target of the link. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ValueGetResponseV1]] containing a [[LinkValueV1]]. + */ @throws(classOf[NotFoundException]) - private def getLinkValue(subjectIri: IRI, - predicateIri: IRI, - objectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ValueGetResponseV1] = { + private def getLinkValue( + subjectIri: IRI, + predicateIri: IRI, + objectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ValueGetResponseV1] = for { maybeValueQueryResult <- findLinkValueByLinkTriple( subjectIri = subjectIri, @@ -1522,110 +1596,113 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case Some(up) => up case None => throw NotFoundException(s"User ${valueQueryResult.creatorIri} not found") } - } yield - ValueGetResponseV1( - valuetype = valueQueryResult.value.valueTypeIri, - rights = valueQueryResult.permissionCode, - value = valueQueryResult.value, - valuecreator = valueCreatorProfile.userData.email.get, - valuecreatorname = valueCreatorProfile.userData.fullname.get, - valuecreationdate = valueQueryResult.creationDate, - comment = valueQueryResult.comment - ) + } yield ValueGetResponseV1( + valuetype = valueQueryResult.value.valueTypeIri, + rights = valueQueryResult.permissionCode, + value = valueQueryResult.value, + valuecreator = valueCreatorProfile.userData.email.get, + valuecreatorname = valueCreatorProfile.userData.fullname.get, + valuecreationdate = valueQueryResult.creationDate, + comment = valueQueryResult.comment + ) case None => throw NotFoundException( - s"No knora-base:LinkValue found describing a link from resource $subjectIri with predicate $predicateIri to resource $objectIri (it may have been deleted)") + s"No knora-base:LinkValue found describing a link from resource $subjectIri with predicate $predicateIri to resource $objectIri (it may have been deleted)" + ) } } yield linkValueResponse - } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Helper methods and types. /** - * Represents the result of querying a value. - */ + * Represents the result of querying a value. + */ trait ValueQueryResult { /** - * The value that was found. - */ + * The value that was found. + */ def value: ApiValueV1 /** - * The IRI Of the user that created the value. - */ + * The IRI Of the user that created the value. + */ def creatorIri: IRI /** - * The date when the value was created, represented as a string. - */ + * The date when the value was created, represented as a string. + */ def creationDate: String /** - * The IRI of the project that the value belongs to. - */ + * The IRI of the project that the value belongs to. + */ def projectIri: IRI /** - * An optional comment describing the value. - */ + * An optional comment describing the value. + */ def comment: Option[String] /** - * A list of the permission-relevant assertions declared on the value. - */ + * A list of the permission-relevant assertions declared on the value. + */ def permissionRelevantAssertions: Seq[(IRI, IRI)] /** - * An integer permission code representing the user's permissions on the value. - */ + * An integer permission code representing the user's permissions on the value. + */ def permissionCode: Int } /** - * Represents basic information resulting from querying a value. This is sufficient if the value is an ordinary - * value (not a link). - */ - case class BasicValueQueryResult(value: ApiValueV1, - creatorIri: IRI, - creationDate: String, - projectIri: IRI, - comment: Option[String], - permissionRelevantAssertions: Seq[(IRI, IRI)], - permissionCode: Int) - extends ValueQueryResult + * Represents basic information resulting from querying a value. This is sufficient if the value is an ordinary + * value (not a link). + */ + case class BasicValueQueryResult( + value: ApiValueV1, + creatorIri: IRI, + creationDate: String, + projectIri: IRI, + comment: Option[String], + permissionRelevantAssertions: Seq[(IRI, IRI)], + permissionCode: Int + ) extends ValueQueryResult /** - * Represents the result of querying a link. - * - * @param directLinkExists `true` if a direct link exists between the two resources. - * @param targetResourceClass if a direct link exists, contains the OWL class of the target resource. - */ - case class LinkValueQueryResult(value: LinkValueV1, - linkValueIri: IRI, - creatorIri: IRI, - creationDate: String, - projectIri: IRI, - comment: Option[String], - directLinkExists: Boolean, - targetResourceClass: Option[IRI], - permissionRelevantAssertions: Seq[(IRI, IRI)], - permissionCode: Int) - extends ValueQueryResult + * Represents the result of querying a link. + * + * @param directLinkExists `true` if a direct link exists between the two resources. + * @param targetResourceClass if a direct link exists, contains the OWL class of the target resource. + */ + case class LinkValueQueryResult( + value: LinkValueV1, + linkValueIri: IRI, + creatorIri: IRI, + creationDate: String, + projectIri: IRI, + comment: Option[String], + directLinkExists: Boolean, + targetResourceClass: Option[IRI], + permissionRelevantAssertions: Seq[(IRI, IRI)], + permissionCode: Int + ) extends ValueQueryResult /** - * Queries a `knora-base:Value` and returns a [[ValueQueryResult]] describing it. - * - * @param valueIri the IRI of the value to be queried. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ValueQueryResult]], or `None` if the value is not found. - */ - private def findValue(valueIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Option[ValueQueryResult]] = { + * Queries a `knora-base:Value` and returns a [[ValueQueryResult]] describing it. + * + * @param valueIri the IRI of the value to be queried. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ValueQueryResult]], or `None` if the value is not found. + */ + private def findValue( + valueIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Option[ValueQueryResult]] = for { sparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1633,7 +1710,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde triplestore = settings.triplestoreType, valueIri = valueIri ) - .toString()) + .toString() + ) response <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] rows: Seq[VariableResultsRow] = response.results.bindings @@ -1656,15 +1734,14 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case None => () } } yield maybeValueQueryResult - } /** - * Checks that the user has permission to see the source and target resources of a link value. - * - * @param linkValueIri the IRI of the link value. - * @param userProfile the profile of the user making the request. - * @return () if the user has the required permission, or an exception otherwise. - */ + * Checks that the user has permission to see the source and target resources of a link value. + * + * @param linkValueIri the IRI of the link value. + * @param userProfile the profile of the user making the request. + * @return () if the user has the required permission, or an exception otherwise. + */ private def checkLinkValueSubjectAndObjectPermissions(linkValueIri: IRI, userProfile: UserADM): Future[Unit] = { val userProfileV1 = userProfile.asUserProfileV1 @@ -1675,7 +1752,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde triplestore = settings.triplestoreType, linkValueIri = linkValueIri ) - .toString()) + .toString() + ) response <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] rows = response.results.bindings @@ -1709,23 +1787,25 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde } /** - * Looks for `knora-base:LinkValue` given its IRI, and returns a [[ValueGetResponseV1]] containing a - * [[LinkValueV1]] describing the `LinkValue`, or `None` if no such `LinkValue` is found. - * - * @param subjectIri the IRI of the resource that is the source of the link. - * @param predicateIri the IRI of the property that links the two resources. - * @param objectIri if provided, the IRI of the target resource. - * @param linkValueIri the IRI of the `LinkValue`. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return an optional [[ValueGetResponseV1]] containing a [[LinkValueV1]]. - */ - private def findLinkValueByIri(subjectIri: IRI, - predicateIri: IRI, - objectIri: Option[IRI], - linkValueIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Option[LinkValueQueryResult]] = { + * Looks for `knora-base:LinkValue` given its IRI, and returns a [[ValueGetResponseV1]] containing a + * [[LinkValueV1]] describing the `LinkValue`, or `None` if no such `LinkValue` is found. + * + * @param subjectIri the IRI of the resource that is the source of the link. + * @param predicateIri the IRI of the property that links the two resources. + * @param objectIri if provided, the IRI of the target resource. + * @param linkValueIri the IRI of the `LinkValue`. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return an optional [[ValueGetResponseV1]] containing a [[LinkValueV1]]. + */ + private def findLinkValueByIri( + subjectIri: IRI, + predicateIri: IRI, + objectIri: Option[IRI], + linkValueIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Option[LinkValueQueryResult]] = for { sparqlQuery <- Future { org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1753,25 +1833,26 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde checkLinkValueSubjectAndObjectPermissions(linkValueIri, userProfile) } } yield maybeLinkValueQueryResult - } /** - * Looks for `knora-base:LinkValue` describing a link between two resources, and returns - * a [[ValueGetResponseV1]] containing a [[LinkValueV1]] describing the `LinkValue`, or `None` if no such - * `LinkValue` is found. - * - * @param subjectIri the IRI of the resource that is the source of the link. - * @param predicateIri the IRI of the property that links the two resources. - * @param objectIri the IRI of the target resource. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return an optional [[ValueGetResponseV1]] containing a [[LinkValueV1]]. - */ - private def findLinkValueByLinkTriple(subjectIri: IRI, - predicateIri: IRI, - objectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Option[LinkValueQueryResult]] = { + * Looks for `knora-base:LinkValue` describing a link between two resources, and returns + * a [[ValueGetResponseV1]] containing a [[LinkValueV1]] describing the `LinkValue`, or `None` if no such + * `LinkValue` is found. + * + * @param subjectIri the IRI of the resource that is the source of the link. + * @param predicateIri the IRI of the property that links the two resources. + * @param objectIri the IRI of the target resource. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return an optional [[ValueGetResponseV1]] containing a [[LinkValueV1]]. + */ + private def findLinkValueByLinkTriple( + subjectIri: IRI, + predicateIri: IRI, + objectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Option[LinkValueQueryResult]] = for { sparqlQuery <- Future { org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1800,24 +1881,25 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case _ => () } } yield maybeLinkValueQueryResult - } /** - * Converts SPARQL query results into a [[ValueQueryResult]]. Checks that the user has permission to view the value object. - * If the value is a link value, the caller of this method is responsible for ensuring that the user has permission to - * view the source and target resources. - * - * @param valueIri the IRI of the value that was queried. - * @param rows the query result rows. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ValueQueryResult]]. - */ + * Converts SPARQL query results into a [[ValueQueryResult]]. Checks that the user has permission to view the value object. + * If the value is a link value, the caller of this method is responsible for ensuring that the user has permission to + * view the source and target resources. + * + * @param valueIri the IRI of the value that was queried. + * @param rows the query result rows. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ValueQueryResult]]. + */ @throws(classOf[ForbiddenException]) - private def sparqlQueryResults2ValueQueryResult(valueIri: IRI, - rows: Seq[VariableResultsRow], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Option[BasicValueQueryResult]] = { + private def sparqlQueryResults2ValueQueryResult( + valueIri: IRI, + rows: Seq[VariableResultsRow], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Option[BasicValueQueryResult]] = { val userProfileV1 = userProfile.asUserProfileV1 if (rows.nonEmpty) { @@ -1827,7 +1909,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde for { projectShortcode: String <- Future( valueIri.toSmartIri.getProjectCode - .getOrElse(throw InconsistentRepositoryDataException(s"Invalid value IRI: $valueIri"))) + .getOrElse(throw InconsistentRepositoryDataException(s"Invalid value IRI: $valueIri")) + ) value <- valueUtilV1.makeValueV1( valueProps = valueProps, @@ -1849,12 +1932,15 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde projectIri = getValuePredicateObject(predicateIri = OntologyConstants.KnoraBase.AttachedToProject, rows = rows) .getOrElse( throw InconsistentRepositoryDataException( - s"The resource containing value $valueIri has no knora-base:attachedToProject")) + s"The resource containing value $valueIri has no knora-base:attachedToProject" + ) + ) // Get the value's creation date. creationDate = getValuePredicateObject( predicateIri = OntologyConstants.KnoraBase.ValueCreationDate, - rows = rows).getOrElse(throw InconsistentRepositoryDataException(s"Value $valueIri has no valueCreationDate")) + rows = rows + ).getOrElse(throw InconsistentRepositoryDataException(s"Value $valueIri has no valueCreationDate")) // Get the optional comment on the value. comment = getValuePredicateObject(predicateIri = OntologyConstants.KnoraBase.ValueHasComment, rows = rows) @@ -1893,34 +1979,35 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde throw ForbiddenException(s"User ${userProfile.id} does not have permission to see value $valueIri") } - } yield - Some( - BasicValueQueryResult( - value = value, - creatorIri = creatorIri, - creationDate = creationDate, - comment = comment, - projectIri = projectIri, - permissionRelevantAssertions = assertions, - permissionCode = permissionCode - ) + } yield Some( + BasicValueQueryResult( + value = value, + creatorIri = creatorIri, + creationDate = creationDate, + comment = comment, + projectIri = projectIri, + permissionRelevantAssertions = assertions, + permissionCode = permissionCode ) + ) } else { Future(None) } } /** - * Converts SPARQL query results about a `knora-base:LinkValue` into a [[LinkValueQueryResult]]. - * - * @param rows SPARQL query results about a `knora-base:LinkValue`. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[LinkValueQueryResult]]. - */ - private def sparqlQueryResults2LinkValueQueryResult(rows: Seq[VariableResultsRow], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Option[LinkValueQueryResult]] = { + * Converts SPARQL query results about a `knora-base:LinkValue` into a [[LinkValueQueryResult]]. + * + * @param rows SPARQL query results about a `knora-base:LinkValue`. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[LinkValueQueryResult]]. + */ + private def sparqlQueryResults2LinkValueQueryResult( + rows: Seq[VariableResultsRow], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Option[LinkValueQueryResult]] = { val userProfileV1 = userProfile.asUserProfileV1 if (rows.nonEmpty) { @@ -1933,7 +2020,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde for { projectShortcode: String <- Future( linkValueIri.toSmartIri.getProjectCode - .getOrElse(throw InconsistentRepositoryDataException(s"Invalid value IRI: $linkValueIri"))) + .getOrElse(throw InconsistentRepositoryDataException(s"Invalid value IRI: $linkValueIri")) + ) linkValueMaybe <- valueUtilV1.makeValueV1( valueProps = valueProps, @@ -1947,7 +2035,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case linkValue: LinkValueV1 => linkValue case other => throw InconsistentRepositoryDataException( - s"Expected value $linkValueIri to be of type ${OntologyConstants.KnoraBase.LinkValue}, but it was read with type ${other.valueTypeIri}") + s"Expected value $linkValueIri to be of type ${OntologyConstants.KnoraBase.LinkValue}, but it was read with type ${other.valueTypeIri}" + ) } // Get the IRI of the value's owner. @@ -1958,11 +2047,15 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde projectIri = getValuePredicateObject(predicateIri = OntologyConstants.KnoraBase.AttachedToProject, rows = rows) .getOrElse( throw InconsistentRepositoryDataException( - s"The resource containing value $linkValueIri has no knora-base:attachedToProject")) + s"The resource containing value $linkValueIri has no knora-base:attachedToProject" + ) + ) // Get the value's creation date. - creationDate = getValuePredicateObject(predicateIri = OntologyConstants.KnoraBase.ValueCreationDate, - rows = rows) + creationDate = getValuePredicateObject( + predicateIri = OntologyConstants.KnoraBase.ValueCreationDate, + rows = rows + ) .getOrElse(throw InconsistentRepositoryDataException(s"Value $linkValueIri has no valueCreationDate")) // Get the optional comment on the value. @@ -1986,42 +2079,43 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde directLinkExists = firstRowMap.get("directLinkExists").exists(_.toBoolean) targetResourceClass = firstRowMap.get("targetResourceClass") - } yield - Some( - LinkValueQueryResult( - value = linkValueV1, - linkValueIri = linkValueIri, - creatorIri = creatorIri, - creationDate = creationDate, - comment = comment, - projectIri = projectIri, - directLinkExists = directLinkExists, - targetResourceClass = targetResourceClass, - permissionRelevantAssertions = permissionRelevantAssertions, - permissionCode = permissionCode - ) + } yield Some( + LinkValueQueryResult( + value = linkValueV1, + linkValueIri = linkValueIri, + creatorIri = creatorIri, + creationDate = creationDate, + comment = comment, + projectIri = projectIri, + directLinkExists = directLinkExists, + targetResourceClass = targetResourceClass, + permissionRelevantAssertions = permissionRelevantAssertions, + permissionCode = permissionCode ) + ) } else { Future(None) } } /** - * Verifies that a value was created. - * - * @param resourceIri the IRI of the resource in which the value should have been created. - * @param propertyIri the IRI of the property that should point from the resource to the value. - * @param unverifiedValue the value that should have been created. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[CreateValueResponseV1]], or a failed [[Future]] if the value could not be found in - * the resource's version history. - */ - private def verifyValueCreation(resourceIri: IRI, - propertyIri: IRI, - unverifiedValue: UnverifiedValueV1, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[CreateValueResponseV1] = { + * Verifies that a value was created. + * + * @param resourceIri the IRI of the resource in which the value should have been created. + * @param propertyIri the IRI of the property that should point from the resource to the value. + * @param unverifiedValue the value that should have been created. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[CreateValueResponseV1]], or a failed [[Future]] if the value could not be found in + * the resource's version history. + */ + private def verifyValueCreation( + resourceIri: IRI, + propertyIri: IRI, + unverifiedValue: UnverifiedValueV1, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[CreateValueResponseV1] = unverifiedValue.value match { case linkUpdateV1: LinkUpdateV1 => for { @@ -2038,13 +2132,12 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde targetResourceIri = linkUpdateV1.targetResourceIri, valueResourceClass = linkValueQueryResult.targetResourceClass ) - } yield - CreateValueResponseV1( - value = apiResponseValue, - comment = linkValueQueryResult.comment, - id = unverifiedValue.newValueIri, - rights = linkValueQueryResult.permissionCode - ) + } yield CreateValueResponseV1( + value = apiResponseValue, + comment = linkValueQueryResult.comment, + id = unverifiedValue.newValueIri, + rights = linkValueQueryResult.permissionCode + ) case ordinaryUpdateValueV1 => for { @@ -2056,34 +2149,34 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde userProfile = userProfile ) - } yield - CreateValueResponseV1( - value = verifyUpdateResult.value, - comment = verifyUpdateResult.comment, - id = unverifiedValue.newValueIri, - rights = verifyUpdateResult.permissionCode - ) + } yield CreateValueResponseV1( + value = verifyUpdateResult.value, + comment = verifyUpdateResult.comment, + id = unverifiedValue.newValueIri, + rights = verifyUpdateResult.permissionCode + ) } - } /** - * Given the IRI of a value that should have been created, looks for the value in the resource's version history, - * and returns details about it. If the value is not found, throws [[UpdateNotPerformedException]]. - * - * @param resourceIri the IRI of the resource that may have the value. - * @param propertyIri the IRI of the property that may have have the value. - * @param searchValueIri the IRI of the value. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ValueQueryResult]]. - */ + * Given the IRI of a value that should have been created, looks for the value in the resource's version history, + * and returns details about it. If the value is not found, throws [[UpdateNotPerformedException]]. + * + * @param resourceIri the IRI of the resource that may have the value. + * @param propertyIri the IRI of the property that may have have the value. + * @param searchValueIri the IRI of the value. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ValueQueryResult]]. + */ @throws(classOf[UpdateNotPerformedException]) @throws(classOf[ForbiddenException]) - private def verifyOrdinaryValueUpdate(resourceIri: IRI, - propertyIri: IRI, - searchValueIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ValueQueryResult] = { + private def verifyOrdinaryValueUpdate( + resourceIri: IRI, + propertyIri: IRI, + searchValueIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ValueQueryResult] = for { // Do a SPARQL query to look for the value in the resource's version history. sparqlQuery <- Future { @@ -2109,31 +2202,34 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde userProfile = userProfile ) - } yield - resultOption.getOrElse(throw UpdateNotPerformedException( - s"The update to value $searchValueIri for property $propertyIri in resource $resourceIri was not performed. Please report this as a possible bug.")) - } + } yield resultOption.getOrElse( + throw UpdateNotPerformedException( + s"The update to value $searchValueIri for property $propertyIri in resource $resourceIri was not performed. Please report this as a possible bug." + ) + ) /** - * Given information about a link that should have been created, verifies that the link exists, and returns - * details about it. If the link has not been created, throws [[UpdateNotPerformedException]]. - * - * @param linkSourceIri the IRI of the resource that should be the source of the link. - * @param linkPropertyIri the IRI of the link property. - * @param linkTargetIri the IRI of the resource that should be the target of the link. - * @param linkValueIri the IRI of the `knora-base:LinkValue` that should have been created. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[LinkValueQueryResult]]. - */ + * Given information about a link that should have been created, verifies that the link exists, and returns + * details about it. If the link has not been created, throws [[UpdateNotPerformedException]]. + * + * @param linkSourceIri the IRI of the resource that should be the source of the link. + * @param linkPropertyIri the IRI of the link property. + * @param linkTargetIri the IRI of the resource that should be the target of the link. + * @param linkValueIri the IRI of the `knora-base:LinkValue` that should have been created. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[LinkValueQueryResult]]. + */ @throws(classOf[UpdateNotPerformedException]) @throws(classOf[ForbiddenException]) - private def verifyLinkUpdate(linkSourceIri: IRI, - linkPropertyIri: IRI, - linkTargetIri: IRI, - linkValueIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[LinkValueQueryResult] = { + private def verifyLinkUpdate( + linkSourceIri: IRI, + linkPropertyIri: IRI, + linkTargetIri: IRI, + linkValueIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[LinkValueQueryResult] = for { maybeLinkValueQueryResult <- findLinkValueByIri( subjectIri = linkSourceIri, @@ -2154,38 +2250,37 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case None => throw UpdateNotPerformedException( - s"The update to link value $linkValueIri with source IRI $linkSourceIri, link property $linkPropertyIri, and target $linkTargetIri was not performed. Please report this as a possible bug.") + s"The update to link value $linkValueIri with source IRI $linkSourceIri, link property $linkPropertyIri, and target $linkTargetIri was not performed. Please report this as a possible bug." + ) } } yield result - } /** - * Finds the object of the specified predicate in SPARQL query results describing a value. - * - * @param predicateIri the IRI of the predicate. - * @param rows the SPARQL query results that describe the value. - * @return the predicate's object. - */ - private def getValuePredicateObject(predicateIri: IRI, rows: Seq[VariableResultsRow]): Option[IRI] = { + * Finds the object of the specified predicate in SPARQL query results describing a value. + * + * @param predicateIri the IRI of the predicate. + * @param rows the SPARQL query results that describe the value. + * @return the predicate's object. + */ + private def getValuePredicateObject(predicateIri: IRI, rows: Seq[VariableResultsRow]): Option[IRI] = rows.find(_.rowMap("objPred") == predicateIri).map(_.rowMap("objObj")) - } /** - * The result of calling the `findResourceWithValue` method. - * - * @param resourceIri the IRI of the resource containing the value. - * @param projectIri the IRI of the resource's project. - * @param propertyIri the IRI of the property pointing to the value. - */ + * The result of calling the `findResourceWithValue` method. + * + * @param resourceIri the IRI of the resource containing the value. + * @param projectIri the IRI of the resource's project. + * @param propertyIri the IRI of the property pointing to the value. + */ case class FindResourceWithValueResult(resourceIri: IRI, projectIri: IRI, propertyIri: IRI) /** - * Given a value IRI, finds the value's resource and property. - * - * @param valueIri the IRI of the value. - * @return a [[FindResourceWithValueResult]]. - */ - private def findResourceWithValue(valueIri: IRI): Future[FindResourceWithValueResult] = { + * Given a value IRI, finds the value's resource and property. + * + * @param valueIri the IRI of the value. + * @return a [[FindResourceWithValueResult]]. + */ + private def findResourceWithValue(valueIri: IRI): Future[FindResourceWithValueResult] = for { findResourceSparqlQuery <- Future( org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -2193,7 +2288,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde triplestore = settings.triplestoreType, searchValueIri = valueIri ) - .toString()) + .toString() + ) findResourceResponse <- (storeManager ? SparqlSelectRequest(findResourceSparqlQuery)).mapTo[SparqlSelectResult] _ = if (findResourceResponse.results.bindings.isEmpty) { @@ -2205,39 +2301,39 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde resourceIri = resultRowMap("resource") projectIri = resultRowMap("project") propertyIri = resultRowMap("property") - } yield - FindResourceWithValueResult( - resourceIri = resourceIri, - projectIri = projectIri, - propertyIri = propertyIri - ) - } + } yield FindResourceWithValueResult( + resourceIri = resourceIri, + projectIri = projectIri, + propertyIri = propertyIri + ) /** - * Creates a new value (either an ordinary value or a link), using an existing transaction, assuming that - * pre-update checks have already been done. - * - * @param dataNamedGraph the named graph in which the value is to be created. - * @param projectIri the IRI of the project in which to create the value. - * @param resourceIri the IRI of the resource in which to create the value. - * @param propertyIri the IRI of the property that will point from the resource to the value. - * @param value the value to create. - * @param valueCreator the IRI of the new value's owner. - * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return an [[UnverifiedValueV1]]. - */ - private def createValueV1AfterChecks(dataNamedGraph: IRI, - projectIri: IRI, - resourceIri: IRI, - propertyIri: IRI, - value: UpdateValueV1, - comment: Option[String], - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[UnverifiedValueV1] = { + * Creates a new value (either an ordinary value or a link), using an existing transaction, assuming that + * pre-update checks have already been done. + * + * @param dataNamedGraph the named graph in which the value is to be created. + * @param projectIri the IRI of the project in which to create the value. + * @param resourceIri the IRI of the resource in which to create the value. + * @param propertyIri the IRI of the property that will point from the resource to the value. + * @param value the value to create. + * @param valueCreator the IRI of the new value's owner. + * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return an [[UnverifiedValueV1]]. + */ + private def createValueV1AfterChecks( + dataNamedGraph: IRI, + projectIri: IRI, + resourceIri: IRI, + propertyIri: IRI, + value: UpdateValueV1, + comment: Option[String], + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[UnverifiedValueV1] = value match { case linkUpdateV1: LinkUpdateV1 => createLinkValueV1AfterChecks( @@ -2265,30 +2361,31 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde userProfile = userProfile ) } - } /** - * Creates a link, using an existing transaction, assuming that pre-update checks have already been done. - * - * @param dataNamedGraph the named graph in which the link is to be created. - * @param resourceIri the resource in which the link is to be created. - * @param propertyIri the link property. - * @param linkUpdateV1 a [[LinkUpdateV1]] specifying the target resource. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return an [[UnverifiedValueV1]]. - */ - private def createLinkValueV1AfterChecks(dataNamedGraph: IRI, - resourceIri: IRI, - propertyIri: IRI, - linkUpdateV1: LinkUpdateV1, - comment: Option[String], - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[UnverifiedValueV1] = { + * Creates a link, using an existing transaction, assuming that pre-update checks have already been done. + * + * @param dataNamedGraph the named graph in which the link is to be created. + * @param resourceIri the resource in which the link is to be created. + * @param propertyIri the link property. + * @param linkUpdateV1 a [[LinkUpdateV1]] specifying the target resource. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return an [[UnverifiedValueV1]]. + */ + private def createLinkValueV1AfterChecks( + dataNamedGraph: IRI, + resourceIri: IRI, + propertyIri: IRI, + linkUpdateV1: LinkUpdateV1, + comment: Option[String], + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[UnverifiedValueV1] = for { sparqlTemplateLinkUpdate <- incrementLinkValue( sourceResourceIri = resourceIri, @@ -2323,34 +2420,34 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Do the update. sparqlUpdateResponse <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV1( - newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, - value = linkUpdateV1 - ) - } + } yield UnverifiedValueV1( + newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, + value = linkUpdateV1 + ) /** - * Creates an ordinary value (i.e. not a link), using an existing transaction, assuming that pre-update checks have already been done. - * - * @param resourceIri the resource in which the value is to be created. - * @param propertyIri the property that should point to the value. - * @param value an [[UpdateValueV1]] describing the value. - * @param valueCreator the IRI of the new value's owner. - * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return an [[UnverifiedValueV1]]. - */ - private def createOrdinaryValueV1AfterChecks(dataNamedGraph: IRI, - resourceIri: IRI, - propertyIri: IRI, - value: UpdateValueV1, - comment: Option[String], - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[UnverifiedValueV1] = { + * Creates an ordinary value (i.e. not a link), using an existing transaction, assuming that pre-update checks have already been done. + * + * @param resourceIri the resource in which the value is to be created. + * @param propertyIri the property that should point to the value. + * @param value an [[UpdateValueV1]] describing the value. + * @param valueCreator the IRI of the new value's owner. + * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return an [[UnverifiedValueV1]]. + */ + private def createOrdinaryValueV1AfterChecks( + dataNamedGraph: IRI, + resourceIri: IRI, + propertyIri: IRI, + value: UpdateValueV1, + comment: Option[String], + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[UnverifiedValueV1] = { // Generate an IRI for the new value. val newValueIri = stringFormatter.makeRandomValueIri(resourceIri) val creationDate: Instant = Instant.now @@ -2408,40 +2505,41 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde // Do the update. sparqlUpdateResponse <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV1( - newValueIri = newValueIri, - value = value - ) + } yield UnverifiedValueV1( + newValueIri = newValueIri, + value = value + ) } /** - * Changes a link, assuming that pre-update checks have already been done. - * - * @param dataNamedGraph the IRI of the named graph containing the link. - * @param projectIri the IRI of the project containing the link. - * @param resourceIri the IRI of the resource containing the link. - * @param propertyIri the IRI of the link property. - * @param currentLinkValueV1 a [[LinkValueV1]] representing the `knora-base:LinkValue` for the existing link. - * @param linkUpdateV1 a [[LinkUpdateV1]] indicating the new target resource. - * @param comment an optional comment on the new link value. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[ChangeValueResponseV1]]. - */ - private def changeLinkValueV1AfterChecks(dataNamedGraph: IRI, - projectIri: IRI, - resourceIri: IRI, - propertyIri: IRI, - currentLinkValueV1: LinkValueV1, - linkUpdateV1: LinkUpdateV1, - comment: Option[String], - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ChangeValueResponseV1] = { + * Changes a link, assuming that pre-update checks have already been done. + * + * @param dataNamedGraph the IRI of the named graph containing the link. + * @param projectIri the IRI of the project containing the link. + * @param resourceIri the IRI of the resource containing the link. + * @param propertyIri the IRI of the link property. + * @param currentLinkValueV1 a [[LinkValueV1]] representing the `knora-base:LinkValue` for the existing link. + * @param linkUpdateV1 a [[LinkUpdateV1]] indicating the new target resource. + * @param comment an optional comment on the new link value. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[ChangeValueResponseV1]]. + */ + private def changeLinkValueV1AfterChecks( + dataNamedGraph: IRI, + projectIri: IRI, + resourceIri: IRI, + propertyIri: IRI, + currentLinkValueV1: LinkValueV1, + linkUpdateV1: LinkUpdateV1, + comment: Option[String], + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ChangeValueResponseV1] = for { // Delete the existing link and decrement its LinkValue's reference count. sparqlTemplateLinkUpdateForCurrentLink <- decrementLinkValue( @@ -2521,43 +2619,43 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde valueResourceClass = linkValueQueryResult.targetResourceClass ) - } yield - ChangeValueResponseV1( - value = apiResponseValue, - comment = linkValueQueryResult.comment, - id = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri, - rights = linkValueQueryResult.permissionCode - ) - } + } yield ChangeValueResponseV1( + value = apiResponseValue, + comment = linkValueQueryResult.comment, + id = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri, + rights = linkValueQueryResult.permissionCode + ) /** - * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done. - * - * @param projectIri the IRI of the project containing the value. - * @param resourceIri the IRI of the resource containing the value. - * @param propertyIri the IRI of the property that points to the value. - * @param currentValueIri the IRI of the existing value. - * @param currentValueV1 an [[ApiValueV1]] representing the existing value. - * @param newValueIri the IRI of the new value. - * @param updateValueV1 an [[UpdateValueV1]] representing the new value. - * @param comment an optional comment on the new value. - * @param valueCreator the IRI of the new value's owner. - * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. - * @param userProfile the profile of the user making the request. - * @return a [[ChangeValueResponseV1]]. - */ - private def changeOrdinaryValueV1AfterChecks(projectIri: IRI, - resourceIri: IRI, - propertyIri: IRI, - currentValueIri: IRI, - currentValueV1: ApiValueV1, - newValueIri: IRI, - updateValueV1: UpdateValueV1, - comment: Option[String], - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[ChangeValueResponseV1] = { + * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done. + * + * @param projectIri the IRI of the project containing the value. + * @param resourceIri the IRI of the resource containing the value. + * @param propertyIri the IRI of the property that points to the value. + * @param currentValueIri the IRI of the existing value. + * @param currentValueV1 an [[ApiValueV1]] representing the existing value. + * @param newValueIri the IRI of the new value. + * @param updateValueV1 an [[UpdateValueV1]] representing the new value. + * @param comment an optional comment on the new value. + * @param valueCreator the IRI of the new value's owner. + * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. + * @param userProfile the profile of the user making the request. + * @return a [[ChangeValueResponseV1]]. + */ + private def changeOrdinaryValueV1AfterChecks( + projectIri: IRI, + resourceIri: IRI, + propertyIri: IRI, + currentValueIri: IRI, + currentValueV1: ApiValueV1, + newValueIri: IRI, + updateValueV1: UpdateValueV1, + comment: Option[String], + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[ChangeValueResponseV1] = { for { // If we're adding a text value, update direct links and LinkValues for any resource references in Standoff. standoffLinkUpdates: Seq[SparqlTemplateLinkUpdate] <- (currentValueV1, updateValueV1) match { @@ -2674,45 +2772,46 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde featureFactoryConfig = featureFactoryConfig, userProfile = userProfile ) - } yield - ChangeValueResponseV1( - value = verifyUpdateResult.value, - comment = verifyUpdateResult.comment, - id = newValueIri, - rights = verifyUpdateResult.permissionCode - ) + } yield ChangeValueResponseV1( + value = verifyUpdateResult.value, + comment = verifyUpdateResult.comment, + id = newValueIri, + rights = verifyUpdateResult.permissionCode + ) } /** - * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to create a `LinkValue` or to - * increment the reference count of an existing `LinkValue`. This happens in two cases: - * - * - When the user creates a link. In this case, neither the link nor the `LinkValue` exist yet. The - * [[SparqlTemplateLinkUpdate]] will specify that the link should be created, and that the `LinkValue` should be - * created with a reference count of 1. - * - When a text value is updated so that its standoff markup refers to a resource that it did not previously - * refer to. Here there are two possibilities: - * - If there is currently a `knora-base:hasStandoffLinkTo` link between the source and target resources, with a - * corresponding `LinkValue`, a new version of the `LinkValue` will be made, with an incremented reference count. - * - If that link and `LinkValue` don't yet exist, they will be created, and the `LinkValue` will be given - * a reference count of 1. - * - * @param sourceResourceIri the IRI of the source resource. - * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. - * @param targetResourceIri the IRI of the target resource. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. - */ - private def incrementLinkValue(sourceResourceIri: IRI, - linkPropertyIri: IRI, - targetResourceIri: IRI, - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[SparqlTemplateLinkUpdate] = { + * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to create a `LinkValue` or to + * increment the reference count of an existing `LinkValue`. This happens in two cases: + * + * - When the user creates a link. In this case, neither the link nor the `LinkValue` exist yet. The + * [[SparqlTemplateLinkUpdate]] will specify that the link should be created, and that the `LinkValue` should be + * created with a reference count of 1. + * - When a text value is updated so that its standoff markup refers to a resource that it did not previously + * refer to. Here there are two possibilities: + * - If there is currently a `knora-base:hasStandoffLinkTo` link between the source and target resources, with a + * corresponding `LinkValue`, a new version of the `LinkValue` will be made, with an incremented reference count. + * - If that link and `LinkValue` don't yet exist, they will be created, and the `LinkValue` will be given + * a reference count of 1. + * + * @param sourceResourceIri the IRI of the source resource. + * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. + * @param targetResourceIri the IRI of the target resource. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. + */ + private def incrementLinkValue( + sourceResourceIri: IRI, + linkPropertyIri: IRI, + targetResourceIri: IRI, + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[SparqlTemplateLinkUpdate] = for { // Check whether a LinkValue already exists for this link. maybeLinkValueQueryResult <- findLinkValueByLinkTriple( @@ -2775,36 +2874,37 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde ) } } yield linkUpdate - } /** - * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to decrement the reference count - * of a `LinkValue`. This happens in two cases: - * - * - When the user deletes (or changes) a user-created link. In this case, the current reference count will be 1. - * The existing link will be removed. A new version of the `LinkValue` be made with a reference count of 0, and - * will be marked as deleted. - * - When a resource reference is removed from standoff markup on a text value, so that the text value no longer - * contains any references to that target resource. In this case, a new version of the `LinkValue` will be - * made, with a decremented reference count. If the new reference count is 0, the link will be removed and the - * `LinkValue` will be marked as deleted. - * - * @param sourceResourceIri the IRI of the source resource. - * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. - * @param targetResourceIri the IRI of the target resource. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. - */ - private def decrementLinkValue(sourceResourceIri: IRI, - linkPropertyIri: IRI, - targetResourceIri: IRI, - valueCreator: IRI, - valuePermissions: String, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[SparqlTemplateLinkUpdate] = { + * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to decrement the reference count + * of a `LinkValue`. This happens in two cases: + * + * - When the user deletes (or changes) a user-created link. In this case, the current reference count will be 1. + * The existing link will be removed. A new version of the `LinkValue` be made with a reference count of 0, and + * will be marked as deleted. + * - When a resource reference is removed from standoff markup on a text value, so that the text value no longer + * contains any references to that target resource. In this case, a new version of the `LinkValue` will be + * made, with a decremented reference count. If the new reference count is 0, the link will be removed and the + * `LinkValue` will be marked as deleted. + * + * @param sourceResourceIri the IRI of the source resource. + * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. + * @param targetResourceIri the IRI of the target resource. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. + */ + private def decrementLinkValue( + sourceResourceIri: IRI, + linkPropertyIri: IRI, + targetResourceIri: IRI, + valueCreator: IRI, + valuePermissions: String, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[SparqlTemplateLinkUpdate] = for { // Query the LinkValue to ensure that it exists and to get its contents. maybeLinkValueQueryResult <- findLinkValueByLinkTriple( @@ -2849,17 +2949,17 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde case None => // We didn't find the LinkValue. This shouldn't happen. throw InconsistentRepositoryDataException( - s"There should be a knora-base:LinkValue describing a direct link from resource $sourceResourceIri to resource $targetResourceIri using property $linkPropertyIri, but it seems to be missing") + s"There should be a knora-base:LinkValue describing a direct link from resource $sourceResourceIri to resource $targetResourceIri using property $linkPropertyIri, but it seems to be missing" + ) } } yield linkUpdate - } /** - * Checks a [[TextValueV1]] to make sure that the resource references in its [[StandoffTagV2]] objects match - * the list of resource IRIs in its `resource_reference` member variable. - * - * @param textValue the [[TextValueV1]] to be checked. - */ + * Checks a [[TextValueV1]] to make sure that the resource references in its [[StandoffTagV2]] objects match + * the list of resource IRIs in its `resource_reference` member variable. + * + * @param textValue the [[TextValueV1]] to be checked. + */ @throws(classOf[BadRequestException]) private def checkTextValueResourceRefs(textValue: TextValueWithStandoffV1): Unit = { @@ -2882,22 +2982,25 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde if (resourceRefsInStandoff != textValue.resource_reference) { throw BadRequestException( - s"The list of resource references in this text value does not match the resource references in its Standoff markup: $textValue") + s"The list of resource references in this text value does not match the resource references in its Standoff markup: $textValue" + ) } } /** - * Given a set of IRIs of standoff resource reference targets, checks that each one actually refers to a `knora-base:Resource`. - * - * @param targetIris the IRIs to check. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return a `Future[Unit]` on success, otherwise a `Future` containing an exception ([[NotFoundException]] if the target resource is not found, - * or [[BadRequestException]] if the target IRI isn't a `knora-base:Resource`). - */ - private def checkStandoffResourceReferenceTargets(targetIris: Set[IRI], - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Unit] = { + * Given a set of IRIs of standoff resource reference targets, checks that each one actually refers to a `knora-base:Resource`. + * + * @param targetIris the IRIs to check. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return a `Future[Unit]` on success, otherwise a `Future` containing an exception ([[NotFoundException]] if the target resource is not found, + * or [[BadRequestException]] if the target IRI isn't a `knora-base:Resource`). + */ + private def checkStandoffResourceReferenceTargets( + targetIris: Set[IRI], + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Unit] = if (targetIris.isEmpty) { Future(()) } else { @@ -2912,7 +3015,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde _ = if (!checkTargetClassResponse.isInClass) throw BadRequestException( - s"$targetIri cannot be the object of a standoff resource reference, because it is not a knora-base:Resource") + s"$targetIri cannot be the object of a standoff resource reference, because it is not a knora-base:Resource" + ) } yield () } @@ -2920,24 +3024,25 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde targetIriChecks: Set[Unit] <- Future.sequence(targetIriCheckFutures) } yield targetIriChecks.head } - } /** - * Implements a pre-update check to ensure that an [[UpdateValueV1]] has the correct type for the `knora-base:objectClassConstraint` of - * the property that is supposed to point to it. - * - * @param propertyIri the IRI of the property. - * @param propertyObjectClassConstraint the IRI of the `knora-base:objectClassConstraint` of the property. - * @param updateValueV1 the value to be updated. - * @param featureFactoryConfig the feature factory configuration. - * @param userProfile the profile of the user making the request. - * @return an empty [[Future]] on success, or a failed [[Future]] if the value has the wrong type. - */ - private def checkPropertyObjectClassConstraintForValue(propertyIri: IRI, - propertyObjectClassConstraint: IRI, - updateValueV1: UpdateValueV1, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[Unit] = { + * Implements a pre-update check to ensure that an [[UpdateValueV1]] has the correct type for the `knora-base:objectClassConstraint` of + * the property that is supposed to point to it. + * + * @param propertyIri the IRI of the property. + * @param propertyObjectClassConstraint the IRI of the `knora-base:objectClassConstraint` of the property. + * @param updateValueV1 the value to be updated. + * @param featureFactoryConfig the feature factory configuration. + * @param userProfile the profile of the user making the request. + * @return an empty [[Future]] on success, or a failed [[Future]] if the value has the wrong type. + */ + private def checkPropertyObjectClassConstraintForValue( + propertyIri: IRI, + propertyObjectClassConstraint: IRI, + updateValueV1: UpdateValueV1, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[Unit] = for { result <- updateValueV1 match { case linkUpdate: LinkUpdateV1 => @@ -2952,7 +3057,8 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde _ = if (!checkTargetClassResponse.isInClass) { throw OntologyConstraintException( - s"Resource ${linkUpdate.targetResourceIri} cannot be the target of property $propertyIri, because it is not a member of OWL class $propertyObjectClassConstraint") + s"Resource ${linkUpdate.targetResourceIri} cannot be the target of property $propertyIri, because it is not a member of OWL class $propertyObjectClassConstraint" + ) } } yield () @@ -2967,11 +3073,10 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde ) } } yield result - } /** - * The permissions that are granted by every `knora-base:LinkValue` describing a standoff link. - */ + * The permissions that are granted by every `knora-base:LinkValue` describing a standoff link. + */ lazy val standoffLinkValuePermissions: String = { val permissions: Set[PermissionADM] = Set( PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.SystemUser), diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ListsResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ListsResponderV2.scala index 400bde3cc9..d539383b1f 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ListsResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ListsResponderV2.scala @@ -37,13 +37,13 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import scala.concurrent.Future /** - * Responds to requests relating to lists and nodes. - */ + * Responds to requests relating to lists and nodes. + */ class ListsResponderV2(responderData: ResponderData) extends Responder(responderData) { /** - * Receives a message of type [[ListsResponderRequestV2]], and returns an appropriate response message inside a future. - */ + * Receives a message of type [[ListsResponderRequestV2]], and returns an appropriate response message inside a future. + */ def receive(msg: ListsResponderRequestV2) = msg match { case ListGetRequestV2(listIri, featureFactoryConfig, requestingUser) => getList(listIri, featureFactoryConfig, requestingUser) @@ -53,16 +53,17 @@ class ListsResponderV2(responderData: ResponderData) extends Responder(responder } /** - * Gets a list from the triplestore. - * - * @param listIri the Iri of the list's root node. - * @param requestingUser the user making the request. - * @return a [[ListGetResponseV2]]. - */ - private def getList(listIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ListGetResponseV2] = { - + * Gets a list from the triplestore. + * + * @param listIri the Iri of the list's root node. + * @param requestingUser the user making the request. + * @return a [[ListGetResponseV2]]. + */ + private def getList( + listIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ListGetResponseV2] = for { listResponseADM: ListGetResponseADM <- (responderManager ? ListGetRequestADM( iri = listIri, @@ -71,20 +72,20 @@ class ListsResponderV2(responderData: ResponderData) extends Responder(responder )).mapTo[ListGetResponseADM] } yield ListGetResponseV2(list = listResponseADM.list, requestingUser.lang, settings.fallbackLanguage) - } /** - * Gets a single list node from the triplestore. - * - * @param nodeIri the Iri of the list node. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[NodeGetResponseV2]]. - */ - private def getNode(nodeIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[NodeGetResponseV2] = { - + * Gets a single list node from the triplestore. + * + * @param nodeIri the Iri of the list node. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[NodeGetResponseV2]]. + */ + private def getNode( + nodeIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[NodeGetResponseV2] = for { nodeResponse: ChildNodeInfoGetResponseADM <- (responderManager ? ListNodeInfoGetRequestADM( iri = nodeIri, @@ -93,5 +94,4 @@ class ListsResponderV2(responderData: ResponderData) extends Responder(responder )).mapTo[ChildNodeInfoGetResponseADM] } yield NodeGetResponseV2(node = nodeResponse.nodeinfo, requestingUser.lang, settings.fallbackLanguage) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/MetadataResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/MetadataResponderV2.scala index 2078daa1ed..8a73081178 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/MetadataResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/MetadataResponderV2.scala @@ -34,14 +34,13 @@ import org.knora.webapi.responders.{IriLocker, Responder} import scala.concurrent.Future /** - * Responds to requests dealing with project metadata. - * - **/ + * Responds to requests dealing with project metadata. + */ class MetadataResponderV2(responderData: ResponderData) extends Responder(responderData) { /** - * Receives a message of type [[MetadataResponderRequestV2]], and returns an appropriate response message. - */ + * Receives a message of type [[MetadataResponderRequestV2]], and returns an appropriate response message. + */ def receive(msg: MetadataResponderRequestV2) = msg match { case getRequest: MetadataGetRequestV2 => getProjectMetadata(projectADM = getRequest.projectADM) case putRequest: MetadataPutRequestV2 => putProjectMetadata(putRequest) @@ -49,11 +48,11 @@ class MetadataResponderV2(responderData: ResponderData) extends Responder(respon } /** - * GET metadata graph of a project. - * - * @param projectADM the project whose metadata is requested. - * @return a [[MetadataGetResponseV2]]. - */ + * GET metadata graph of a project. + * + * @param projectADM the project whose metadata is requested. + * @return a [[MetadataGetResponseV2]]. + */ private def getProjectMetadata(projectADM: ProjectADM): Future[MetadataGetResponseV2] = { val graphIri: IRI = stringFormatter.projectMetadataNamedGraphV2(projectADM) @@ -64,17 +63,17 @@ class MetadataResponderV2(responderData: ResponderData) extends Responder(respon } /** - * PUT metadata graph of a project. Every time a new metdata information is given for a project, it overwrites the - * previous metadata graph. - * - * @param request the request to put the metadata graph into the triplestore. - * @return a [[SuccessResponseV2]]. - */ + * PUT metadata graph of a project. Every time a new metdata information is given for a project, it overwrites the + * previous metadata graph. + * + * @param request the request to put the metadata graph into the triplestore. + * @return a [[SuccessResponseV2]]. + */ private def putProjectMetadata(request: MetadataPutRequestV2): Future[SuccessResponseV2] = { val metadataGraphIRI: IRI = stringFormatter.projectMetadataNamedGraphV2(request.projectADM) val graphContent: String = request.toTurtle(request.featureFactoryConfig) - def makeTaskFuture: Future[SuccessResponseV2] = { + def makeTaskFuture: Future[SuccessResponseV2] = for { // Create the project metadata graph. _ <- (storeManager ? @@ -97,7 +96,6 @@ class MetadataResponderV2(responderData: ResponderData) extends Responder(respon throw AssertionException("Project metadata was stored, but is not correct. Please report this a bug.") } } yield SuccessResponseV2(s"Project metadata was stored for project <${request.projectIri}>.") - } for { // Create the metadata graph holding an update lock on the graph IRI so that no other client can diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala index a224aeeb3b..e285f81db8 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala @@ -167,60 +167,58 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon (standoffClassIris ++ standoffPropertyIris).filter(_.getOntologySchema.contains(ApiV2Simple)) _ = if (entitiesInWrongSchema.nonEmpty) { - throw NotFoundException( - s"Some requested standoff classes were not found: ${entitiesInWrongSchema.mkString(", ")}" - ) - } + throw NotFoundException( + s"Some requested standoff classes were not found: ${entitiesInWrongSchema.mkString(", ")}" + ) + } - classIrisForCache = standoffClassIris.map(_.toOntologySchema(InternalSchema)) + classIrisForCache = standoffClassIris.map(_.toOntologySchema(InternalSchema)) propertyIrisForCache = standoffPropertyIris.map(_.toOntologySchema(InternalSchema)) classOntologies: Iterable[ReadOntologyV2] = cacheData.ontologies.view - .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) - .values + .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) + .values propertyOntologies: Iterable[ReadOntologyV2] = cacheData.ontologies.view - .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) - .values + .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) + .values classDefsAvailable: Map[SmartIri, ReadClassInfoV2] = classOntologies.flatMap { ontology => - ontology.classes.filter { case (classIri, classDef) => - classDef.isStandoffClass && standoffClassIris.contains( - classIri - ) - } - }.toMap + ontology.classes.filter { case (classIri, classDef) => + classDef.isStandoffClass && standoffClassIris.contains( + classIri + ) + } + }.toMap propertyDefsAvailable: Map[SmartIri, ReadPropertyInfoV2] = propertyOntologies.flatMap { ontology => - ontology.properties.filter { case (propertyIri, _) => - standoffPropertyIris.contains( - propertyIri - ) && cacheData.standoffProperties.contains( - propertyIri - ) - } - }.toMap - - missingClassDefs = classIrisForCache -- classDefsAvailable.keySet + ontology.properties.filter { case (propertyIri, _) => + standoffPropertyIris.contains( + propertyIri + ) && cacheData.standoffProperties.contains( + propertyIri + ) + } + }.toMap + + missingClassDefs = classIrisForCache -- classDefsAvailable.keySet missingPropertyDefs = propertyIrisForCache -- propertyDefsAvailable.keySet _ = if (missingClassDefs.nonEmpty) { - throw NotFoundException( - s"Some requested standoff classes were not found: ${missingClassDefs.mkString(", ")}" - ) - } + throw NotFoundException( + s"Some requested standoff classes were not found: ${missingClassDefs.mkString(", ")}" + ) + } _ = if (missingPropertyDefs.nonEmpty) { - throw NotFoundException( - s"Some requested standoff properties were not found: ${missingPropertyDefs.mkString(", ")}" - ) - } + throw NotFoundException( + s"Some requested standoff properties were not found: ${missingPropertyDefs.mkString(", ")}" + ) + } response = StandoffEntityInfoGetResponseV2( - standoffClassInfoMap = - new ErrorHandlingMap(classDefsAvailable, key => s"Resource class $key not found"), - standoffPropertyInfoMap = - new ErrorHandlingMap(propertyDefsAvailable, key => s"Property $key not found") - ) + standoffClassInfoMap = new ErrorHandlingMap(classDefsAvailable, key => s"Resource class $key not found"), + standoffPropertyInfoMap = new ErrorHandlingMap(propertyDefsAvailable, key => s"Property $key not found") + ) } yield response /** @@ -270,11 +268,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon for { cacheData <- Cache.getCacheData response = CheckSubClassResponseV2( - isSubClass = cacheData.subClassOfRelations.get(subClassIri) match { - case Some(baseClasses) => baseClasses.contains(superClassIri) - case None => throw BadRequestException(s"Class $subClassIri not found") - } - ) + isSubClass = cacheData.subClassOfRelations.get(subClassIri) match { + case Some(baseClasses) => baseClasses.contains(superClassIri) + case None => throw BadRequestException(s"Class $subClassIri not found") + } + ) } yield response /** @@ -290,21 +288,21 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon subClassIris = cacheData.superClassOfRelations(classIri).toVector.sorted subClasses = subClassIris.map { subClassIri => - val classInfo: ReadClassInfoV2 = - cacheData.ontologies(subClassIri.getOntologyFromEntity).classes(subClassIri) - - SubClassInfoV2( - id = subClassIri, - label = classInfo.entityInfoContent - .getPredicateStringLiteralObject( - predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, - preferredLangs = Some(requestingUser.lang, settings.fallbackLanguage) - ) - .getOrElse( - throw InconsistentRepositoryDataException(s"Resource class $subClassIri has no rdfs:label") - ) - ) - } + val classInfo: ReadClassInfoV2 = + cacheData.ontologies(subClassIri.getOntologyFromEntity).classes(subClassIri) + + SubClassInfoV2( + id = subClassIri, + label = classInfo.entityInfoContent + .getPredicateStringLiteralObject( + predicateIri = OntologyConstants.Rdfs.Label.toSmartIri, + preferredLangs = Some(requestingUser.lang, settings.fallbackLanguage) + ) + .getOrElse( + throw InconsistentRepositoryDataException(s"Resource class $subClassIri has no rdfs:label") + ) + ) + } } yield SubClassesGetResponseV2( subClasses = subClasses ) @@ -322,7 +320,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ): Future[OntologyKnoraEntitiesIriInfoV2] = for { cacheData <- Cache.getCacheData - ontology = cacheData.ontologies(ontologyIri) + ontology = cacheData.ontologies(ontologyIri) } yield OntologyKnoraEntitiesIriInfoV2( ontologyIri = ontologyIri, propertyIris = ontology.properties.keySet.filter { propertyIri => @@ -349,18 +347,19 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon requestingUser: UserADM ): Future[ReadOntologyMetadataV2] = for { - cacheData <- Cache.getCacheData + cacheData <- Cache.getCacheData returnAllOntologies: Boolean = projectIris.isEmpty - ontologyMetadata: Set[OntologyMetadataV2] = if (returnAllOntologies) { - cacheData.ontologies.values.map(_.ontologyMetadata).toSet - } else { - cacheData.ontologies.values.filter { ontology => - projectIris.contains(ontology.ontologyMetadata.projectIri.get) - }.map { ontology => - ontology.ontologyMetadata - }.toSet - } + ontologyMetadata: Set[OntologyMetadataV2] = + if (returnAllOntologies) { + cacheData.ontologies.values.map(_.ontologyMetadata).toSet + } else { + cacheData.ontologies.values.filter { ontology => + projectIris.contains(ontology.ontologyMetadata.projectIri.get) + }.map { ontology => + ontology.ontologyMetadata + }.toSet + } } yield ReadOntologyMetadataV2( ontologies = ontologyMetadata ) @@ -377,32 +376,33 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon requestingUser: UserADM ): Future[ReadOntologyMetadataV2] = for { - cacheData <- Cache.getCacheData + cacheData <- Cache.getCacheData returnAllOntologies: Boolean = ontologyIris.isEmpty - ontologyMetadata: Set[OntologyMetadataV2] = if (returnAllOntologies) { - cacheData.ontologies.values.map(_.ontologyMetadata).toSet - } else { - val ontologyIrisForCache = - ontologyIris.map(_.toOntologySchema(InternalSchema)) - val missingOntologies = - ontologyIrisForCache -- cacheData.ontologies.keySet - - if (missingOntologies.nonEmpty) { - throw BadRequestException( - s"One or more requested ontologies were not found: ${missingOntologies - .mkString(", ")}" - ) - } - - cacheData.ontologies.view - .filterKeys(ontologyIrisForCache) - .values - .map { ontology => - ontology.ontologyMetadata - } - .toSet - } + ontologyMetadata: Set[OntologyMetadataV2] = + if (returnAllOntologies) { + cacheData.ontologies.values.map(_.ontologyMetadata).toSet + } else { + val ontologyIrisForCache = + ontologyIris.map(_.toOntologySchema(InternalSchema)) + val missingOntologies = + ontologyIrisForCache -- cacheData.ontologies.keySet + + if (missingOntologies.nonEmpty) { + throw BadRequestException( + s"One or more requested ontologies were not found: ${missingOntologies + .mkString(", ")}" + ) + } + + cacheData.ontologies.view + .filterKeys(ontologyIrisForCache) + .values + .map { ontology => + ontology.ontologyMetadata + } + .toSet + } } yield ReadOntologyMetadataV2( ontologies = ontologyMetadata ) @@ -423,22 +423,23 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon cacheData <- Cache.getCacheData _ = if (ontologyIri.getOntologyName == "standoff" && ontologyIri.getOntologySchema.contains(ApiV2Simple)) { - throw BadRequestException(s"The standoff ontology is not available in the API v2 simple schema") - } + throw BadRequestException(s"The standoff ontology is not available in the API v2 simple schema") + } ontology = cacheData.ontologies.get(ontologyIri.toOntologySchema(InternalSchema)) match { - case Some(cachedOntology) => cachedOntology - case None => throw NotFoundException(s"Ontology not found: $ontologyIri") - } + case Some(cachedOntology) => cachedOntology + case None => throw NotFoundException(s"Ontology not found: $ontologyIri") + } // Are we returning data in the user's preferred language, or in all available languages? - userLang = if (!allLanguages) { - // Just the user's preferred language. - Some(requestingUser.lang) - } else { - // All available languages. - None - } + userLang = + if (!allLanguages) { + // Just the user's preferred language. + Some(requestingUser.lang) + } else { + // All available languages. + None + } } yield ontology.copy( userLang = userLang ) @@ -475,21 +476,22 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ontologyIris = propertyIris.map(_.getOntologyFromEntity) _ = if (ontologyIris.size != 1) { - throw BadRequestException(s"Only one ontology may be queried per request") - } + throw BadRequestException(s"Only one ontology may be queried per request") + } propertyInfoResponse: EntityInfoGetResponseV2 <- getEntityInfoResponseV2(propertyIris = propertyIris, requestingUser = requestingUser) internalOntologyIri = ontologyIris.head.toOntologySchema(InternalSchema) // Are we returning data in the user's preferred language, or in all available languages? - userLang = if (!allLanguages) { - // Just the user's preferred language. - Some(requestingUser.lang) - } else { - // All available languages. - None - } + userLang = + if (!allLanguages) { + // Just the user's preferred language. + Some(requestingUser.lang) + } else { + // All available languages. + None + } } yield ReadOntologyV2( ontologyMetadata = cacheData.ontologies(internalOntologyIri).ontologyMetadata, properties = propertyInfoResponse.propertyInfoMap, @@ -509,18 +511,17 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Make sure the ontology doesn't already exist. existingOntologyMetadata: Option[OntologyMetadataV2] <- OntologyHelpers.loadOntologyMetadata( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = - createOntologyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = createOntologyRequest.featureFactoryConfig + ) _ = if (existingOntologyMetadata.nonEmpty) { - throw BadRequestException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be created, because it already exists" - ) - } + throw BadRequestException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be created, because it already exists" + ) + } // If this is a shared ontology, make sure it's in the default shared ontologies project. _ = @@ -547,47 +548,46 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now createOntologySparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .createOntology( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - projectIri = createOntologyRequest.projectIri, - isShared = createOntologyRequest.isShared, - ontologyLabel = createOntologyRequest.label, - ontologyComment = createOntologyRequest.comment, - currentTime = currentTime - ) - .toString + .createOntology( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + projectIri = createOntologyRequest.projectIri, + isShared = createOntologyRequest.isShared, + ontologyLabel = createOntologyRequest.label, + ontologyComment = createOntologyRequest.comment, + currentTime = currentTime + ) + .toString _ <- (storeManager ? SparqlUpdateRequest(createOntologySparql)).mapTo[SparqlUpdateResponse] // Check that the update was successful. To do this, we have to undo the SPARQL-escaping of the input. unescapedNewMetadata = OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(createOntologyRequest.projectIri), - label = Some(createOntologyRequest.label), - comment = createOntologyRequest.comment, - lastModificationDate = Some(currentTime) - ).unescape + ontologyIri = internalOntologyIri, + projectIri = Some(createOntologyRequest.projectIri), + label = Some(createOntologyRequest.label), + comment = createOntologyRequest.comment, + lastModificationDate = Some(currentTime) + ).unescape maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- OntologyHelpers.loadOntologyMetadata( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = - createOntologyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = createOntologyRequest.featureFactoryConfig + ) _ = maybeLoadedOntologyMetadata match { - case Some(loadedOntologyMetadata) => - if (loadedOntologyMetadata != unescapedNewMetadata) { - throw UpdateNotPerformedException() - } - - case None => throw UpdateNotPerformedException() + case Some(loadedOntologyMetadata) => + if (loadedOntologyMetadata != unescapedNewMetadata) { + throw UpdateNotPerformedException() } + case None => throw UpdateNotPerformedException() + } + // Update the ontology cache with the unescaped metadata. _ = @@ -602,7 +602,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon for { requestingUser <- FastFuture.successful(createOntologyRequest.requestingUser) - projectIri = createOntologyRequest.projectIri + projectIri = createOntologyRequest.projectIri // check if the requesting user is allowed to create an ontology _ = @@ -618,10 +618,10 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Get project info for the shortcode. projectInfo: ProjectGetResponseADM <- (responderManager ? ProjectGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(projectIri.toString)), - featureFactoryConfig = createOntologyRequest.featureFactoryConfig, - requestingUser = requestingUser - )).mapTo[ProjectGetResponseADM] + identifier = ProjectIdentifierADM(maybeIri = Some(projectIri.toString)), + featureFactoryConfig = createOntologyRequest.featureFactoryConfig, + requestingUser = requestingUser + )).mapTo[ProjectGetResponseADM] // Check that the ontology name is valid. validOntologyName = @@ -632,17 +632,17 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Make the internal ontology IRI. internalOntologyIri = stringFormatter.makeProjectSpecificInternalOntologyIri( - validOntologyName, - createOntologyRequest.isShared, - projectInfo.project.shortcode - ) + validOntologyName, + createOntologyRequest.isShared, + projectInfo.project.shortcode + ) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = createOntologyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => makeTaskFuture(internalOntologyIri) - ) + apiRequestID = createOntologyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => makeTaskFuture(internalOntologyIri) + ) } yield taskResult } @@ -661,18 +661,18 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the user has permission to update the ontology. projectIri <- OntologyHelpers.checkPermissionsForOntologyUpdate( - internalOntologyIri = internalOntologyIri, - requestingUser = changeOntologyMetadataRequest.requestingUser - ) + internalOntologyIri = internalOntologyIri, + requestingUser = changeOntologyMetadataRequest.requestingUser + ) // Check that the ontology exists and has not been updated by another user since the client last read its metadata. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeOntologyMetadataRequest.lastModificationDate, - featureFactoryConfig = changeOntologyMetadataRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeOntologyMetadataRequest.lastModificationDate, + featureFactoryConfig = changeOntologyMetadataRequest.featureFactoryConfig + ) // get the metadata of the ontology. oldMetadata: OntologyMetadataV2 = cacheData.ontologies(internalOntologyIri).ontologyMetadata @@ -684,48 +684,50 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changeOntologyMetadata( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - newLabel = changeOntologyMetadataRequest.label, - hasOldComment = ontologyHasComment, - deleteOldComment = ontologyHasComment && changeOntologyMetadataRequest.comment.nonEmpty, - newComment = changeOntologyMetadataRequest.comment, - lastModificationDate = changeOntologyMetadataRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changeOntologyMetadata( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + newLabel = changeOntologyMetadataRequest.label, + hasOldComment = ontologyHasComment, + deleteOldComment = ontologyHasComment && changeOntologyMetadataRequest.comment.nonEmpty, + newComment = changeOntologyMetadataRequest.comment, + lastModificationDate = changeOntologyMetadataRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the update was successful. To do this, we have to undo the SPARQL-escaping of the input. // Is there any new label given? - label = if (changeOntologyMetadataRequest.label.isEmpty) { - // No. Consider the old label for checking the update. - oldMetadata.label - } else { - // Yes. Consider the new label for checking the update. - changeOntologyMetadataRequest.label - } + label = + if (changeOntologyMetadataRequest.label.isEmpty) { + // No. Consider the old label for checking the update. + oldMetadata.label + } else { + // Yes. Consider the new label for checking the update. + changeOntologyMetadataRequest.label + } // Is there any new comment given? - comment = if (changeOntologyMetadataRequest.comment.isEmpty) { - // No. Consider the old comment for checking the update. - oldMetadata.comment - } else { - // Yes. Consider the new comment for checking the update. - changeOntologyMetadataRequest.comment - } + comment = + if (changeOntologyMetadataRequest.comment.isEmpty) { + // No. Consider the old comment for checking the update. + oldMetadata.comment + } else { + // Yes. Consider the new comment for checking the update. + changeOntologyMetadataRequest.comment + } unescapedNewMetadata = OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(projectIri), - label = label, - comment = comment, - lastModificationDate = Some(currentTime) - ).unescape + ontologyIri = internalOntologyIri, + projectIri = Some(projectIri), + label = label, + comment = comment, + lastModificationDate = Some(currentTime) + ).unescape maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- OntologyHelpers.loadOntologyMetadata( @@ -736,36 +738,36 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = maybeLoadedOntologyMetadata match { - case Some(loadedOntologyMetadata) => - if (loadedOntologyMetadata != unescapedNewMetadata) { - throw UpdateNotPerformedException() - } - - case None => throw UpdateNotPerformedException() + case Some(loadedOntologyMetadata) => + if (loadedOntologyMetadata != unescapedNewMetadata) { + throw UpdateNotPerformedException() } + case None => throw UpdateNotPerformedException() + } + // Update the ontology cache with the unescaped metadata. _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData - .ontologies(internalOntologyIri) - .copy(ontologyMetadata = unescapedNewMetadata)) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData + .ontologies(internalOntologyIri) + .copy(ontologyMetadata = unescapedNewMetadata)) + ) + ) } yield ReadOntologyMetadataV2(ontologies = Set(unescapedNewMetadata)) for { - _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(changeOntologyMetadataRequest.ontologyIri) + _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(changeOntologyMetadataRequest.ontologyIri) internalOntologyIri = changeOntologyMetadataRequest.ontologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeOntologyMetadataRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) - ) + apiRequestID = changeOntologyMetadataRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) + ) } yield taskResult } @@ -778,18 +780,18 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the user has permission to update the ontology. projectIri <- OntologyHelpers.checkPermissionsForOntologyUpdate( - internalOntologyIri = internalOntologyIri, - requestingUser = deleteOntologyCommentRequestV2.requestingUser - ) + internalOntologyIri = internalOntologyIri, + requestingUser = deleteOntologyCommentRequestV2.requestingUser + ) // Check that the ontology exists and has not been updated by another user since the client last read its metadata. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, - featureFactoryConfig = deleteOntologyCommentRequestV2.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, + featureFactoryConfig = deleteOntologyCommentRequestV2.featureFactoryConfig + ) // get the metadata of the ontology. oldMetadata: OntologyMetadataV2 = cacheData.ontologies(internalOntologyIri).ontologyMetadata @@ -801,30 +803,30 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changeOntologyMetadata( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - newLabel = None, - hasOldComment = ontologyHasComment, - deleteOldComment = true, - newComment = None, - lastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changeOntologyMetadata( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + newLabel = None, + hasOldComment = ontologyHasComment, + deleteOldComment = true, + newComment = None, + lastModificationDate = deleteOntologyCommentRequestV2.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the update was successful. unescapedNewMetadata = OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(projectIri), - label = oldMetadata.label, - comment = None, - lastModificationDate = Some(currentTime) - ).unescape + ontologyIri = internalOntologyIri, + projectIri = Some(projectIri), + label = oldMetadata.label, + comment = None, + lastModificationDate = Some(currentTime) + ).unescape maybeLoadedOntologyMetadata: Option[OntologyMetadataV2] <- OntologyHelpers.loadOntologyMetadata( @@ -835,36 +837,36 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = maybeLoadedOntologyMetadata match { - case Some(loadedOntologyMetadata) => - if (loadedOntologyMetadata != unescapedNewMetadata) { - throw UpdateNotPerformedException() - } - - case None => throw UpdateNotPerformedException() + case Some(loadedOntologyMetadata) => + if (loadedOntologyMetadata != unescapedNewMetadata) { + throw UpdateNotPerformedException() } + case None => throw UpdateNotPerformedException() + } + // Update the ontology cache with the unescaped metadata. _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData - .ontologies(internalOntologyIri) - .copy(ontologyMetadata = unescapedNewMetadata)) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> cacheData + .ontologies(internalOntologyIri) + .copy(ontologyMetadata = unescapedNewMetadata)) + ) + ) } yield ReadOntologyMetadataV2(ontologies = Set(unescapedNewMetadata)) for { - _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(deleteOntologyCommentRequestV2.ontologyIri) + _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(deleteOntologyCommentRequestV2.ontologyIri) internalOntologyIri = deleteOntologyCommentRequestV2.ontologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteOntologyCommentRequestV2.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) - ) + apiRequestID = deleteOntologyCommentRequestV2.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => makeTaskFuture(internalOntologyIri = internalOntologyIri) + ) } yield taskResult } @@ -877,40 +879,40 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon private def createClass(createClassRequest: CreateClassRequestV2): Future[ReadOntologyV2] = { def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- Cache.getCacheData + cacheData <- Cache.getCacheData internalClassDef: ClassInfoContentV2 = createClassRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = createClassRequest.lastModificationDate, - featureFactoryConfig = createClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = createClassRequest.lastModificationDate, + featureFactoryConfig = createClassRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. rdfType: SmartIri = internalClassDef.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } ontology = cacheData.ontologies(internalOntologyIri) // Check that the class doesn't exist yet. _ = if (ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${createClassRequest.classInfoContent.classIri} already exists") - } + throw BadRequestException(s"Class ${createClassRequest.classInfoContent.classIri} already exists") + } // Check that the class's IRI isn't already used for something else. _ = if (ontology.properties.contains(internalClassIri) || ontology.individuals.contains(internalClassIri)) { - throw BadRequestException(s"IRI ${createClassRequest.classInfoContent.classIri} is already used") - } + throw BadRequestException(s"IRI ${createClassRequest.classInfoContent.classIri} is already used") + } // Check that the base classes that have Knora IRIs are defined as Knora resource classes. @@ -920,34 +922,34 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon .filter(baseClassIri => !OntologyHelpers.isKnoraInternalResourceClass(baseClassIri, cacheData)) _ = if (missingBaseClasses.nonEmpty) { - throw BadRequestException( - s"One or more specified base classes are invalid: ${missingBaseClasses.mkString(", ")}" - ) - } + throw BadRequestException( + s"One or more specified base classes are invalid: ${missingBaseClasses.mkString(", ")}" + ) + } // Check for rdfs:subClassOf cycles. allBaseClassIrisWithoutSelf: Set[SmartIri] = internalClassDef.subClassOf.flatMap { baseClassIri => - cacheData.subClassOfRelations - .getOrElse(baseClassIri, Set.empty[SmartIri]) - .toSet - } + cacheData.subClassOfRelations + .getOrElse(baseClassIri, Set.empty[SmartIri]) + .toSet + } _ = if (allBaseClassIrisWithoutSelf.contains(internalClassIri)) { - throw BadRequestException( - s"Class ${createClassRequest.classInfoContent.classIri} would have a cyclical rdfs:subClassOf" - ) - } + throw BadRequestException( + s"Class ${createClassRequest.classInfoContent.classIri} would have a cyclical rdfs:subClassOf" + ) + } // Check that the class is a subclass of knora-base:Resource. allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutSelf.toSeq _ = if (!allBaseClassIris.contains(OntologyConstants.KnoraBase.Resource.toSmartIri)) { - throw BadRequestException( - s"Class ${createClassRequest.classInfoContent.classIri} would not be a subclass of knora-api:Resource" - ) - } + throw BadRequestException( + s"Class ${createClassRequest.classInfoContent.classIri} would not be a subclass of knora-api:Resource" + ) + } // Check that the cardinalities are valid, and add any inherited cardinalities. (internalClassDefWithLinkValueProps, cardinalitiesForClassWithInheritance) = @@ -960,12 +962,12 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the class definition doesn't refer to any non-shared ontologies in other projects. _ = Cache.checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = internalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + ontologyCacheData = cacheData, + classDef = internalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Prepare to update the ontology cache, undoing the SPARQL-escaping of the input. @@ -979,122 +981,122 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon unescapedClassDefWithLinkValueProps = internalClassDefWithLinkValueProps.unescape readClassInfo = ReadClassInfoV2( - entityInfoContent = unescapedClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) - ), - linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkProp(propertyIri, cacheData) - ), - linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkValueProp(propertyIri, cacheData) - ), - fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isFileValueProp(propertyIri, cacheData) - ) - ) + entityInfoContent = unescapedClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) // Add the SPARQL-escaped class to the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .createClass( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classDef = internalClassDefWithLinkValueProps, - lastModificationDate = createClassRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .createClass( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classDef = internalClassDefWithLinkValueProps, + lastModificationDate = createClassRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = createClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = createClassRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. loadedClassDef <- OntologyHelpers.loadClassDefinition( - settings, - storeManager, - classIri = internalClassIri, - featureFactoryConfig = createClassRequest.featureFactoryConfig - ) + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = createClassRequest.featureFactoryConfig + ) _ = if (loadedClassDef != unescapedClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $unescapedClassDefWithLinkValueProps, but $loadedClassDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $unescapedClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update the cache. - updatedSubClassOfRelations = cacheData.subClassOfRelations + (internalClassIri -> allBaseClassIris) + updatedSubClassOfRelations = cacheData.subClassOfRelations + (internalClassIri -> allBaseClassIris) updatedSuperClassOfRelations = OntologyHelpers.calculateSuperClassOfRelations(updatedSubClassOfRelations) updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subClassOfRelations = updatedSubClassOfRelations, - superClassOfRelations = updatedSuperClassOfRelations - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subClassOfRelations = updatedSubClassOfRelations, + superClassOfRelations = updatedSuperClassOfRelations + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = createClassRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = createClassRequest.requestingUser + ) } yield response } for { requestingUser <- FastFuture.successful(createClassRequest.requestingUser) - externalClassIri = createClassRequest.classInfoContent.classIri + externalClassIri = createClassRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = createClassRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = createClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -1107,28 +1109,28 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon private def changeGuiOrder(changeGuiOrderRequest: ChangeGuiOrderRequestV2): Future[ReadOntologyV2] = { def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- Cache.getCacheData + cacheData <- Cache.getCacheData internalClassDef: ClassInfoContentV2 = changeGuiOrderRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeGuiOrderRequest.lastModificationDate, - featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeGuiOrderRequest.lastModificationDate, + featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. rdfType: SmartIri = internalClassDef.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that the class exists. @@ -1147,26 +1149,26 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon internalClassDef.directCardinalities.keySet -- currentReadClassInfo.entityInfoContent.directCardinalities.keySet _ = if (wrongProperties.nonEmpty) { - throw BadRequestException( - s"One or more submitted properties do not have cardinalities in class ${changeGuiOrderRequest.classInfoContent.classIri}: ${wrongProperties - .map(_.toOntologySchema(ApiV2Complex)) - .mkString(", ")}" - ) - } + throw BadRequestException( + s"One or more submitted properties do not have cardinalities in class ${changeGuiOrderRequest.classInfoContent.classIri}: ${wrongProperties + .map(_.toOntologySchema(ApiV2Complex)) + .mkString(", ")}" + ) + } linkValuePropCardinalities = internalClassDef.directCardinalities.filter { - case (propertyIri: SmartIri, _: KnoraCardinalityInfo) => - val propertyDef = cacheData - .ontologies(propertyIri.getOntologyFromEntity) - .properties(propertyIri) - propertyDef.isLinkProp - }.map { - case ( - propertyIri: SmartIri, - cardinalityWithCurrentGuiOrder: KnoraCardinalityInfo - ) => - propertyIri.fromLinkPropToLinkValueProp -> cardinalityWithCurrentGuiOrder - } + case (propertyIri: SmartIri, _: KnoraCardinalityInfo) => + val propertyDef = cacheData + .ontologies(propertyIri.getOntologyFromEntity) + .properties(propertyIri) + propertyDef.isLinkProp + }.map { + case ( + propertyIri: SmartIri, + cardinalityWithCurrentGuiOrder: KnoraCardinalityInfo + ) => + propertyIri.fromLinkPropToLinkValueProp -> cardinalityWithCurrentGuiOrder + } internalClassDefWithLinkValueProps = internalClassDef.directCardinalities ++ linkValuePropCardinalities @@ -1192,71 +1194,71 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .replaceClassCardinalities( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - newCardinalities = newReadClassInfo.entityInfoContent.directCardinalities, - lastModificationDate = changeGuiOrderRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .replaceClassCardinalities( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + newCardinalities = newReadClassInfo.entityInfoContent.directCardinalities, + lastModificationDate = changeGuiOrderRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. loadedClassDef: ClassInfoContentV2 <- OntologyHelpers.loadClassDefinition( - settings, - storeManager, - classIri = internalClassIri, - featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig - ) + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = changeGuiOrderRequest.featureFactoryConfig + ) _ = if (loadedClassDef != newReadClassInfo.entityInfoContent) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition ${newReadClassInfo.entityInfoContent}, but $loadedClassDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition ${newReadClassInfo.entityInfoContent}, but $loadedClassDef was saved" + ) + } // Update the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> newReadClassInfo) - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> newReadClassInfo) + ) // Update subclasses and write the cache. _ = Cache.storeCacheData( - Cache.updateSubClasses( - baseClassIri = internalClassIri, - cacheData = cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + Cache.updateSubClasses( + baseClassIri = internalClassIri, + cacheData = cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) ) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = changeGuiOrderRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = changeGuiOrderRequest.requestingUser + ) } yield response } @@ -1264,28 +1266,28 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon for { requestingUser <- FastFuture.successful(changeGuiOrderRequest.requestingUser) - externalClassIri = changeGuiOrderRequest.classInfoContent.classIri + externalClassIri = changeGuiOrderRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeGuiOrderRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changeGuiOrderRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -1300,34 +1302,34 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ): Future[ReadOntologyV2] = { def makeTaskFuture(internalClassIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- Cache.getCacheData + cacheData <- Cache.getCacheData internalClassDef: ClassInfoContentV2 = addCardinalitiesRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = addCardinalitiesRequest.lastModificationDate, - featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = addCardinalitiesRequest.lastModificationDate, + featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. rdfType: SmartIri = internalClassDef.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that cardinalities were submitted. _ = if (internalClassDef.directCardinalities.isEmpty) { - throw BadRequestException("No cardinalities specified") - } + throw BadRequestException("No cardinalities specified") + } // Check that the class exists, that it's a Knora resource class, and that the submitted cardinalities aren't for properties that already have cardinalities // directly defined on the class. @@ -1343,14 +1345,14 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon existingClassDef: ClassInfoContentV2 = existingReadClassInfo.entityInfoContent redundantCardinalities = existingClassDef.directCardinalities.keySet - .intersect(internalClassDef.directCardinalities.keySet) + .intersect(internalClassDef.directCardinalities.keySet) _ = if (redundantCardinalities.nonEmpty) { - throw BadRequestException( - s"The cardinalities of ${addCardinalitiesRequest.classInfoContent.classIri} already include the following property or properties: ${redundantCardinalities - .mkString(", ")}" - ) - } + throw BadRequestException( + s"The cardinalities of ${addCardinalitiesRequest.classInfoContent.classIri} already include the following property or properties: ${redundantCardinalities + .mkString(", ")}" + ) + } // Is there any property with minCardinality>0 or Cardinality=1? hasCardinality: Option[(SmartIri, KnoraCardinalityInfo)] = @@ -1360,34 +1362,33 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } _ <- hasCardinality match { - // If there is, check that the class isn't used in data, and that it has no subclasses. - case Some((propIri: SmartIri, cardinality: KnoraCardinalityInfo)) => - throwIfEntityIsUsed( - entityIri = internalClassIri, - errorFun = throw BadRequestException( - s"Cardinality ${cardinality.toString} for $propIri cannot be added to class ${addCardinalitiesRequest.classInfoContent.classIri}, because it is used in data or has a subclass" - ), - ignoreKnoraConstraints = - true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - ) - case None => Future.successful(()) - } + // If there is, check that the class isn't used in data, and that it has no subclasses. + case Some((propIri: SmartIri, cardinality: KnoraCardinalityInfo)) => + throwIfEntityIsUsed( + entityIri = internalClassIri, + errorFun = throw BadRequestException( + s"Cardinality ${cardinality.toString} for $propIri cannot be added to class ${addCardinalitiesRequest.classInfoContent.classIri}, because it is used in data or has a subclass" + ), + ignoreKnoraConstraints = + true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + ) + case None => Future.successful(()) + } // Make an updated class definition. newInternalClassDef = existingClassDef.copy( - directCardinalities = - existingClassDef.directCardinalities ++ internalClassDef.directCardinalities - ) + directCardinalities = existingClassDef.directCardinalities ++ internalClassDef.directCardinalities + ) // Check that the new cardinalities are valid, and add any inherited cardinalities. allBaseClassIrisWithoutInternal: Seq[SmartIri] = newInternalClassDef.subClassOf.toSeq.flatMap { baseClassIri => - cacheData.subClassOfRelations.getOrElse( - baseClassIri, - Seq.empty[SmartIri] - ) - } + cacheData.subClassOfRelations.getOrElse( + baseClassIri, + Seq.empty[SmartIri] + ) + } allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutInternal @@ -1402,12 +1403,12 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the class definition doesn't refer to any non-shared ontologies in other projects. _ = Cache.checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = newInternalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there // isn't any text to escape in cardinalities.) @@ -1420,121 +1421,121 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } readClassInfo = ReadClassInfoV2( - entityInfoContent = newInternalClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) - ), - linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkProp(propertyIri, cacheData) - ), - linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkValueProp(propertyIri, cacheData) - ), - fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isFileValueProp(propertyIri, cacheData) - ) - ) + entityInfoContent = newInternalClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) // Add the cardinalities to the class definition in the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .addCardinalitiesToClass( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - cardinalitiesToAdd = newInternalClassDefWithLinkValueProps.directCardinalities, - lastModificationDate = addCardinalitiesRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .addCardinalitiesToClass( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + cardinalitiesToAdd = newInternalClassDefWithLinkValueProps.directCardinalities, + lastModificationDate = addCardinalitiesRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. loadedClassDef <- OntologyHelpers.loadClassDefinition( - settings, - storeManager, - classIri = internalClassIri, - featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig - ) + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = addCardinalitiesRequest.featureFactoryConfig + ) _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update subclasses and write the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) _ = Cache.storeCacheData( - Cache.updateSubClasses( - baseClassIri = internalClassIri, - cacheData = cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + Cache.updateSubClasses( + baseClassIri = internalClassIri, + cacheData = cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) ) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = addCardinalitiesRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = addCardinalitiesRequest.requestingUser + ) } yield response } for { requestingUser <- FastFuture.successful(addCardinalitiesRequest.requestingUser) - externalClassIri = addCardinalitiesRequest.classInfoContent.classIri + externalClassIri = addCardinalitiesRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = addCardinalitiesRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = addCardinalitiesRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -1547,31 +1548,31 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon private def canChangeClassCardinalities( canChangeCardinalitiesRequest: CanChangeCardinalitiesRequestV2 ): Future[CanDoResponseV2] = { - val internalClassIri: SmartIri = canChangeCardinalitiesRequest.classIri.toOntologySchema(InternalSchema) + val internalClassIri: SmartIri = canChangeCardinalitiesRequest.classIri.toOntologySchema(InternalSchema) val internalOntologyIri: SmartIri = internalClassIri.getOntologyFromEntity for { cacheData <- Cache.getCacheData ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException( - s"Ontology ${canChangeCardinalitiesRequest.classIri.getOntologyFromEntity} does not exist" - ) - ) + internalOntologyIri, + throw BadRequestException( + s"Ontology ${canChangeCardinalitiesRequest.classIri.getOntologyFromEntity} does not exist" + ) + ) _ = if (!ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${canChangeCardinalitiesRequest.classIri} does not exist") - } + throw BadRequestException(s"Class ${canChangeCardinalitiesRequest.classIri} does not exist") + } userCanUpdateOntology <- OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canChangeCardinalitiesRequest.requestingUser) classIsUsed <- isEntityUsed( - entityIri = internalClassIri, - ignoreKnoraConstraints = - true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - ) + entityIri = internalClassIri, + ignoreKnoraConstraints = + true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + ) } yield CanDoResponseV2(userCanUpdateOntology && !classIsUsed) } @@ -1592,23 +1593,23 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeCardinalitiesRequest.lastModificationDate, - featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeCardinalitiesRequest.lastModificationDate, + featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. rdfType: SmartIri = internalClassDef.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that the class exists. @@ -1626,28 +1627,28 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // TODO: If class is used in data, check additionally if the property(ies) being removed is(are) truly used and if not, then allow. _ <- throwIfEntityIsUsed( - entityIri = internalClassIri, - errorFun = throw BadRequestException( - s"The cardinalities of class ${changeCardinalitiesRequest.classInfoContent.classIri} cannot be changed, because it is used in data or has a subclass" - ), - ignoreKnoraConstraints = - true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. - ) + entityIri = internalClassIri, + errorFun = throw BadRequestException( + s"The cardinalities of class ${changeCardinalitiesRequest.classInfoContent.classIri} cannot be changed, because it is used in data or has a subclass" + ), + ignoreKnoraConstraints = + true // It's OK if a property refers to the class via knora-base:subjectClassConstraint or knora-base:objectClassConstraint. + ) // Make an updated class definition. newInternalClassDef = existingClassDef.copy( - directCardinalities = internalClassDef.directCardinalities - ) + directCardinalities = internalClassDef.directCardinalities + ) // Check that the new cardinalities are valid, and don't add any inherited cardinalities. allBaseClassIrisWithoutInternal: Seq[SmartIri] = newInternalClassDef.subClassOf.toSeq.flatMap { baseClassIri => - cacheData.subClassOfRelations.getOrElse( - baseClassIri, - Seq.empty[SmartIri] - ) - } + cacheData.subClassOfRelations.getOrElse( + baseClassIri, + Seq.empty[SmartIri] + ) + } allBaseClassIris: Seq[SmartIri] = internalClassIri +: allBaseClassIrisWithoutInternal @@ -1661,12 +1662,12 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the class definition doesn't refer to any non-shared ontologies in other projects. _ = Cache.checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = newInternalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there // isn't any text to escape in cardinalities.) @@ -1679,118 +1680,118 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } readClassInfo = ReadClassInfoV2( - entityInfoContent = newInternalClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) - ), - linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkProp(propertyIri, cacheData) - ), - linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkValueProp(propertyIri, cacheData) - ), - fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isFileValueProp(propertyIri, cacheData) - ) - ) + entityInfoContent = newInternalClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) // Add the cardinalities to the class definition in the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .replaceClassCardinalities( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, - lastModificationDate = changeCardinalitiesRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .replaceClassCardinalities( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, + lastModificationDate = changeCardinalitiesRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. loadedClassDef <- OntologyHelpers.loadClassDefinition( - settings, - storeManager, - classIri = internalClassIri, - featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig - ) + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = changeCardinalitiesRequest.featureFactoryConfig + ) _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = changeCardinalitiesRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = changeCardinalitiesRequest.requestingUser + ) } yield response } for { requestingUser <- FastFuture.successful(changeCardinalitiesRequest.requestingUser) - externalClassIri = changeCardinalitiesRequest.classInfoContent.classIri + externalClassIri = changeCardinalitiesRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeCardinalitiesRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changeCardinalitiesRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -1807,31 +1808,31 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon for { requestingUser <- FastFuture.successful(canDeleteCardinalitiesFromClassRequest.requestingUser) - externalClassIri = canDeleteCardinalitiesFromClassRequest.classInfoContent.classIri + externalClassIri = canDeleteCardinalitiesFromClassRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = canDeleteCardinalitiesFromClassRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - Cardinalities.canDeleteCardinalitiesFromClass( - settings, - storeManager, - deleteCardinalitiesFromClassRequest = canDeleteCardinalitiesFromClassRequest, - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = canDeleteCardinalitiesFromClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + Cardinalities.canDeleteCardinalitiesFromClass( + settings, + storeManager, + deleteCardinalitiesFromClassRequest = canDeleteCardinalitiesFromClassRequest, + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult /** @@ -1847,31 +1848,31 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon for { requestingUser <- FastFuture.successful(deleteCardinalitiesFromClassRequest.requestingUser) - externalClassIri = deleteCardinalitiesFromClassRequest.classInfoContent.classIri + externalClassIri = deleteCardinalitiesFromClassRequest.classInfoContent.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteCardinalitiesFromClassRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - Cardinalities.deleteCardinalitiesFromClass( - settings, - storeManager, - deleteCardinalitiesFromClassRequest = deleteCardinalitiesFromClassRequest, - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deleteCardinalitiesFromClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + Cardinalities.deleteCardinalitiesFromClass( + settings, + storeManager, + deleteCardinalitiesFromClassRequest = deleteCardinalitiesFromClassRequest, + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult /** @@ -1881,7 +1882,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon * @return a [[CanDoResponseV2]]. */ private def canDeleteClass(canDeleteClassRequest: CanDeleteClassRequestV2): Future[CanDoResponseV2] = { - val internalClassIri: SmartIri = canDeleteClassRequest.classIri.toOntologySchema(InternalSchema) + val internalClassIri: SmartIri = canDeleteClassRequest.classIri.toOntologySchema(InternalSchema) val internalOntologyIri: SmartIri = internalClassIri.getOntologyFromEntity for { @@ -1894,8 +1895,8 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = if (!ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${canDeleteClassRequest.classIri} does not exist") - } + throw BadRequestException(s"Class ${canDeleteClassRequest.classIri} does not exist") + } userCanUpdateOntology <- OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canDeleteClassRequest.requestingUser) @@ -1916,65 +1917,65 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteClassRequest.lastModificationDate, - featureFactoryConfig = deleteClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteClassRequest.lastModificationDate, + featureFactoryConfig = deleteClassRequest.featureFactoryConfig + ) // Check that the class exists. ontology = cacheData.ontologies(internalOntologyIri) _ = if (!ontology.classes.contains(internalClassIri)) { - throw BadRequestException(s"Class ${deleteClassRequest.classIri} does not exist") - } + throw BadRequestException(s"Class ${deleteClassRequest.classIri} does not exist") + } // Check that the class isn't used in data or ontologies. _ <- throwIfEntityIsUsed( - entityIri = internalClassIri, - errorFun = throw BadRequestException( - s"Class ${deleteClassRequest.classIri} cannot be deleted, because it is used in data or ontologies" - ) - ) + entityIri = internalClassIri, + errorFun = throw BadRequestException( + s"Class ${deleteClassRequest.classIri} cannot be deleted, because it is used in data or ontologies" + ) + ) // Delete the class from the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .deleteClass( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - lastModificationDate = deleteClassRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .deleteClass( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + lastModificationDate = deleteClassRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = deleteClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = deleteClassRequest.featureFactoryConfig + ) // Update the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes - internalClassIri - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes - internalClassIri + ) updatedSubClassOfRelations = (cacheData.subClassOfRelations - internalClassIri).map { case (subClass, baseClasses) => @@ -1984,39 +1985,39 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon updatedSuperClassOfRelations = OntologyHelpers.calculateSuperClassOfRelations(updatedSubClassOfRelations) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subClassOfRelations = updatedSubClassOfRelations, - superClassOfRelations = updatedSuperClassOfRelations - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subClassOfRelations = updatedSubClassOfRelations, + superClassOfRelations = updatedSuperClassOfRelations + ) + ) } yield ReadOntologyMetadataV2(Set(updatedOntology.ontologyMetadata)) for { requestingUser <- FastFuture.successful(deleteClassRequest.requestingUser) - externalClassIri = deleteClassRequest.classIri + externalClassIri = deleteClassRequest.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteClassRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deleteClassRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -2034,11 +2035,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon cacheData <- Cache.getCacheData ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException( - s"Ontology ${canDeletePropertyRequest.propertyIri.getOntologyFromEntity} does not exist" - ) - ) + internalOntologyIri, + throw BadRequestException( + s"Ontology ${canDeletePropertyRequest.propertyIri.getOntologyFromEntity} does not exist" + ) + ) propertyDef: ReadPropertyInfoV2 = ontology.properties.getOrElse( @@ -2047,10 +2048,10 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = if (propertyDef.isLinkValueProp) { - throw BadRequestException( - s"A link value property cannot be deleted directly; check the corresponding link property instead" - ) - } + throw BadRequestException( + s"A link value property cannot be deleted directly; check the corresponding link property instead" + ) + } userCanUpdateOntology <- OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canDeletePropertyRequest.requestingUser) @@ -2071,12 +2072,12 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deletePropertyRequest.lastModificationDate, - featureFactoryConfig = deletePropertyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deletePropertyRequest.lastModificationDate, + featureFactoryConfig = deletePropertyRequest.featureFactoryConfig + ) // Check that the property exists. @@ -2088,77 +2089,78 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = if (propertyDef.isLinkValueProp) { - throw BadRequestException( - s"A link value property cannot be deleted directly; delete the corresponding link property instead" - ) - } + throw BadRequestException( + s"A link value property cannot be deleted directly; delete the corresponding link property instead" + ) + } - maybeInternalLinkValuePropertyIri: Option[SmartIri] = if (propertyDef.isLinkProp) { - Some(internalPropertyIri.fromLinkPropToLinkValueProp) - } else { - None - } + maybeInternalLinkValuePropertyIri: Option[SmartIri] = + if (propertyDef.isLinkProp) { + Some(internalPropertyIri.fromLinkPropToLinkValueProp) + } else { + None + } // Check that the property isn't used in data or ontologies. _ <- throwIfEntityIsUsed( - entityIri = internalPropertyIri, - errorFun = throw BadRequestException( - s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because it is used in data or ontologies" - ) - ) + entityIri = internalPropertyIri, + errorFun = throw BadRequestException( + s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because it is used in data or ontologies" + ) + ) _ <- maybeInternalLinkValuePropertyIri match { - case Some(internalLinkValuePropertyIri) => - throwIfEntityIsUsed( - entityIri = internalLinkValuePropertyIri, - errorFun = throw BadRequestException( - s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because the corresponding link value property, ${internalLinkValuePropertyIri - .toOntologySchema(ApiV2Complex)}, is used in data or ontologies" - ) - ) - - case None => FastFuture.successful(()) - } + case Some(internalLinkValuePropertyIri) => + throwIfEntityIsUsed( + entityIri = internalLinkValuePropertyIri, + errorFun = throw BadRequestException( + s"Property ${deletePropertyRequest.propertyIri} cannot be deleted, because the corresponding link value property, ${internalLinkValuePropertyIri + .toOntologySchema(ApiV2Complex)}, is used in data or ontologies" + ) + ) + + case None => FastFuture.successful(()) + } // Delete the property from the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .deleteProperty( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyIri = internalPropertyIri, - maybeLinkValuePropertyIri = maybeInternalLinkValuePropertyIri, - lastModificationDate = deletePropertyRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .deleteProperty( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyIri = internalPropertyIri, + maybeLinkValuePropertyIri = maybeInternalLinkValuePropertyIri, + lastModificationDate = deletePropertyRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = deletePropertyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = deletePropertyRequest.featureFactoryConfig + ) // Update the cache. propertiesToRemoveFromCache = Set(internalPropertyIri) ++ maybeInternalLinkValuePropertyIri updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - properties = ontology.properties -- propertiesToRemoveFromCache - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + properties = ontology.properties -- propertiesToRemoveFromCache + ) updatedSubPropertyOfRelations = (cacheData.subPropertyOfRelations -- propertiesToRemoveFromCache).map { case (subProperty, baseProperties) => @@ -2166,11 +2168,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subPropertyOfRelations = updatedSubPropertyOfRelations - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subPropertyOfRelations = updatedSubPropertyOfRelations + ) + ) } yield ReadOntologyMetadataV2(Set(updatedOntology.ontologyMetadata)) for { @@ -2180,24 +2182,24 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalOntologyIri = externalPropertyIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deletePropertyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deletePropertyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -2214,11 +2216,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon cacheData <- Cache.getCacheData ontology = cacheData.ontologies.getOrElse( - internalOntologyIri, - throw BadRequestException( - s"Ontology ${canDeleteOntologyRequest.ontologyIri.getOntologyFromEntity} does not exist" - ) - ) + internalOntologyIri, + throw BadRequestException( + s"Ontology ${canDeleteOntologyRequest.ontologyIri.getOntologyFromEntity} does not exist" + ) + ) userCanUpdateOntology <- OntologyHelpers.canUserUpdateOntology(internalOntologyIri, canDeleteOntologyRequest.requestingUser) @@ -2233,104 +2235,104 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the user has permission to update the ontology. _ <- OntologyHelpers.checkPermissionsForOntologyUpdate( - internalOntologyIri = internalOntologyIri, - requestingUser = deleteOntologyRequest.requestingUser - ) + internalOntologyIri = internalOntologyIri, + requestingUser = deleteOntologyRequest.requestingUser + ) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteOntologyRequest.lastModificationDate, - featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteOntologyRequest.lastModificationDate, + featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig + ) // Check that none of the entities in the ontology are used in data or in other ontologies. - ontology = cacheData.ontologies(internalOntologyIri) + ontology = cacheData.ontologies(internalOntologyIri) subjectsUsingOntology: Set[IRI] <- OntologyHelpers.getSubjectsUsingOntology(settings, storeManager, ontology) _ = if (subjectsUsingOntology.nonEmpty) { - val sortedSubjects: Seq[IRI] = subjectsUsingOntology.map(s => "<" + s + ">").toVector.sorted + val sortedSubjects: Seq[IRI] = subjectsUsingOntology.map(s => "<" + s + ">").toVector.sorted - throw BadRequestException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be deleted, because of subjects that refer to it: ${sortedSubjects - .mkString(", ")}" - ) - } + throw BadRequestException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} cannot be deleted, because of subjects that refer to it: ${sortedSubjects + .mkString(", ")}" + ) + } // Delete everything in the ontology's named graph. updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .deleteOntology( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri - ) - .toString() + .deleteOntology( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology has been deleted. maybeOntologyMetadata <- OntologyHelpers.loadOntologyMetadata( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = deleteOntologyRequest.featureFactoryConfig + ) _ = if (maybeOntologyMetadata.nonEmpty) { - throw UpdateNotPerformedException( - s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} was not deleted. Please report this as a possible bug." - ) - } + throw UpdateNotPerformedException( + s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} was not deleted. Please report this as a possible bug." + ) + } // Remove the ontology from the cache. updatedSubClassOfRelations = cacheData.subClassOfRelations.filterNot { case (subClass, _) => - subClass.getOntologyFromEntity == internalOntologyIri - }.map { case (subClass, baseClasses) => - subClass -> baseClasses.filterNot(_.getOntologyFromEntity == internalOntologyIri) - } + subClass.getOntologyFromEntity == internalOntologyIri + }.map { case (subClass, baseClasses) => + subClass -> baseClasses.filterNot(_.getOntologyFromEntity == internalOntologyIri) + } updatedSuperClassOfRelations = OntologyHelpers.calculateSuperClassOfRelations(updatedSubClassOfRelations) updatedSubPropertyOfRelations = cacheData.subPropertyOfRelations.filterNot { case (subProperty, _) => - subProperty.getOntologyFromEntity == internalOntologyIri - }.map { case (subProperty, baseProperties) => - subProperty -> baseProperties.filterNot( - _.getOntologyFromEntity == internalOntologyIri - ) - } + subProperty.getOntologyFromEntity == internalOntologyIri + }.map { case (subProperty, baseProperties) => + subProperty -> baseProperties.filterNot( + _.getOntologyFromEntity == internalOntologyIri + ) + } updatedStandoffProperties = cacheData.standoffProperties.filterNot(_.getOntologyFromEntity == internalOntologyIri) updatedCacheData = cacheData.copy( - ontologies = cacheData.ontologies - internalOntologyIri, - subClassOfRelations = updatedSubClassOfRelations, - superClassOfRelations = updatedSuperClassOfRelations, - subPropertyOfRelations = updatedSubPropertyOfRelations, - standoffProperties = updatedStandoffProperties - ) + ontologies = cacheData.ontologies - internalOntologyIri, + subClassOfRelations = updatedSubClassOfRelations, + superClassOfRelations = updatedSuperClassOfRelations, + subPropertyOfRelations = updatedSubPropertyOfRelations, + standoffProperties = updatedStandoffProperties + ) _ = Cache.storeCacheData(updatedCacheData) } yield SuccessResponseV2(s"Ontology ${internalOntologyIri.toOntologySchema(ApiV2Complex)} has been deleted") for { - _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(deleteOntologyRequest.ontologyIri) + _ <- OntologyHelpers.checkExternalOntologyIriForUpdate(deleteOntologyRequest.ontologyIri) internalOntologyIri = deleteOntologyRequest.ontologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = deleteOntologyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = deleteOntologyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -2343,203 +2345,187 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon private def createProperty(createPropertyRequest: CreatePropertyRequestV2): Future[ReadOntologyV2] = { def makeTaskFuture(internalPropertyIri: SmartIri, internalOntologyIri: SmartIri): Future[ReadOntologyV2] = { for { - cacheData <- Cache.getCacheData + cacheData <- Cache.getCacheData internalPropertyDef = createPropertyRequest.propertyInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = createPropertyRequest.lastModificationDate, - featureFactoryConfig = createPropertyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = createPropertyRequest.lastModificationDate, + featureFactoryConfig = createPropertyRequest.featureFactoryConfig + ) // Check that the property's rdf:type is owl:ObjectProperty. rdfType: SmartIri = internalPropertyDef.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.ObjectProperty.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that the property doesn't exist yet. ontology = cacheData.ontologies(internalOntologyIri) _ = if (ontology.properties.contains(internalPropertyIri)) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} already exists" - ) - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} already exists" + ) + } // Check that the property's IRI isn't already used for something else. _ = if (ontology.classes.contains(internalPropertyIri) || ontology.individuals.contains(internalPropertyIri)) { - throw BadRequestException(s"IRI ${createPropertyRequest.propertyInfoContent.propertyIri} is already used") - } + throw BadRequestException(s"IRI ${createPropertyRequest.propertyInfoContent.propertyIri} is already used") + } // Check that the base properties that have Knora IRIs are defined as Knora resource properties. knoraSuperProperties = internalPropertyDef.subPropertyOf.filter(_.isKnoraInternalEntityIri) invalidSuperProperties = knoraSuperProperties.filterNot(baseProperty => - OntologyHelpers - .isKnoraResourceProperty( - baseProperty, - cacheData - ) && baseProperty.toString != OntologyConstants.KnoraBase.ResourceProperty - ) + OntologyHelpers + .isKnoraResourceProperty( + baseProperty, + cacheData + ) && baseProperty.toString != OntologyConstants.KnoraBase.ResourceProperty + ) _ = if (invalidSuperProperties.nonEmpty) { - throw BadRequestException( - s"One or more specified base properties are invalid: ${invalidSuperProperties.mkString(", ")}" - ) - } + throw BadRequestException( + s"One or more specified base properties are invalid: ${invalidSuperProperties.mkString(", ")}" + ) + } // Check for rdfs:subPropertyOf cycles. allKnoraSuperPropertyIrisWithoutSelf: Set[SmartIri] = knoraSuperProperties.flatMap { superPropertyIri => - cacheData.subPropertyOfRelations.getOrElse( - superPropertyIri, - Set.empty[SmartIri] - ) - } + cacheData.subPropertyOfRelations.getOrElse( + superPropertyIri, + Set.empty[SmartIri] + ) + } _ = if (allKnoraSuperPropertyIrisWithoutSelf.contains(internalPropertyIri)) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would have a cyclical rdfs:subPropertyOf" - ) - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would have a cyclical rdfs:subPropertyOf" + ) + } // Check the property is a subproperty of knora-base:hasValue or knora-base:hasLinkTo, but not both. allKnoraSuperPropertyIris: Set[SmartIri] = allKnoraSuperPropertyIrisWithoutSelf + internalPropertyIri - isValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) - isLinkProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) + isValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) + isLinkProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) isLinkValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri) isFileValueProp = allKnoraSuperPropertyIris.contains(OntologyConstants.KnoraBase.HasFileValue.toSmartIri) _ = if (!(isValueProp || isLinkProp)) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would not be a subproperty of knora-api:hasValue or knora-api:hasLinkTo" - ) - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would not be a subproperty of knora-api:hasValue or knora-api:hasLinkTo" + ) + } _ = if (isValueProp && isLinkProp) { - throw BadRequestException( - s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would be a subproperty of both knora-api:hasValue and knora-api:hasLinkTo" - ) - } + throw BadRequestException( + s"Property ${createPropertyRequest.propertyInfoContent.propertyIri} would be a subproperty of both knora-api:hasValue and knora-api:hasLinkTo" + ) + } // Don't allow new file value properties to be created. _ = if (isFileValueProp) { - throw BadRequestException("New file value properties cannot be created") - } + throw BadRequestException("New file value properties cannot be created") + } // Don't allow new link value properties to be created directly, because we do that automatically when creating a link property. _ = if (isLinkValueProp) { - throw BadRequestException( - "New link value properties cannot be created directly. Create a link property instead." - ) - } + throw BadRequestException( + "New link value properties cannot be created directly. Create a link property instead." + ) + } // Check the property's salsah-gui:guiElement and salsah-gui:guiAttribute. _ = OntologyHelpers.validateGuiAttributes( - propertyInfoContent = internalPropertyDef, - allGuiAttributeDefinitions = cacheData.guiAttributeDefinitions, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + propertyInfoContent = internalPropertyDef, + allGuiAttributeDefinitions = cacheData.guiAttributeDefinitions, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // If we're creating a link property, make the definition of the corresponding link value property. - maybeLinkValuePropertyDef: Option[PropertyInfoContentV2] = if (isLinkProp) { - val linkValuePropertyDef = OntologyHelpers - .linkPropertyDefToLinkValuePropertyDef( - internalPropertyDef - ) - - if ( - ontology.properties.contains( - linkValuePropertyDef.propertyIri - ) - ) { - throw BadRequestException( - s"Link value property ${linkValuePropertyDef.propertyIri} already exists" - ) - } - - Some(linkValuePropertyDef) - } else { - None - } + maybeLinkValuePropertyDef: Option[PropertyInfoContentV2] = + if (isLinkProp) { + val linkValuePropertyDef = OntologyHelpers + .linkPropertyDefToLinkValuePropertyDef( + internalPropertyDef + ) + + if ( + ontology.properties.contains( + linkValuePropertyDef.propertyIri + ) + ) { + throw BadRequestException( + s"Link value property ${linkValuePropertyDef.propertyIri} already exists" + ) + } + + Some(linkValuePropertyDef) + } else { + None + } // Check that the subject class constraint, if provided, designates a Knora resource class that exists. maybeSubjectClassConstraintPred: Option[PredicateInfoV2] = internalPropertyDef.predicates.get(OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) maybeSubjectClassConstraint = maybeSubjectClassConstraintPred.map( - _.requireIriObject(throw BadRequestException("Invalid knora-api:subjectType")) - ) + _.requireIriObject(throw BadRequestException("Invalid knora-api:subjectType")) + ) _ = maybeSubjectClassConstraint.foreach { subjectClassConstraint => - if (!OntologyHelpers.isKnoraInternalResourceClass(subjectClassConstraint, cacheData)) { - throw BadRequestException( - s"Invalid subject class constraint: ${subjectClassConstraint.toOntologySchema(ApiV2Complex)}" - ) - } - } + if (!OntologyHelpers.isKnoraInternalResourceClass(subjectClassConstraint, cacheData)) { + throw BadRequestException( + s"Invalid subject class constraint: ${subjectClassConstraint.toOntologySchema(ApiV2Complex)}" + ) + } + } // Check that the object class constraint designates an appropriate class that exists. objectClassConstraint: SmartIri = internalPropertyDef.requireIriObject( - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - throw BadRequestException(s"No knora-api:objectType specified") - ) + OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + throw BadRequestException(s"No knora-api:objectType specified") + ) // If this is a value property, ensure its object class constraint is not LinkValue or a file value class. _ = if (!isLinkProp) { - if ( - objectClassConstraint.toString == OntologyConstants.KnoraBase.LinkValue || - OntologyConstants.KnoraBase.FileValueClasses.contains(objectClassConstraint.toString) - ) { - throw BadRequestException( - s"Invalid object class constraint for value property: ${objectClassConstraint.toOntologySchema(ApiV2Complex)}" - ) - } - } + if ( + objectClassConstraint.toString == OntologyConstants.KnoraBase.LinkValue || + OntologyConstants.KnoraBase.FileValueClasses.contains(objectClassConstraint.toString) + ) { + throw BadRequestException( + s"Invalid object class constraint for value property: ${objectClassConstraint.toOntologySchema(ApiV2Complex)}" + ) + } + } // Check that the subject class, if provided, is a subclass of the subject classes of the base properties. _ = maybeSubjectClassConstraint match { - case Some(subjectClassConstraint) => - Cache.checkPropertyConstraint( - cacheData = cacheData, - internalPropertyIri = internalPropertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, - constraintValueToBeChecked = subjectClassConstraint, - allSuperPropertyIris = allKnoraSuperPropertyIris, - errorSchema = ApiV2Complex, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) - - case None => () - } - - // Check that the object class is a subclass of the object classes of the base properties. - - _ = Cache.checkPropertyConstraint( + case Some(subjectClassConstraint) => + Cache.checkPropertyConstraint( cacheData = cacheData, internalPropertyIri = internalPropertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - constraintValueToBeChecked = objectClassConstraint, + constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, + constraintValueToBeChecked = subjectClassConstraint, allSuperPropertyIris = allKnoraSuperPropertyIris, errorSchema = ApiV2Complex, errorFun = { msg: String => @@ -2547,60 +2533,77 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } ) + case None => () + } + + // Check that the object class is a subclass of the object classes of the base properties. + + _ = Cache.checkPropertyConstraint( + cacheData = cacheData, + internalPropertyIri = internalPropertyIri, + constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + constraintValueToBeChecked = objectClassConstraint, + allSuperPropertyIris = allKnoraSuperPropertyIris, + errorSchema = ApiV2Complex, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) + // Check that the property definition doesn't refer to any non-shared ontologies in other projects. _ = Cache.checkOntologyReferencesInPropertyDef( - ontologyCacheData = cacheData, - propertyDef = internalPropertyDef, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + ontologyCacheData = cacheData, + propertyDef = internalPropertyDef, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Add the property (and the link value property if needed) to the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .createProperty( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyDef = internalPropertyDef, - maybeLinkValuePropertyDef = maybeLinkValuePropertyDef, - lastModificationDate = createPropertyRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .createProperty( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyDef = internalPropertyDef, + maybeLinkValuePropertyDef = maybeLinkValuePropertyDef, + lastModificationDate = createPropertyRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = createPropertyRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = createPropertyRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. loadedPropertyDef <- OntologyHelpers.loadPropertyDefinition( - settings, - storeManager, - propertyIri = internalPropertyIri, - featureFactoryConfig = createPropertyRequest.featureFactoryConfig - ) + settings, + storeManager, + propertyIri = internalPropertyIri, + featureFactoryConfig = createPropertyRequest.featureFactoryConfig + ) unescapedInputPropertyDef = internalPropertyDef.unescape _ = if (loadedPropertyDef != unescapedInputPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save property definition $unescapedInputPropertyDef, but $loadedPropertyDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save property definition $unescapedInputPropertyDef, but $loadedPropertyDef was saved" + ) + } maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = maybeLinkValuePropertyDef.map { linkValuePropertyDef => @@ -2617,24 +2620,24 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon maybeUnescapedNewLinkValuePropertyDef = maybeLinkValuePropertyDef.map(_.unescape) _ = (maybeLoadedLinkValuePropertyDef, maybeUnescapedNewLinkValuePropertyDef) match { - case (Some(loadedLinkValuePropertyDef), Some(unescapedNewLinkPropertyDef)) => - if (loadedLinkValuePropertyDef != unescapedNewLinkPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved" - ) - } - - case _ => () + case (Some(loadedLinkValuePropertyDef), Some(unescapedNewLinkPropertyDef)) => + if (loadedLinkValuePropertyDef != unescapedNewLinkPropertyDef) { + throw InconsistentRepositoryDataException( + s"Attempted to save link value property definition $unescapedNewLinkPropertyDef, but $loadedLinkValuePropertyDef was saved" + ) } + case _ => () + } + // Update the ontology cache, using the unescaped definition(s). readPropertyInfo = ReadPropertyInfoV2( - entityInfoContent = unescapedInputPropertyDef, - isEditable = true, - isResourceProp = true, - isLinkProp = isLinkProp - ) + entityInfoContent = unescapedInputPropertyDef, + isEditable = true, + isResourceProp = true, + isLinkProp = isLinkProp + ) maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = maybeUnescapedNewLinkValuePropertyDef.map { unescapedNewLinkPropertyDef => @@ -2647,8 +2650,8 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } updatedOntologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ) + lastModificationDate = Some(currentTime) + ) updatedOntology = ontology.copy( @@ -2658,19 +2661,19 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), - subPropertyOfRelations = - cacheData.subPropertyOfRelations + (internalPropertyIri -> allKnoraSuperPropertyIris) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology), + subPropertyOfRelations = + cacheData.subPropertyOfRelations + (internalPropertyIri -> allKnoraSuperPropertyIris) + ) + ) // Read the data back from the cache. response <- getPropertyDefinitionsFromOntologyV2( - propertyIris = Set(internalPropertyIri), - allLanguages = true, - requestingUser = createPropertyRequest.requestingUser - ) + propertyIris = Set(internalPropertyIri), + allLanguages = true, + requestingUser = createPropertyRequest.requestingUser + ) } yield response } @@ -2681,24 +2684,24 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalOntologyIri = externalPropertyIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = createPropertyRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = createPropertyRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -2725,70 +2728,70 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changePropertyGuiElementRequest.lastModificationDate, - featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changePropertyGuiElementRequest.lastModificationDate, + featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig + ) // If this is a link property, also change the GUI element and attribute of the corresponding link value property. - maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = if (currentReadPropertyInfo.isLinkProp) { - val linkValuePropertyIri = - internalPropertyIri.fromLinkPropToLinkValueProp - Some( - ontology.properties.getOrElse( - linkValuePropertyIri, - throw InconsistentRepositoryDataException( - s"Link value property $linkValuePropertyIri not found" - ) - ) - ) - } else { - None - } + maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = + if (currentReadPropertyInfo.isLinkProp) { + val linkValuePropertyIri = + internalPropertyIri.fromLinkPropToLinkValueProp + Some( + ontology.properties.getOrElse( + linkValuePropertyIri, + throw InconsistentRepositoryDataException( + s"Link value property $linkValuePropertyIri not found" + ) + ) + ) + } else { + None + } // Do the update. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changePropertyGuiElement( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyIri = internalPropertyIri, - maybeLinkValuePropertyIri = - maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), - maybeNewGuiElement = changePropertyGuiElementRequest.newGuiElement, - newGuiAttributes = changePropertyGuiElementRequest.newGuiAttributes, - lastModificationDate = changePropertyGuiElementRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changePropertyGuiElement( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyIri = internalPropertyIri, + maybeLinkValuePropertyIri = maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), + maybeNewGuiElement = changePropertyGuiElementRequest.newGuiElement, + newGuiAttributes = changePropertyGuiElementRequest.newGuiAttributes, + lastModificationDate = changePropertyGuiElementRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. loadedPropertyDef <- OntologyHelpers.loadPropertyDefinition( - settings, - storeManager, - propertyIri = internalPropertyIri, - featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig - ) + settings, + storeManager, + propertyIri = internalPropertyIri, + featureFactoryConfig = changePropertyGuiElementRequest.featureFactoryConfig + ) maybeNewGuiElementPredicate: Option[(SmartIri, PredicateInfoV2)] = changePropertyGuiElementRequest.newGuiElement.map { guiElement: SmartIri => @@ -2811,19 +2814,18 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } unescapedNewPropertyDef: PropertyInfoContentV2 = currentReadPropertyInfo.entityInfoContent.copy( - predicates = - currentReadPropertyInfo.entityInfoContent.predicates - - OntologyConstants.SalsahGui.GuiElementProp.toSmartIri - - OntologyConstants.SalsahGui.GuiAttribute.toSmartIri ++ - maybeNewGuiElementPredicate ++ - maybeUnescapedNewGuiAttributePredicate - ) + predicates = currentReadPropertyInfo.entityInfoContent.predicates - + OntologyConstants.SalsahGui.GuiElementProp.toSmartIri - + OntologyConstants.SalsahGui.GuiAttribute.toSmartIri ++ + maybeNewGuiElementPredicate ++ + maybeUnescapedNewGuiAttributePredicate + ) _ = if (loadedPropertyDef != unescapedNewPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved" + ) + } maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = maybeCurrentLinkValueReadPropertyInfo.map { linkValueReadPropertyInfo => @@ -2860,11 +2862,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Update the ontology cache, using the unescaped definition(s). newReadPropertyInfo = ReadPropertyInfoV2( - entityInfoContent = unescapedNewPropertyDef, - isEditable = true, - isResourceProp = true, - isLinkProp = currentReadPropertyInfo.isLinkProp - ) + entityInfoContent = unescapedNewPropertyDef, + isEditable = true, + isResourceProp = true, + isLinkProp = currentReadPropertyInfo.isLinkProp + ) maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = maybeUnescapedNewLinkValuePropertyDef.map { unescapedNewLinkPropertyDef => @@ -2876,8 +2878,8 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } updatedOntologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ) + lastModificationDate = Some(currentTime) + ) updatedOntology = ontology.copy( @@ -2887,18 +2889,18 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getPropertyDefinitionsFromOntologyV2( - propertyIris = Set(internalPropertyIri), - allLanguages = true, - requestingUser = changePropertyGuiElementRequest.requestingUser - ) + propertyIris = Set(internalPropertyIri), + allLanguages = true, + requestingUser = changePropertyGuiElementRequest.requestingUser + ) } yield response } @@ -2909,24 +2911,24 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalOntologyIri = externalPropertyIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changePropertyGuiElementRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changePropertyGuiElementRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -2953,70 +2955,70 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, - featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, + featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig + ) // If this is a link property, also change the labels/comments of the corresponding link value property. - maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = if (currentReadPropertyInfo.isLinkProp) { - val linkValuePropertyIri = - internalPropertyIri.fromLinkPropToLinkValueProp - Some( - ontology.properties.getOrElse( - linkValuePropertyIri, - throw InconsistentRepositoryDataException( - s"Link value property $linkValuePropertyIri not found" - ) - ) - ) - } else { - None - } + maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = + if (currentReadPropertyInfo.isLinkProp) { + val linkValuePropertyIri = + internalPropertyIri.fromLinkPropToLinkValueProp + Some( + ontology.properties.getOrElse( + linkValuePropertyIri, + throw InconsistentRepositoryDataException( + s"Link value property $linkValuePropertyIri not found" + ) + ) + ) + } else { + None + } // Do the update. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changePropertyLabelsOrComments( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - propertyIri = internalPropertyIri, - maybeLinkValuePropertyIri = - maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), - predicateToUpdate = changePropertyLabelsOrCommentsRequest.predicateToUpdate, - newObjects = changePropertyLabelsOrCommentsRequest.newObjects, - lastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changePropertyLabelsOrComments( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + propertyIri = internalPropertyIri, + maybeLinkValuePropertyIri = maybeCurrentLinkValueReadPropertyInfo.map(_.entityInfoContent.propertyIri), + predicateToUpdate = changePropertyLabelsOrCommentsRequest.predicateToUpdate, + newObjects = changePropertyLabelsOrCommentsRequest.newObjects, + lastModificationDate = changePropertyLabelsOrCommentsRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings = settings, - storeManager = storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig - ) + settings = settings, + storeManager = storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. loadedPropertyDef <- OntologyHelpers.loadPropertyDefinition( - settings, - storeManager, - propertyIri = internalPropertyIri, - featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig - ) + settings, + storeManager, + propertyIri = internalPropertyIri, + featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig + ) unescapedNewLabelOrCommentPredicate: PredicateInfoV2 = PredicateInfoV2( @@ -3031,10 +3033,10 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = if (loadedPropertyDef != unescapedNewPropertyDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save property definition $unescapedNewPropertyDef, but $loadedPropertyDef was saved" + ) + } maybeLoadedLinkValuePropertyDefFuture: Option[Future[PropertyInfoContentV2]] = maybeCurrentLinkValueReadPropertyInfo.map { linkValueReadPropertyInfo => @@ -3068,11 +3070,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Update the ontology cache, using the unescaped definition(s). newReadPropertyInfo = ReadPropertyInfoV2( - entityInfoContent = unescapedNewPropertyDef, - isEditable = true, - isResourceProp = true, - isLinkProp = currentReadPropertyInfo.isLinkProp - ) + entityInfoContent = unescapedNewPropertyDef, + isEditable = true, + isResourceProp = true, + isLinkProp = currentReadPropertyInfo.isLinkProp + ) maybeLinkValuePropertyCacheEntry: Option[(SmartIri, ReadPropertyInfoV2)] = maybeUnescapedNewLinkValuePropertyDef.map { unescapedNewLinkPropertyDef => @@ -3084,8 +3086,8 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon } updatedOntologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ) + lastModificationDate = Some(currentTime) + ) updatedOntology = ontology.copy( @@ -3095,18 +3097,18 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getPropertyDefinitionsFromOntologyV2( - propertyIris = Set(internalPropertyIri), - allLanguages = true, - requestingUser = changePropertyLabelsOrCommentsRequest.requestingUser - ) + propertyIris = Set(internalPropertyIri), + allLanguages = true, + requestingUser = changePropertyLabelsOrCommentsRequest.requestingUser + ) } yield response } @@ -3117,24 +3119,24 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon externalOntologyIri = externalPropertyIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalPropertyIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalPropertyIri, + requestingUser = requestingUser + ) internalPropertyIri = externalPropertyIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changePropertyLabelsOrCommentsRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalPropertyIri = internalPropertyIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changePropertyLabelsOrCommentsRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalPropertyIri = internalPropertyIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } @@ -3160,57 +3162,56 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, - featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, + featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig + ) // Do the update. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .changeClassLabelsOrComments( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - predicateToUpdate = changeClassLabelsOrCommentsRequest.predicateToUpdate, - newObjects = changeClassLabelsOrCommentsRequest.newObjects, - lastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .changeClassLabelsOrComments( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + predicateToUpdate = changeClassLabelsOrCommentsRequest.predicateToUpdate, + newObjects = changeClassLabelsOrCommentsRequest.newObjects, + lastModificationDate = changeClassLabelsOrCommentsRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. To make this comparison, // we have to undo the SPARQL-escaping of the input. loadedClassDef: ClassInfoContentV2 <- OntologyHelpers.loadClassDefinition( - settings, - storeManager, - classIri = internalClassIri, - featureFactoryConfig = - changeClassLabelsOrCommentsRequest.featureFactoryConfig - ) + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig + ) unescapedNewLabelOrCommentPredicate = PredicateInfoV2( - predicateIri = changeClassLabelsOrCommentsRequest.predicateToUpdate, - objects = changeClassLabelsOrCommentsRequest.newObjects - ).unescape + predicateIri = changeClassLabelsOrCommentsRequest.predicateToUpdate, + objects = changeClassLabelsOrCommentsRequest.newObjects + ).unescape unescapedNewClassDef: ClassInfoContentV2 = currentReadClassInfo.entityInfoContent.copy( @@ -3219,64 +3220,64 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon ) _ = if (loadedClassDef != unescapedNewClassDef) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $unescapedNewClassDef, but $loadedClassDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $unescapedNewClassDef, but $loadedClassDef was saved" + ) + } // Update the ontology cache, using the unescaped definition(s). newReadClassInfo = currentReadClassInfo.copy( - entityInfoContent = unescapedNewClassDef - ) + entityInfoContent = unescapedNewClassDef + ) updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> newReadClassInfo) - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> newReadClassInfo) + ) _ = Cache.storeCacheData( - cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) + ) + ) // Read the data back from the cache. response <- getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = changeClassLabelsOrCommentsRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = changeClassLabelsOrCommentsRequest.requestingUser + ) } yield response for { requestingUser <- FastFuture.successful(changeClassLabelsOrCommentsRequest.requestingUser) - externalClassIri = changeClassLabelsOrCommentsRequest.classIri + externalClassIri = changeClassLabelsOrCommentsRequest.classIri externalOntologyIri = externalClassIri.getOntologyFromEntity _ <- OntologyHelpers.checkOntologyAndEntityIrisForUpdate( - externalOntologyIri = externalOntologyIri, - externalEntityIri = externalClassIri, - requestingUser = requestingUser - ) + externalOntologyIri = externalOntologyIri, + externalEntityIri = externalClassIri, + requestingUser = requestingUser + ) - internalClassIri = externalClassIri.toOntologySchema(InternalSchema) + internalClassIri = externalClassIri.toOntologySchema(InternalSchema) internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) // Do the remaining pre-update checks and the update while holding a global ontology cache lock. taskResult <- IriLocker.runWithIriLock( - apiRequestID = changeClassLabelsOrCommentsRequest.apiRequestID, - iri = ONTOLOGY_CACHE_LOCK_IRI, - task = () => - makeTaskFuture( - internalClassIri = internalClassIri, - internalOntologyIri = internalOntologyIri - ) - ) + apiRequestID = changeClassLabelsOrCommentsRequest.apiRequestID, + iri = ONTOLOGY_CACHE_LOCK_IRI, + task = () => + makeTaskFuture( + internalClassIri = internalClassIri, + internalOntologyIri = internalOntologyIri + ) + ) } yield taskResult } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourceUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourceUtilV2.scala index 53c6a145fc..62bbcb9986 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourceUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourceUtilV2.scala @@ -46,19 +46,21 @@ import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} /** - * Utility functions for working with Knora resources and their values. - */ + * Utility functions for working with Knora resources and their values. + */ object ResourceUtilV2 { /** - * Checks that a user has the specified permission on a resource. - * - * @param resourceInfo the resource to be updated. - * @param requestingUser the requesting user. - */ - def checkResourcePermission(resourceInfo: ReadResourceV2, - permissionNeeded: EntityPermission, - requestingUser: UserADM): Unit = { + * Checks that a user has the specified permission on a resource. + * + * @param resourceInfo the resource to be updated. + * @param requestingUser the requesting user. + */ + def checkResourcePermission( + resourceInfo: ReadResourceV2, + permissionNeeded: EntityPermission, + requestingUser: UserADM + ): Unit = { val maybeUserPermission: Option[EntityPermission] = PermissionUtilADM.getUserPermissionADM( entityCreator = resourceInfo.attachedToUser, entityProject = resourceInfo.projectADM.id, @@ -73,21 +75,24 @@ object ResourceUtilV2 { if (!hasRequiredPermission) { throw ForbiddenException( - s"User ${requestingUser.email} does not have ${permissionNeeded.getName} on resource <${resourceInfo.resourceIri}>") + s"User ${requestingUser.email} does not have ${permissionNeeded.getName} on resource <${resourceInfo.resourceIri}>" + ) } } /** - * Checks that a user has the specified permission on a value. - * - * @param resourceInfo the resource containing the value. - * @param valueInfo the value to be updated. - * @param requestingUser the requesting user. - */ - def checkValuePermission(resourceInfo: ReadResourceV2, - valueInfo: ReadValueV2, - permissionNeeded: EntityPermission, - requestingUser: UserADM): Unit = { + * Checks that a user has the specified permission on a value. + * + * @param resourceInfo the resource containing the value. + * @param valueInfo the value to be updated. + * @param requestingUser the requesting user. + */ + def checkValuePermission( + resourceInfo: ReadResourceV2, + valueInfo: ReadValueV2, + permissionNeeded: EntityPermission, + requestingUser: UserADM + ): Unit = { val maybeUserPermission: Option[EntityPermission] = PermissionUtilADM.getUserPermissionADM( entityCreator = valueInfo.attachedToUser, entityProject = resourceInfo.projectADM.id, @@ -102,25 +107,27 @@ object ResourceUtilV2 { if (!hasRequiredPermission) { throw ForbiddenException( - s"User ${requestingUser.email} does not have ${permissionNeeded.getName} on value <${valueInfo.valueIri}>") + s"User ${requestingUser.email} does not have ${permissionNeeded.getName} on value <${valueInfo.valueIri}>" + ) } } /** - * Gets the default permissions for a new value. - * - * @param projectIri the IRI of the project of the containing resource. - * @param resourceClassIri the internal IRI of the resource class. - * @param propertyIri the internal IRI of the property that points to the value. - * @param requestingUser the user that is creating the value. - * @return a permission string. - */ + * Gets the default permissions for a new value. + * + * @param projectIri the IRI of the project of the containing resource. + * @param resourceClassIri the internal IRI of the resource class. + * @param propertyIri the internal IRI of the property that points to the value. + * @param requestingUser the user that is creating the value. + * @return a permission string. + */ def getDefaultValuePermissions( - projectIri: IRI, - resourceClassIri: SmartIri, - propertyIri: SmartIri, - requestingUser: UserADM, - responderManager: ActorRef)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[String] = { + projectIri: IRI, + resourceClassIri: SmartIri, + propertyIri: SmartIri, + requestingUser: UserADM, + responderManager: ActorRef + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[String] = for { defaultObjectAccessPermissionsResponse: DefaultObjectAccessPermissionsStringResponseADM <- { responderManager ? DefaultObjectAccessPermissionsStringForPropertyGetADM( @@ -132,21 +139,22 @@ object ResourceUtilV2 { ) }.mapTo[DefaultObjectAccessPermissionsStringResponseADM] } yield defaultObjectAccessPermissionsResponse.permissionLiteral - } /** - * Checks whether a list node exists, and throws [[NotFoundException]] otherwise. - * - * @param listNodeIri the IRI of the list node. - */ - def checkListNodeExists(listNodeIri: IRI, storeManager: ActorRef)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Unit] = { + * Checks whether a list node exists, and throws [[NotFoundException]] otherwise. + * + * @param listNodeIri the IRI of the list node. + */ + def checkListNodeExists(listNodeIri: IRI, storeManager: ActorRef)(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[Unit] = for { askString <- Future( org.knora.webapi.messages.twirl.queries.sparql.admin.txt .checkListNodeExistsByIri(listNodeIri = listNodeIri) - .toString) + .toString + ) checkListNodeExistsResponse <- (storeManager ? SparqlAskRequest(askString)).mapTo[SparqlAskResponse] @@ -154,25 +162,25 @@ object ResourceUtilV2 { throw NotFoundException(s"<$listNodeIri> does not exist or is not a ListNode") } } yield () - } /** - * Given a future representing an operation that was supposed to update a value in a triplestore, checks whether - * the updated value was a file value. If not, this method returns the same future. If it was a file value, this - * method checks whether the update was successful. If so, it asks Sipi to move the file to permanent storage. - * If not, it asks Sipi to delete the temporary file. - * - * @param updateFuture the future that should have updated the triplestore. - * @param valueContent the value that should have been created or updated. - * @param requestingUser the user making the request. - */ + * Given a future representing an operation that was supposed to update a value in a triplestore, checks whether + * the updated value was a file value. If not, this method returns the same future. If it was a file value, this + * method checks whether the update was successful. If so, it asks Sipi to move the file to permanent storage. + * If not, it asks Sipi to delete the temporary file. + * + * @param updateFuture the future that should have updated the triplestore. + * @param valueContent the value that should have been created or updated. + * @param requestingUser the user making the request. + */ def doSipiPostUpdate[T <: UpdateResultInProject]( - updateFuture: Future[T], - valueContent: ValueContentV2, - requestingUser: UserADM, - responderManager: ActorRef, - storeManager: ActorRef, - log: Logger)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[T] = { + updateFuture: Future[T], + valueContent: ValueContentV2, + requestingUser: UserADM, + responderManager: ActorRef, + storeManager: ActorRef, + log: Logger + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[T] = // Was this a file value update? valueContent match { case fileValueContent: FileValueContentV2 => @@ -215,5 +223,4 @@ object ResourceUtilV2 { // This wasn't a file value update. Return the future we were given. updateFuture } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala index c5deb94310..a6460d6b83 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala @@ -85,59 +85,71 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt implicit val materializer: Materializer = Materializer.matFromSystem(system) /** - * Represents a resource that is ready to be created and whose contents can be verified afterwards. - * - * @param sparqlTemplateResourceToCreate a [[SparqlTemplateResourceToCreate]] describing SPARQL for creating - * the resource. - * @param values the resource's values for verification. - * @param hasStandoffLink `true` if the property `knora-base:hasStandoffLinkToValue` was automatically added. - */ - private case class ResourceReadyToCreate(sparqlTemplateResourceToCreate: SparqlTemplateResourceToCreate, - values: Map[SmartIri, Seq[UnverifiedValueV2]], - hasStandoffLink: Boolean) + * Represents a resource that is ready to be created and whose contents can be verified afterwards. + * + * @param sparqlTemplateResourceToCreate a [[SparqlTemplateResourceToCreate]] describing SPARQL for creating + * the resource. + * @param values the resource's values for verification. + * @param hasStandoffLink `true` if the property `knora-base:hasStandoffLinkToValue` was automatically added. + */ + private case class ResourceReadyToCreate( + sparqlTemplateResourceToCreate: SparqlTemplateResourceToCreate, + values: Map[SmartIri, Seq[UnverifiedValueV2]], + hasStandoffLink: Boolean + ) /** - * Receives a message of type [[ResourcesResponderRequestV2]], and returns an appropriate response message. - */ + * Receives a message of type [[ResourcesResponderRequestV2]], and returns an appropriate response message. + */ def receive(msg: ResourcesResponderRequestV2) = msg match { - case ResourcesGetRequestV2(resIris, - propertyIri, - valueUuid, - versionDate, - withDeleted, - targetSchema, - schemaOptions, - featureFactoryConfig, - requestingUser) => - getResourcesV2(resIris, - propertyIri, - valueUuid, - versionDate, - withDeleted, - targetSchema, - schemaOptions, - featureFactoryConfig, - requestingUser) - case ResourcesPreviewGetRequestV2(resIris, - withDeletedResource, - targetSchema, - featureFactoryConfig, - requestingUser) => + case ResourcesGetRequestV2( + resIris, + propertyIri, + valueUuid, + versionDate, + withDeleted, + targetSchema, + schemaOptions, + featureFactoryConfig, + requestingUser + ) => + getResourcesV2( + resIris, + propertyIri, + valueUuid, + versionDate, + withDeleted, + targetSchema, + schemaOptions, + featureFactoryConfig, + requestingUser + ) + case ResourcesPreviewGetRequestV2( + resIris, + withDeletedResource, + targetSchema, + featureFactoryConfig, + requestingUser + ) => getResourcePreviewV2(resIris, withDeletedResource, targetSchema, featureFactoryConfig, requestingUser) - case ResourceTEIGetRequestV2(resIri, - textProperty, - mappingIri, - gravsearchTemplateIri, - headerXSLTIri, - featureFactoryConfig, - requestingUser) => - getResourceAsTeiV2(resIri, - textProperty, - mappingIri, - gravsearchTemplateIri, - headerXSLTIri, - featureFactoryConfig, - requestingUser) + case ResourceTEIGetRequestV2( + resIri, + textProperty, + mappingIri, + gravsearchTemplateIri, + headerXSLTIri, + featureFactoryConfig, + requestingUser + ) => + getResourceAsTeiV2( + resIri, + textProperty, + mappingIri, + gravsearchTemplateIri, + headerXSLTIri, + featureFactoryConfig, + requestingUser + ) case createResourceRequestV2: CreateResourceRequestV2 => createResourceV2(createResourceRequestV2) @@ -165,11 +177,11 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Creates a new resource. - * - * @param createResourceRequestV2 the request to create the resource. - * @return a [[ReadResourcesSequenceV2]] containing a preview of the resource. - */ + * Creates a new resource. + * + * @param createResourceRequestV2 the request to create the resource. + * @return a [[ReadResourcesSequenceV2]] containing a preview of the resource. + */ private def createResourceV2(createResourceRequestV2: CreateResourceRequestV2): Future[ReadResourcesSequenceV2] = { def makeTaskFuture(resourceIri: IRI): Future[ReadResourcesSequenceV2] = { @@ -183,7 +195,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Convert the resource to the internal ontology schema. internalCreateResource: CreateResourceV2 <- Future( - createResourceRequestV2.createResource.toOntologySchema(InternalSchema)) + createResourceRequestV2.createResource.toOntologySchema(InternalSchema) + ) // Check link targets and list nodes that should exist. @@ -212,7 +225,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt )).mapTo[EntityInfoGetResponseV2] resourceClassInfo: ReadClassInfoV2 = resourceClassEntityInfoResponse.classInfoMap( - internalCreateResource.resourceClassIri) + internalCreateResource.resourceClassIri + ) propertyEntityInfoResponse: EntityInfoGetResponseV2 <- (responderManager ? EntityInfoGetRequestV2( propertyIris = resourceClassInfo.knoraResourceProperties, @@ -238,12 +252,14 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt defaultPropertyPermissionsMap: Map[SmartIri, Map[SmartIri, String]] <- getDefaultPropertyPermissions( projectIri = createResourceRequestV2.createResource.projectADM.id, - resourceClassProperties = Map(internalCreateResource.resourceClassIri -> internalCreateResource.values.keySet), + resourceClassProperties = + Map(internalCreateResource.resourceClassIri -> internalCreateResource.values.keySet), requestingUser = createResourceRequestV2.requestingUser ) defaultPropertyPermissions: Map[SmartIri, String] = defaultPropertyPermissionsMap( - internalCreateResource.resourceClassIri) + internalCreateResource.resourceClassIri + ) // Make a versionDate for the resource and its values. creationDate: Instant = internalCreateResource.creationDate.getOrElse(Instant.now) @@ -304,7 +320,9 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt projectIri = createResourceRequestV2.createResource.projectADM.id - _ = if (projectIri == OntologyConstants.KnoraAdmin.SystemProject || projectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject) { + _ = if ( + projectIri == OntologyConstants.KnoraAdmin.SystemProject || projectIri == OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { throw BadRequestException(s"Resources cannot be created in project <$projectIri>") } @@ -313,16 +331,20 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt resourceClassOntologyIri: SmartIri = createResourceRequestV2.createResource.resourceClassIri.getOntologyFromEntity readOntologyMetadataV2: ReadOntologyMetadataV2 <- (responderManager ? OntologyMetadataGetByIriRequestV2( Set(resourceClassOntologyIri), - createResourceRequestV2.requestingUser)).mapTo[ReadOntologyMetadataV2] + createResourceRequestV2.requestingUser + )).mapTo[ReadOntologyMetadataV2] ontologyMetadata: OntologyMetadataV2 = readOntologyMetadataV2.ontologies.headOption .getOrElse(throw BadRequestException(s"Ontology $resourceClassOntologyIri not found")) ontologyProjectIri: IRI = ontologyMetadata.projectIri .getOrElse(throw InconsistentRepositoryDataException(s"Ontology $resourceClassOntologyIri has no project")) .toString - _ = if (projectIri != ontologyProjectIri && !(ontologyMetadata.ontologyIri.isKnoraBuiltInDefinitionIri || ontologyMetadata.ontologyIri.isKnoraSharedDefinitionIri)) { + _ = if ( + projectIri != ontologyProjectIri && !(ontologyMetadata.ontologyIri.isKnoraBuiltInDefinitionIri || ontologyMetadata.ontologyIri.isKnoraSharedDefinitionIri) + ) { throw BadRequestException( - s"Cannot create a resource in project <$projectIri> with resource class <${createResourceRequestV2.createResource.resourceClassIri}>, which is defined in a non-shared ontology in another project") + s"Cannot create a resource in project <$projectIri> with resource class <${createResourceRequestV2.createResource.resourceClassIri}>, which is defined in a non-shared ontology in another project" + ) } // Check user's PermissionProfile (part of UserADM) to see if the user has the permission to @@ -331,17 +353,22 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt internalResourceClassIri: SmartIri = createResourceRequestV2.createResource.resourceClassIri .toOntologySchema(InternalSchema) - _ = if (!createResourceRequestV2.requestingUser.permissions.hasPermissionFor( - ResourceCreateOperation(internalResourceClassIri.toString), - projectIri, - None)) { + _ = if ( + !createResourceRequestV2.requestingUser.permissions.hasPermissionFor( + ResourceCreateOperation(internalResourceClassIri.toString), + projectIri, + None + ) + ) { throw ForbiddenException( - s"User ${createResourceRequestV2.requestingUser.username} does not have permission to create a resource of class <${createResourceRequestV2.createResource.resourceClassIri}> in project <$projectIri>") + s"User ${createResourceRequestV2.requestingUser.username} does not have permission to create a resource of class <${createResourceRequestV2.createResource.resourceClassIri}> in project <$projectIri>" + ) } resourceIri: IRI <- checkOrCreateEntityIri( createResourceRequestV2.createResource.resourceIri, - stringFormatter.makeRandomResourceIri(createResourceRequestV2.createResource.projectADM.shortcode)) + stringFormatter.makeRandomResourceIri(createResourceRequestV2.createResource.projectADM.shortcode) + ) // Do the remaining pre-update checks and the update while holding an update lock on the resource to be created. taskResult <- IriLocker.runWithIriLock( @@ -361,13 +388,14 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Updates a resources metadata. - * - * @param updateResourceMetadataRequestV2 the update request. - * @return a [[UpdateResourceMetadataResponseV2]]. - */ + * Updates a resources metadata. + * + * @param updateResourceMetadataRequestV2 the update request. + * @return a [[UpdateResourceMetadataResponseV2]]. + */ private def updateResourceMetadataV2( - updateResourceMetadataRequestV2: UpdateResourceMetadataRequestV2): Future[UpdateResourceMetadataResponseV2] = { + updateResourceMetadataRequestV2: UpdateResourceMetadataRequestV2 + ): Future[UpdateResourceMetadataResponseV2] = { def makeTaskFuture: Future[UpdateResourceMetadataResponseV2] = { for { // Get the metadata of the resource to be updated. @@ -384,19 +412,25 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Make sure that the resource's class is what the client thinks it is. _ = if (resource.resourceClassIri != internalResourceClassIri) { throw BadRequestException( - s"Resource <${resource.resourceIri}> is not a member of class <${updateResourceMetadataRequestV2.resourceClassIri}>") + s"Resource <${resource.resourceIri}> is not a member of class <${updateResourceMetadataRequestV2.resourceClassIri}>" + ) } // If resource has already been modified, make sure that its lastModificationDate is given in the request body. - _ = if (resource.lastModificationDate.nonEmpty && updateResourceMetadataRequestV2.maybeLastModificationDate.isEmpty) { + _ = if ( + resource.lastModificationDate.nonEmpty && updateResourceMetadataRequestV2.maybeLastModificationDate.isEmpty + ) { throw EditConflictException( s"Resource <${resource.resourceIri}> has been modified in the past. Its lastModificationDate " + - s"${resource.lastModificationDate.get} must be included in the request body.") + s"${resource.lastModificationDate.get} must be included in the request body." + ) } // Make sure that the resource hasn't been updated since the client got its last modification date. - _ = if (updateResourceMetadataRequestV2.maybeLastModificationDate.nonEmpty && - resource.lastModificationDate != updateResourceMetadataRequestV2.maybeLastModificationDate) { + _ = if ( + updateResourceMetadataRequestV2.maybeLastModificationDate.nonEmpty && + resource.lastModificationDate != updateResourceMetadataRequestV2.maybeLastModificationDate + ) { throw EditConflictException(s"Resource <${resource.resourceIri}> has been modified since you last read it") } @@ -414,7 +448,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case Some(submittedNewModificationDate) => if (resource.lastModificationDate.exists(_.isAfter(submittedNewModificationDate))) { throw BadRequestException( - s"Submitted knora-api:newModificationDate is before the resource's current knora-api:lastModificationDate") + s"Submitted knora-api:newModificationDate is before the resource's current knora-api:lastModificationDate" + ) } else { submittedNewModificationDate } @@ -455,7 +490,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt _ = if (!updatedResource.lastModificationDate.contains(newModificationDate)) { throw UpdateNotPerformedException( - s"Updated resource has last modification date ${updatedResource.lastModificationDate}, expected $newModificationDate") + s"Updated resource has last modification date ${updatedResource.lastModificationDate}, expected $newModificationDate" + ) } _ = updateResourceMetadataRequestV2.maybeLabel match { @@ -469,8 +505,10 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt _ = updateResourceMetadataRequestV2.maybePermissions match { case Some(newPermissions) => - if (PermissionUtilADM.parsePermissions(updatedResource.permissions) != PermissionUtilADM.parsePermissions( - newPermissions)) { + if ( + PermissionUtilADM + .parsePermissions(updatedResource.permissions) != PermissionUtilADM.parsePermissions(newPermissions) + ) { throw UpdateNotPerformedException() } @@ -487,15 +525,14 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case None => FastFuture.successful(()) } - } yield - UpdateResourceMetadataResponseV2( - resourceIri = updateResourceMetadataRequestV2.resourceIri, - resourceClassIri = updateResourceMetadataRequestV2.resourceClassIri, - maybeLabel = updateResourceMetadataRequestV2.maybeLabel, - maybePermissions = updateResourceMetadataRequestV2.maybePermissions, - lastModificationDate = newModificationDate, - featureFactoryConfig = updateResourceMetadataRequestV2.featureFactoryConfig - ) + } yield UpdateResourceMetadataResponseV2( + resourceIri = updateResourceMetadataRequestV2.resourceIri, + resourceClassIri = updateResourceMetadataRequestV2.resourceClassIri, + maybeLabel = updateResourceMetadataRequestV2.maybeLabel, + maybePermissions = updateResourceMetadataRequestV2.maybePermissions, + lastModificationDate = newModificationDate, + featureFactoryConfig = updateResourceMetadataRequestV2.featureFactoryConfig + ) } for { @@ -509,27 +546,27 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Either marks a resource as deleted or erases it from the triplestore, depending on the value of `erase` - * in the request message. - * - * @param deleteOrEraseResourceV2 the request message. - */ + * Either marks a resource as deleted or erases it from the triplestore, depending on the value of `erase` + * in the request message. + * + * @param deleteOrEraseResourceV2 the request message. + */ private def deleteOrEraseResourceV2( - deleteOrEraseResourceV2: DeleteOrEraseResourceRequestV2): Future[SuccessResponseV2] = { + deleteOrEraseResourceV2: DeleteOrEraseResourceRequestV2 + ): Future[SuccessResponseV2] = if (deleteOrEraseResourceV2.erase) { eraseResourceV2(deleteOrEraseResourceV2) } else { markResourceAsDeletedV2(deleteOrEraseResourceV2) } - } /** - * Marks a resource as deleted. - * - * @param deleteResourceV2 the request message. - */ + * Marks a resource as deleted. + * + * @param deleteResourceV2 the request message. + */ private def markResourceAsDeletedV2(deleteResourceV2: DeleteOrEraseResourceRequestV2): Future[SuccessResponseV2] = { - def makeTaskFuture: Future[SuccessResponseV2] = { + def makeTaskFuture: Future[SuccessResponseV2] = for { // Get the metadata of the resource to be updated. resourcesSeq: ReadResourcesSequenceV2 <- getResourcePreviewV2( @@ -545,7 +582,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Make sure that the resource's class is what the client thinks it is. _ = if (resource.resourceClassIri != internalResourceClassIri) { throw BadRequestException( - s"Resource <${resource.resourceIri}> is not a member of class <${deleteResourceV2.resourceClassIri}>") + s"Resource <${resource.resourceIri}> is not a member of class <${deleteResourceV2.resourceClassIri}>" + ) } // Make sure that the resource hasn't been updated since the client got its last modification date. @@ -554,10 +592,14 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } // If a custom delete date was provided, make sure it's later than the resource's most recent timestamp. - _ = if (deleteResourceV2.maybeDeleteDate.exists( - !_.isAfter(resource.lastModificationDate.getOrElse(resource.creationDate)))) { + _ = if ( + deleteResourceV2.maybeDeleteDate.exists( + !_.isAfter(resource.lastModificationDate.getOrElse(resource.creationDate)) + ) + ) { throw BadRequestException( - s"A custom delete date must be later than the date when the resource was created or last modified") + s"A custom delete date must be later than the date when the resource was created or last modified" + ) } // Check that the user has permission to mark the resource as deleted. @@ -598,15 +640,19 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt rows = sparqlSelectResponse.results.bindings - _ = if (rows.isEmpty || !stringFormatter.optionStringToBoolean( - rows.head.rowMap.get("isDeleted"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}"))) { + _ = if ( + rows.isEmpty || !stringFormatter.optionStringToBoolean( + rows.head.rowMap.get("isDeleted"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}" + ) + ) + ) { throw UpdateNotPerformedException( - s"Resource <${deleteResourceV2.resourceIri}> was not marked as deleted. Please report this as a possible bug.") + s"Resource <${deleteResourceV2.resourceIri}> was not marked as deleted. Please report this as a possible bug." + ) } } yield SuccessResponseV2("Resource marked as deleted") - } if (deleteResourceV2.erase) { throw AssertionException(s"Request message has erase == true") @@ -623,12 +669,12 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Erases a resource from the triplestore. - * - * @param eraseResourceV2 the request message. - */ + * Erases a resource from the triplestore. + * + * @param eraseResourceV2 the request message. + */ private def eraseResourceV2(eraseResourceV2: DeleteOrEraseResourceRequestV2): Future[SuccessResponseV2] = { - def makeTaskFuture: Future[SuccessResponseV2] = { + def makeTaskFuture: Future[SuccessResponseV2] = for { // Get the metadata of the resource to be updated. resourcesSeq: ReadResourcesSequenceV2 <- getResourcePreviewV2( @@ -641,8 +687,10 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt resource: ReadResourceV2 = resourcesSeq.toResource(eraseResourceV2.resourceIri) // Ensure that the requesting user is a system admin, or an admin of this project. - _ = if (!(eraseResourceV2.requestingUser.permissions.isProjectAdmin(resource.projectADM.id) || - eraseResourceV2.requestingUser.permissions.isSystemAdmin)) { + _ = if ( + !(eraseResourceV2.requestingUser.permissions.isProjectAdmin(resource.projectADM.id) || + eraseResourceV2.requestingUser.permissions.isSystemAdmin) + ) { throw ForbiddenException(s"Only a system admin or project admin can erase a resource") } @@ -651,7 +699,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Make sure that the resource's class is what the client thinks it is. _ = if (resource.resourceClassIri != internalResourceClassIri) { throw BadRequestException( - s"Resource <${resource.resourceIri}> is not a member of class <${eraseResourceV2.resourceClassIri}>") + s"Resource <${resource.resourceIri}> is not a member of class <${eraseResourceV2.resourceClassIri}>" + ) } // Make sure that the resource hasn't been updated since the client got its last modification date. @@ -670,7 +719,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt _ <- throwIfEntityIsUsed( entityIri = resourceSmartIri, errorFun = throw BadRequestException( - s"Resource ${eraseResourceV2.resourceIri} cannot be erased, because it is referred to by another resource"), + s"Resource ${eraseResourceV2.resourceIri} cannot be erased, because it is referred to by another resource" + ), ignoreRdfSubjectAndObject = true ) @@ -697,10 +747,10 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt _ = if (resourceStillExists) { throw UpdateNotPerformedException( - s"Resource <${eraseResourceV2.resourceIri}> was not erased. Please report this as a possible bug.") + s"Resource <${eraseResourceV2.resourceIri}> was not erased. Please report this as a possible bug." + ) } } yield SuccessResponseV2("Resource erased") - } if (!eraseResourceV2.erase) { throw AssertionException(s"Request message has erase == false") @@ -717,34 +767,36 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Generates a [[SparqlTemplateResourceToCreate]] describing SPARQL for creating a resource and its values. - * This method does pre-update checks that have to be done for each new resource individually, even when - * multiple resources are being created in a single request. - * - * @param internalCreateResource the resource to be created. - * @param linkTargetClasses a map of resources that are link targets to the IRIs of those resources' classes. - * @param entityInfo an [[EntityInfoGetResponseV2]] containing definitions of the class of the resource to - * be created, as well as the classes that all the link targets - * belong to. - * @param clientResourceIDs a map of IRIs of resources to be created to client IDs for the same resources, if any. - * @param defaultResourcePermissions the default permissions to be given to the resource, if it does not have custom permissions. - * @param defaultPropertyPermissions the default permissions to be given to the resource's values, if they do not - * have custom permissions. This is a map of property IRIs to permission strings. - * @param creationDate the versionDate to be attached to the resource and its values. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[ResourceReadyToCreate]]. - */ - private def generateResourceReadyToCreate(resourceIri: IRI, - internalCreateResource: CreateResourceV2, - linkTargetClasses: Map[IRI, SmartIri], - entityInfo: EntityInfoGetResponseV2, - clientResourceIDs: Map[IRI, String], - defaultResourcePermissions: String, - defaultPropertyPermissions: Map[SmartIri, String], - creationDate: Instant, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ResourceReadyToCreate] = { + * Generates a [[SparqlTemplateResourceToCreate]] describing SPARQL for creating a resource and its values. + * This method does pre-update checks that have to be done for each new resource individually, even when + * multiple resources are being created in a single request. + * + * @param internalCreateResource the resource to be created. + * @param linkTargetClasses a map of resources that are link targets to the IRIs of those resources' classes. + * @param entityInfo an [[EntityInfoGetResponseV2]] containing definitions of the class of the resource to + * be created, as well as the classes that all the link targets + * belong to. + * @param clientResourceIDs a map of IRIs of resources to be created to client IDs for the same resources, if any. + * @param defaultResourcePermissions the default permissions to be given to the resource, if it does not have custom permissions. + * @param defaultPropertyPermissions the default permissions to be given to the resource's values, if they do not + * have custom permissions. This is a map of property IRIs to permission strings. + * @param creationDate the versionDate to be attached to the resource and its values. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[ResourceReadyToCreate]]. + */ + private def generateResourceReadyToCreate( + resourceIri: IRI, + internalCreateResource: CreateResourceV2, + linkTargetClasses: Map[IRI, SmartIri], + entityInfo: EntityInfoGetResponseV2, + clientResourceIDs: Map[IRI, String], + defaultResourcePermissions: String, + defaultPropertyPermissions: Map[SmartIri, String], + creationDate: Instant, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ResourceReadyToCreate] = { val resourceIDForErrorMsg: String = clientResourceIDs.get(resourceIri).map(resourceID => s"In resource '$resourceID': ").getOrElse("") @@ -752,9 +804,10 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Check that the resource class has a suitable cardinality for each submitted value. resourceClassInfo <- Future(entityInfo.classInfoMap(internalCreateResource.resourceClassIri)) - knoraPropertyCardinalities: Map[SmartIri, Cardinality.KnoraCardinalityInfo] = resourceClassInfo.allCardinalities.view - .filterKeys(resourceClassInfo.knoraResourceProperties) - .toMap + knoraPropertyCardinalities: Map[SmartIri, Cardinality.KnoraCardinalityInfo] = + resourceClassInfo.allCardinalities.view + .filterKeys(resourceClassInfo.knoraResourceProperties) + .toMap _ = internalCreateResource.values.foreach { case (propertyIri: SmartIri, valuesForProperty: Seq[CreateValueInNewResourceV2]) => @@ -763,22 +816,25 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt val cardinalityInfo = knoraPropertyCardinalities.getOrElse( internalPropertyIri, throw OntologyConstraintException( - s"${resourceIDForErrorMsg}Resource class <${internalCreateResource.resourceClassIri.toOntologySchema( - ApiV2Complex)}> has no cardinality for property <$propertyIri>") + s"${resourceIDForErrorMsg}Resource class <${internalCreateResource.resourceClassIri + .toOntologySchema(ApiV2Complex)}> has no cardinality for property <$propertyIri>" + ) ) - if ((cardinalityInfo.cardinality == Cardinality.MayHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveOne) && valuesForProperty.size > 1) { + if ( + (cardinalityInfo.cardinality == Cardinality.MayHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveOne) && valuesForProperty.size > 1 + ) { throw OntologyConstraintException( - s"${resourceIDForErrorMsg}Resource class <${internalCreateResource.resourceClassIri.toOntologySchema( - ApiV2Complex)}> does not allow more than one value for property <$propertyIri>") + s"${resourceIDForErrorMsg}Resource class <${internalCreateResource.resourceClassIri + .toOntologySchema(ApiV2Complex)}> does not allow more than one value for property <$propertyIri>" + ) } } // Check that no required values are missing. - requiredProps: Set[SmartIri] = knoraPropertyCardinalities.filter { - case (_, cardinalityInfo) => - cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome + requiredProps: Set[SmartIri] = knoraPropertyCardinalities.filter { case (_, cardinalityInfo) => + cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome }.keySet -- resourceClassInfo.linkProperties internalPropertyIris: Set[SmartIri] = internalCreateResource.values.keySet @@ -788,7 +844,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt (requiredProps -- internalPropertyIris).map(iri => s"<${iri.toOntologySchema(ApiV2Complex)}>").mkString(", ") throw OntologyConstraintException( s"${resourceIDForErrorMsg}Values were not submitted for the following property or properties, which are required by resource class <${internalCreateResource.resourceClassIri - .toOntologySchema(ApiV2Complex)}>: $missingProps") + .toOntologySchema(ApiV2Complex)}>: $missingProps" + ) } // Check that each submitted value is consistent with the knora-base:objectClassConstraint of the property that is supposed to @@ -823,7 +880,11 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ) // Is the requesting user a system admin, or an admin of this project? - _ = if (!(requestingUser.permissions.isProjectAdmin(internalCreateResource.projectADM.id) || requestingUser.permissions.isSystemAdmin)) { + _ = if ( + !(requestingUser.permissions.isProjectAdmin( + internalCreateResource.projectADM.id + ) || requestingUser.permissions.isSystemAdmin) + ) { // No. Make sure they don't give themselves higher permissions than they would get from the default permissions. @@ -837,7 +898,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt if (permissionComparisonResult == AGreaterThanB) { throw ForbiddenException( - s"${resourceIDForErrorMsg}The specified permissions would give the resource's creator a higher permission on the resource than the default permissions") + s"${resourceIDForErrorMsg}The specified permissions would give the resource's creator a higher permission on the resource than the default permissions" + ) } } } yield validatedCustomPermissions @@ -845,14 +907,15 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case None => FastFuture.successful(defaultResourcePermissions) } - valuesWithValidatedPermissions: Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]] <- validateAndFormatValuePermissions( - project = internalCreateResource.projectADM, - values = internalCreateResource.values, - defaultPropertyPermissions = defaultPropertyPermissions, - resourceIDForErrorMsg = resourceIDForErrorMsg, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + valuesWithValidatedPermissions: Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]] <- + validateAndFormatValuePermissions( + project = internalCreateResource.projectADM, + values = internalCreateResource.values, + defaultPropertyPermissions = defaultPropertyPermissions, + resourceIDForErrorMsg = resourceIDForErrorMsg, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) // Ask the values responder for SPARQL for generating the values. sparqlForValuesResponse: GenerateSparqlToCreateMultipleValuesResponseV2 <- (responderManager ? @@ -862,35 +925,36 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt creationDate = creationDate, requestingUser = requestingUser )).mapTo[GenerateSparqlToCreateMultipleValuesResponseV2] - } yield - ResourceReadyToCreate( - sparqlTemplateResourceToCreate = SparqlTemplateResourceToCreate( - resourceIri = resourceIri, - permissions = resourcePermissions, - sparqlForValues = sparqlForValuesResponse.insertSparql, - resourceClassIri = internalCreateResource.resourceClassIri.toString, - resourceLabel = internalCreateResource.label, - resourceCreationDate = creationDate - ), - values = sparqlForValuesResponse.unverifiedValues, - hasStandoffLink = sparqlForValuesResponse.hasStandoffLink - ) + } yield ResourceReadyToCreate( + sparqlTemplateResourceToCreate = SparqlTemplateResourceToCreate( + resourceIri = resourceIri, + permissions = resourcePermissions, + sparqlForValues = sparqlForValuesResponse.insertSparql, + resourceClassIri = internalCreateResource.resourceClassIri.toString, + resourceLabel = internalCreateResource.label, + resourceCreationDate = creationDate + ), + values = sparqlForValuesResponse.unverifiedValues, + hasStandoffLink = sparqlForValuesResponse.hasStandoffLink + ) } /** - * Given a sequence of resources to be created, gets the class IRIs of all the resources that are the targets of - * link values in the new resources, whether these already exist in the triplestore or are among the resources - * to be created. - * - * @param internalCreateResources the resources to be created. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a map of resource IRIs to class IRIs. - */ - private def getLinkTargetClasses(resourceIri: IRI, - internalCreateResources: Seq[CreateResourceV2], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Map[IRI, SmartIri]] = { + * Given a sequence of resources to be created, gets the class IRIs of all the resources that are the targets of + * link values in the new resources, whether these already exist in the triplestore or are among the resources + * to be created. + * + * @param internalCreateResources the resources to be created. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a map of resource IRIs to class IRIs. + */ + private def getLinkTargetClasses( + resourceIri: IRI, + internalCreateResources: Seq[CreateResourceV2], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Map[IRI, SmartIri]] = { // Get the IRIs of the new and existing resources that are targets of links. val (existingTargetIris: Set[IRI], newTargets: Set[IRI]) = internalCreateResources.flatMap(_.flatValues).foldLeft((Set.empty[IRI], Set.empty[IRI])) { @@ -908,12 +972,9 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } // Make a map of the IRIs of new target resources to their class IRIs. - val classesOfNewTargets: Map[IRI, SmartIri] = internalCreateResources - .map { resourceToCreate => - resourceIri -> resourceToCreate.resourceClassIri - } - .toMap - .view + val classesOfNewTargets: Map[IRI, SmartIri] = internalCreateResources.map { resourceToCreate => + resourceIri -> resourceToCreate.resourceClassIri + }.toMap.view .filterKeys(newTargets) .toMap @@ -934,134 +995,144 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Checks that values to be created in a new resource do not contain duplicates. - * - * @param values a map of property IRIs to values to be created (in the internal schema). - * @param clientResourceIDs a map of IRIs of resources to be created to client IDs for the same resources, if any. - * @param resourceIDForErrorMsg something that can be prepended to an error message to specify the client's ID for the - * resource to be created, if any. - */ - private def checkForDuplicateValues(values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], - clientResourceIDs: Map[IRI, String] = Map.empty[IRI, String], - resourceIDForErrorMsg: IRI): Unit = { - values.foreach { - case (propertyIri: SmartIri, valuesToCreate: Seq[CreateValueInNewResourceV2]) => - // Given the values for a property, compute all possible combinations of two of those values. - val valueCombinations: Iterator[Seq[CreateValueInNewResourceV2]] = valuesToCreate.combinations(2) - - for (valueCombination: Seq[CreateValueInNewResourceV2] <- valueCombinations) { - // valueCombination must have two elements. - val firstValue: ValueContentV2 = valueCombination.head.valueContent - val secondValue: ValueContentV2 = valueCombination(1).valueContent - - if (firstValue.wouldDuplicateOtherValue(secondValue)) { - throw DuplicateValueException( - s"${resourceIDForErrorMsg}Duplicate values for property <${propertyIri.toOntologySchema(ApiV2Complex)}>") - } + * Checks that values to be created in a new resource do not contain duplicates. + * + * @param values a map of property IRIs to values to be created (in the internal schema). + * @param clientResourceIDs a map of IRIs of resources to be created to client IDs for the same resources, if any. + * @param resourceIDForErrorMsg something that can be prepended to an error message to specify the client's ID for the + * resource to be created, if any. + */ + private def checkForDuplicateValues( + values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], + clientResourceIDs: Map[IRI, String] = Map.empty[IRI, String], + resourceIDForErrorMsg: IRI + ): Unit = + values.foreach { case (propertyIri: SmartIri, valuesToCreate: Seq[CreateValueInNewResourceV2]) => + // Given the values for a property, compute all possible combinations of two of those values. + val valueCombinations: Iterator[Seq[CreateValueInNewResourceV2]] = valuesToCreate.combinations(2) + + for (valueCombination: Seq[CreateValueInNewResourceV2] <- valueCombinations) { + // valueCombination must have two elements. + val firstValue: ValueContentV2 = valueCombination.head.valueContent + val secondValue: ValueContentV2 = valueCombination(1).valueContent + + if (firstValue.wouldDuplicateOtherValue(secondValue)) { + throw DuplicateValueException( + s"${resourceIDForErrorMsg}Duplicate values for property <${propertyIri.toOntologySchema(ApiV2Complex)}>" + ) } + } } - } /** - * Checks that values to be created in a new resource are compatible with the object class constraints - * of the resource's properties. - * - * @param values a map of property IRIs to values to be created (in the internal schema). - * @param linkTargetClasses a map of resources that are link targets to the IRIs of those resource's classes. - * @param entityInfo an [[EntityInfoGetResponseV2]] containing definitions of the classes that all the link targets - * belong to. - * @param clientResourceIDs a map of IRIs of resources to be created to client IDs for the same resources, if any. - * @param resourceIDForErrorMsg something that can be prepended to an error message to specify the client's ID for the - * resource to be created, if any. - */ - private def checkObjectClassConstraints(values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], - linkTargetClasses: Map[IRI, SmartIri], - entityInfo: EntityInfoGetResponseV2, - clientResourceIDs: Map[IRI, String] = Map.empty[IRI, String], - resourceIDForErrorMsg: IRI): Unit = { - values.foreach { - case (propertyIri: SmartIri, valuesToCreate: Seq[CreateValueInNewResourceV2]) => - val propertyInfo: ReadPropertyInfoV2 = entityInfo.propertyInfoMap(propertyIri) - - // Don't accept link properties. - if (propertyInfo.isLinkProp) { - throw BadRequestException( - s"${resourceIDForErrorMsg}Invalid property <${propertyIri.toOntologySchema(ApiV2Complex)}>. Use a link value property to submit a link.") - } + * Checks that values to be created in a new resource are compatible with the object class constraints + * of the resource's properties. + * + * @param values a map of property IRIs to values to be created (in the internal schema). + * @param linkTargetClasses a map of resources that are link targets to the IRIs of those resource's classes. + * @param entityInfo an [[EntityInfoGetResponseV2]] containing definitions of the classes that all the link targets + * belong to. + * @param clientResourceIDs a map of IRIs of resources to be created to client IDs for the same resources, if any. + * @param resourceIDForErrorMsg something that can be prepended to an error message to specify the client's ID for the + * resource to be created, if any. + */ + private def checkObjectClassConstraints( + values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], + linkTargetClasses: Map[IRI, SmartIri], + entityInfo: EntityInfoGetResponseV2, + clientResourceIDs: Map[IRI, String] = Map.empty[IRI, String], + resourceIDForErrorMsg: IRI + ): Unit = + values.foreach { case (propertyIri: SmartIri, valuesToCreate: Seq[CreateValueInNewResourceV2]) => + val propertyInfo: ReadPropertyInfoV2 = entityInfo.propertyInfoMap(propertyIri) + + // Don't accept link properties. + if (propertyInfo.isLinkProp) { + throw BadRequestException( + s"${resourceIDForErrorMsg}Invalid property <${propertyIri.toOntologySchema(ApiV2Complex)}>. Use a link value property to submit a link." + ) + } - // Get the property's object class constraint. If this is a link value property, we want the object - // class constraint of the corresponding link property instead. + // Get the property's object class constraint. If this is a link value property, we want the object + // class constraint of the corresponding link property instead. - val propertyInfoForObjectClassConstraint = if (propertyInfo.isLinkValueProp) { - entityInfo.propertyInfoMap(propertyIri.fromLinkValuePropToLinkProp) - } else { - propertyInfo - } + val propertyInfoForObjectClassConstraint = if (propertyInfo.isLinkValueProp) { + entityInfo.propertyInfoMap(propertyIri.fromLinkValuePropToLinkProp) + } else { + propertyInfo + } - val propertyIriForObjectClassConstraint = propertyInfoForObjectClassConstraint.entityInfoContent.propertyIri + val propertyIriForObjectClassConstraint = propertyInfoForObjectClassConstraint.entityInfoContent.propertyIri - val objectClassConstraint: SmartIri = propertyInfoForObjectClassConstraint.entityInfoContent.requireIriObject( - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - throw InconsistentRepositoryDataException( - s"Property <$propertyIriForObjectClassConstraint> has no knora-api:objectType") + val objectClassConstraint: SmartIri = propertyInfoForObjectClassConstraint.entityInfoContent.requireIriObject( + OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + throw InconsistentRepositoryDataException( + s"Property <$propertyIriForObjectClassConstraint> has no knora-api:objectType" ) + ) - // Check each value. - for (valueToCreate: CreateValueInNewResourceV2 <- valuesToCreate) { - valueToCreate.valueContent match { - case linkValueContentV2: LinkValueContentV2 => - // It's a link value. + // Check each value. + for (valueToCreate: CreateValueInNewResourceV2 <- valuesToCreate) { + valueToCreate.valueContent match { + case linkValueContentV2: LinkValueContentV2 => + // It's a link value. - if (!propertyInfo.isLinkValueProp) { - throw OntologyConstraintException(s"${resourceIDForErrorMsg}Property <${propertyIri.toOntologySchema( - ApiV2Complex)}> requires a value of type <${objectClassConstraint.toOntologySchema(ApiV2Complex)}>") - } + if (!propertyInfo.isLinkValueProp) { + throw OntologyConstraintException( + s"${resourceIDForErrorMsg}Property <${propertyIri.toOntologySchema(ApiV2Complex)}> requires a value of type <${objectClassConstraint + .toOntologySchema(ApiV2Complex)}>" + ) + } - // Does the resource that's the target of the link belongs to a subclass of the - // link property's object class constraint? + // Does the resource that's the target of the link belongs to a subclass of the + // link property's object class constraint? - val linkTargetClass = linkTargetClasses(linkValueContentV2.referredResourceIri) - val linkTargetClassInfo = entityInfo.classInfoMap(linkTargetClass) + val linkTargetClass = linkTargetClasses(linkValueContentV2.referredResourceIri) + val linkTargetClassInfo = entityInfo.classInfoMap(linkTargetClass) - if (!linkTargetClassInfo.allBaseClasses.contains(objectClassConstraint)) { - // No. If the target resource already exists, use its IRI in the error message. - // Otherwise, use the client's ID for the resource. - val resourceID = if (linkValueContentV2.referredResourceExists) { - s"<${linkValueContentV2.referredResourceIri}>" - } else { - s"'${clientResourceIDs(linkValueContentV2.referredResourceIri)}'" - } - - throw OntologyConstraintException( - s"${resourceIDForErrorMsg}Resource $resourceID cannot be the object of property <${propertyIriForObjectClassConstraint - .toOntologySchema(ApiV2Complex)}>, because it does not belong to class <${objectClassConstraint - .toOntologySchema(ApiV2Complex)}>") + if (!linkTargetClassInfo.allBaseClasses.contains(objectClassConstraint)) { + // No. If the target resource already exists, use its IRI in the error message. + // Otherwise, use the client's ID for the resource. + val resourceID = if (linkValueContentV2.referredResourceExists) { + s"<${linkValueContentV2.referredResourceIri}>" + } else { + s"'${clientResourceIDs(linkValueContentV2.referredResourceIri)}'" } - case otherValueContentV2: ValueContentV2 => - // It's not a link value. Check that its type is equal to the property's object - // class constraint. - if (otherValueContentV2.valueType != objectClassConstraint) { - throw OntologyConstraintException(s"${resourceIDForErrorMsg}Property <${propertyIri.toOntologySchema( - ApiV2Complex)}> requires a value of type <${objectClassConstraint.toOntologySchema(ApiV2Complex)}>") - } - } + throw OntologyConstraintException( + s"${resourceIDForErrorMsg}Resource $resourceID cannot be the object of property <${propertyIriForObjectClassConstraint + .toOntologySchema(ApiV2Complex)}>, because it does not belong to class <${objectClassConstraint + .toOntologySchema(ApiV2Complex)}>" + ) + } + + case otherValueContentV2: ValueContentV2 => + // It's not a link value. Check that its type is equal to the property's object + // class constraint. + if (otherValueContentV2.valueType != objectClassConstraint) { + throw OntologyConstraintException( + s"${resourceIDForErrorMsg}Property <${propertyIri.toOntologySchema(ApiV2Complex)}> requires a value of type <${objectClassConstraint + .toOntologySchema(ApiV2Complex)}>" + ) + } } + } } - } /** - * Given a sequence of values to be created in a new resource, checks the targets of standoff links in text - * values. For each link, if the target is expected to exist, checks that it exists and that the user has - * permission to see it. - * - * @param values the values to be checked. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ - private def checkStandoffLinkTargets(values: Iterable[CreateValueInNewResourceV2], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Unit] = { + * Given a sequence of values to be created in a new resource, checks the targets of standoff links in text + * values. For each link, if the target is expected to exist, checks that it exists and that the user has + * permission to see it. + * + * @param values the values to be checked. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ + private def checkStandoffLinkTargets( + values: Iterable[CreateValueInNewResourceV2], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Unit] = { val standoffLinkTargetsThatShouldExist: Set[IRI] = values.foldLeft(Set.empty[IRI]) { case (acc: Set[IRI], valueToCreate: CreateValueInNewResourceV2) => valueToCreate.valueContent match { @@ -1080,12 +1151,12 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Given a sequence of values to be created in a new resource, checks the existence of the list nodes referred to - * in list values. - * - * @param values the values to be checked. - * @param requestingUser the user making the request. - */ + * Given a sequence of values to be created in a new resource, checks the existence of the list nodes referred to + * in list values. + * + * @param values the values to be checked. + * @param requestingUser the user making the request. + */ private def checkListNodes(values: Iterable[CreateValueInNewResourceV2], requestingUser: UserADM): Future[Unit] = { val listNodesThatShouldExist: Set[IRI] = values.foldLeft(Set.empty[IRI]) { case (acc: Set[IRI], valueToCreate: CreateValueInNewResourceV2) => @@ -1100,30 +1171,32 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt .sequence( listNodesThatShouldExist .map(listNodeIri => ResourceUtilV2.checkListNodeExists(listNodeIri, storeManager)) - .toSeq) + .toSeq + ) .map(_ => ()) } /** - * Given a map of property IRIs to values to be created in a new resource, validates and reformats any custom - * permissions in the values, and sets all value permissions to defaults if custom permissions are not provided. - * - * @param project the project in which the resource is to be created. - * @param values the values whose permissions are to be validated. - * @param defaultPropertyPermissions a map of property IRIs to default permissions. - * @param resourceIDForErrorMsg a string that can be prepended to an error message to specify the client's - * ID for the containing resource, if provided. - * @param requestingUser the user making the request. - * @return a map of property IRIs to sequences of [[GenerateSparqlForValueInNewResourceV2]], in which - * all permissions have been validated and defined. - */ + * Given a map of property IRIs to values to be created in a new resource, validates and reformats any custom + * permissions in the values, and sets all value permissions to defaults if custom permissions are not provided. + * + * @param project the project in which the resource is to be created. + * @param values the values whose permissions are to be validated. + * @param defaultPropertyPermissions a map of property IRIs to default permissions. + * @param resourceIDForErrorMsg a string that can be prepended to an error message to specify the client's + * ID for the containing resource, if provided. + * @param requestingUser the user making the request. + * @return a map of property IRIs to sequences of [[GenerateSparqlForValueInNewResourceV2]], in which + * all permissions have been validated and defined. + */ private def validateAndFormatValuePermissions( - project: ProjectADM, - values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], - defaultPropertyPermissions: Map[SmartIri, String], - resourceIDForErrorMsg: String, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]]] = { + project: ProjectADM, + values: Map[SmartIri, Seq[CreateValueInNewResourceV2]], + defaultPropertyPermissions: Map[SmartIri, String], + resourceIDForErrorMsg: String, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]]] = { val propertyValuesWithValidatedPermissionsFutures : Map[SmartIri, Seq[Future[GenerateSparqlForValueInNewResourceV2]]] = values.map { case (propertyIri: SmartIri, valuesToCreate: Seq[CreateValueInNewResourceV2]) => @@ -1141,7 +1214,9 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ) // Is the requesting user a system admin, or an admin of this project? - _ = if (!(requestingUser.permissions.isProjectAdmin(project.id) || requestingUser.permissions.isSystemAdmin)) { + _ = if ( + !(requestingUser.permissions.isProjectAdmin(project.id) || requestingUser.permissions.isSystemAdmin) + ) { // No. Make sure they don't give themselves higher permissions than they would get from the default permissions. @@ -1156,17 +1231,17 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt if (permissionComparisonResult == AGreaterThanB) { throw ForbiddenException( - s"${resourceIDForErrorMsg}The specified value permissions would give a value's creator a higher permission on the value than the default permissions") + s"${resourceIDForErrorMsg}The specified value permissions would give a value's creator a higher permission on the value than the default permissions" + ) } } - } yield - GenerateSparqlForValueInNewResourceV2( - valueContent = valueToCreate.valueContent, - customValueIri = valueToCreate.customValueIri, - customValueUUID = valueToCreate.customValueUUID, - customValueCreationDate = valueToCreate.customValueCreationDate, - permissions = validatedCustomPermissions - ) + } yield GenerateSparqlForValueInNewResourceV2( + valueContent = valueToCreate.valueContent, + customValueIri = valueToCreate.customValueIri, + customValueUUID = valueToCreate.customValueUUID, + customValueCreationDate = valueToCreate.customValueCreationDate, + permissions = validatedCustomPermissions + ) case None => // No. Use the default permissions. @@ -1189,16 +1264,18 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Gets the default permissions for resource classs in a project. - * - * @param projectIri the IRI of the project. - * @param resourceClassIris the internal IRIs of the resource classes. - * @param requestingUser the user making the request. - * @return a map of resource class IRIs to default permission strings. - */ - private def getResourceClassDefaultPermissions(projectIri: IRI, - resourceClassIris: Set[SmartIri], - requestingUser: UserADM): Future[Map[SmartIri, String]] = { + * Gets the default permissions for resource classs in a project. + * + * @param projectIri the IRI of the project. + * @param resourceClassIris the internal IRIs of the resource classes. + * @param requestingUser the user making the request. + * @return a map of resource class IRIs to default permission strings. + */ + private def getResourceClassDefaultPermissions( + projectIri: IRI, + resourceClassIris: Set[SmartIri], + requestingUser: UserADM + ): Future[Map[SmartIri, String]] = { val permissionsFutures: Map[SmartIri, Future[String]] = resourceClassIris.toSeq.map { resourceClassIri => val requestMessage = DefaultObjectAccessPermissionsStringForResourceClassGetADM( projectIri = projectIri, @@ -1216,16 +1293,18 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Gets the default permissions for properties in a resource class in a project. - * - * @param projectIri the IRI of the project. - * @param resourceClassProperties a map of internal resource class IRIs to sets of internal property IRIs. - * @param requestingUser the user making the request. - * @return a map of internal resource class IRIs to maps of property IRIs to default permission strings. - */ - private def getDefaultPropertyPermissions(projectIri: IRI, - resourceClassProperties: Map[SmartIri, Set[SmartIri]], - requestingUser: UserADM): Future[Map[SmartIri, Map[SmartIri, String]]] = { + * Gets the default permissions for properties in a resource class in a project. + * + * @param projectIri the IRI of the project. + * @param resourceClassProperties a map of internal resource class IRIs to sets of internal property IRIs. + * @param requestingUser the user making the request. + * @return a map of internal resource class IRIs to maps of property IRIs to default permission strings. + */ + private def getDefaultPropertyPermissions( + projectIri: IRI, + resourceClassProperties: Map[SmartIri, Set[SmartIri]], + requestingUser: UserADM + ): Future[Map[SmartIri, Map[SmartIri, String]]] = { val permissionsFutures: Map[SmartIri, Future[Map[SmartIri, String]]] = resourceClassProperties.map { case (resourceClassIri, propertyIris) => val propertyPermissionsFutures: Map[SmartIri, Future[String]] = propertyIris.toSeq.map { propertyIri => @@ -1245,18 +1324,20 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Checks that a resource was created correctly. - * - * @param resourceReadyToCreate the resource that should have been created. - * @param projectIri the IRI of the project in which the resource should have been created. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user that attempted to create the resource. - * @return a preview of the resource that was created. - */ - private def verifyResource(resourceReadyToCreate: ResourceReadyToCreate, - projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourcesSequenceV2] = { + * Checks that a resource was created correctly. + * + * @param resourceReadyToCreate the resource that should have been created. + * @param projectIri the IRI of the project in which the resource should have been created. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user that attempted to create the resource. + * @return a preview of the resource that was created. + */ + private def verifyResource( + resourceReadyToCreate: ResourceReadyToCreate, + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourcesSequenceV2] = { val resourceIri = resourceReadyToCreate.sparqlTemplateResourceToCreate.resourceIri val resourceFuture: Future[ReadResourcesSequenceV2] = for { @@ -1270,7 +1351,9 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt resource: ReadResourceV2 = resourcesResponse.toResource(requestedResourceIri = resourceIri) - _ = if (resource.resourceClassIri.toString != resourceReadyToCreate.sparqlTemplateResourceToCreate.resourceClassIri) { + _ = if ( + resource.resourceClassIri.toString != resourceReadyToCreate.sparqlTemplateResourceToCreate.resourceClassIri + ) { throw AssertionException(s"Resource <$resourceIri> was saved, but it has the wrong resource class") } @@ -1288,7 +1371,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Undo any escapes in the submitted rdfs:label to compare it with the saved one. unescapedLabel: String = stringFormatter.fromSparqlEncodedString( - resourceReadyToCreate.sparqlTemplateResourceToCreate.resourceLabel) + resourceReadyToCreate.sparqlTemplateResourceToCreate.resourceLabel + ) _ = if (resource.label != unescapedLabel) { throw AssertionException(s"Resource <$resourceIri> was saved, but it has the wrong label") @@ -1297,9 +1381,12 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt savedPropertyIris: Set[SmartIri] = resource.values.keySet // Check that the property knora-base:hasStandoffLinkToValue was automatically added if necessary. - expectedPropertyIris: Set[SmartIri] = resourceReadyToCreate.values.keySet ++ (if (resourceReadyToCreate.hasStandoffLink) { + expectedPropertyIris: Set[SmartIri] = resourceReadyToCreate.values.keySet ++ (if ( + resourceReadyToCreate.hasStandoffLink + ) { Some( - OntologyConstants.KnoraBase.HasStandoffLinkToValue.toSmartIri) + OntologyConstants.KnoraBase.HasStandoffLinkToValue.toSmartIri + ) } else { None }) @@ -1308,7 +1395,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt throw AssertionException( s"Resource <$resourceIri> was saved, but it has the wrong properties: expected (${expectedPropertyIris .map(_.toSparql) - .mkString(", ")}), but saved (${savedPropertyIris.map(_.toSparql).mkString(", ")})") + .mkString(", ")}), but saved (${savedPropertyIris.map(_.toSparql).mkString(", ")})" + ) } // Ignore knora-base:hasStandoffLinkToValue when checking the expected values. @@ -1320,41 +1408,44 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt throw AssertionException(s"Resource <$resourceIri> was saved, but it has the wrong values") } - savedValues.zip(expectedValues).foreach { - case (savedValue, expectedValue) => - if (!(expectedValue.valueContent.wouldDuplicateCurrentVersion(savedValue.valueContent) && - savedValue.permissions == expectedValue.permissions && - savedValue.attachedToUser == requestingUser.id)) { - // println(s"========== Expected ==========\n${MessageUtil.toSource(expectedValue.valueContent)}\n========== Saved ==========\n${MessageUtil.toSource(savedValue.valueContent)}") - throw AssertionException( - s"Resource <$resourceIri> was saved, but one or more of its values are not correct") - } + savedValues.zip(expectedValues).foreach { case (savedValue, expectedValue) => + if ( + !(expectedValue.valueContent.wouldDuplicateCurrentVersion(savedValue.valueContent) && + savedValue.permissions == expectedValue.permissions && + savedValue.attachedToUser == requestingUser.id) + ) { + // println(s"========== Expected ==========\n${MessageUtil.toSource(expectedValue.valueContent)}\n========== Saved ==========\n${MessageUtil.toSource(savedValue.valueContent)}") + throw AssertionException( + s"Resource <$resourceIri> was saved, but one or more of its values are not correct" + ) + } } } - } yield - ReadResourcesSequenceV2( - resources = Seq(resource.copy(values = Map.empty)) - ) + } yield ReadResourcesSequenceV2( + resources = Seq(resource.copy(values = Map.empty)) + ) - resourceFuture.recover { - case _: NotFoundException => - throw UpdateNotPerformedException( - s"Resource <$resourceIri> was not created. Please report this as a possible bug.") + resourceFuture.recover { case _: NotFoundException => + throw UpdateNotPerformedException( + s"Resource <$resourceIri> was not created. Please report this as a possible bug." + ) } } /** - * After the attempted creation of one or more resources, looks for file values among the values that were supposed - * to be created, and tells Sipi to move those files to permanent storage if the update succeeded, or to delete the - * temporary files if the update failed. - * - * @param updateFuture the operation that was supposed to create the resources. - * @param createResources the resources that were supposed to be created. - * @param requestingUser the user making the request. - */ - private def doSipiPostUpdateForResources[T <: UpdateResultInProject](updateFuture: Future[T], - createResources: Seq[CreateResourceV2], - requestingUser: UserADM): Future[T] = { + * After the attempted creation of one or more resources, looks for file values among the values that were supposed + * to be created, and tells Sipi to move those files to permanent storage if the update succeeded, or to delete the + * temporary files if the update failed. + * + * @param updateFuture the operation that was supposed to create the resources. + * @param createResources the resources that were supposed to be created. + * @param requestingUser the user making the request. + */ + private def doSipiPostUpdateForResources[T <: UpdateResultInProject]( + updateFuture: Future[T], + createResources: Seq[CreateResourceV2], + requestingUser: UserADM + ): Future[T] = { val allValues: Seq[ValueContentV2] = createResources.flatMap(_.flatValues).map(_.valueContent) val resultFutures: Seq[Future[T]] = allValues.map { valueContent => @@ -1375,29 +1466,30 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Gets the requested resources from the triplestore. - * - * @param resourceIris the Iris of the requested resources. - * @param preview `true` if a preview of the resource is requested. - * @param withDeleted if defined, indicates if the deleted resources and values should be returned or not. - * @param propertyIri if defined, requests only the values of the specified explicit property. - * @param valueUuid if defined, requests only the value with the specified UUID. - * @param versionDate if defined, requests the state of the resources at the specified time in the past. - * Cannot be used in conjunction with `preview`. - * @param queryStandoff `true` if standoff should be queried. - * @param featureFactoryConfig the feature factory configuration. - * @return a map of resource IRIs to RDF data. - */ + * Gets the requested resources from the triplestore. + * + * @param resourceIris the Iris of the requested resources. + * @param preview `true` if a preview of the resource is requested. + * @param withDeleted if defined, indicates if the deleted resources and values should be returned or not. + * @param propertyIri if defined, requests only the values of the specified explicit property. + * @param valueUuid if defined, requests only the value with the specified UUID. + * @param versionDate if defined, requests the state of the resources at the specified time in the past. + * Cannot be used in conjunction with `preview`. + * @param queryStandoff `true` if standoff should be queried. + * @param featureFactoryConfig the feature factory configuration. + * @return a map of resource IRIs to RDF data. + */ private def getResourcesFromTriplestore( - resourceIris: Seq[IRI], - preview: Boolean, - withDeleted: Boolean = false, - propertyIri: Option[SmartIri] = None, - valueUuid: Option[UUID] = None, - versionDate: Option[Instant] = None, - queryStandoff: Boolean, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ConstructResponseUtilV2.MainResourcesAndValueRdfData] = { + resourceIris: Seq[IRI], + preview: Boolean, + withDeleted: Boolean = false, + propertyIri: Option[SmartIri] = None, + valueUuid: Option[UUID] = None, + versionDate: Option[Instant] = None, + queryStandoff: Boolean, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ConstructResponseUtilV2.MainResourcesAndValueRdfData] = { // eliminate duplicate Iris val resourceIrisDistinct: Seq[IRI] = resourceIris.distinct @@ -1425,7 +1517,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt maybeStandoffMaxStartIndex = maybeStandoffMaxStartIndex, stringFormatter = stringFormatter ) - .toString()) + .toString() + ) // _ = println(resourceRequestSparql) @@ -1445,28 +1538,30 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Get one or several resources and return them as a sequence. - * - * @param resourceIris the IRIs of the resources to be queried. - * @param propertyIri if defined, requests only the values of the specified explicit property. - * @param valueUuid if defined, requests only the value with the specified UUID. - * @param versionDate if defined, requests the state of the resources at the specified time in the past. - * @param withDeleted if defined, indicates if the deleted resource and values should be returned or not. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[ReadResourcesSequenceV2]]. - */ - private def getResourcesV2(resourceIris: Seq[IRI], - propertyIri: Option[SmartIri] = None, - valueUuid: Option[UUID] = None, - versionDate: Option[Instant] = None, - withDeleted: Boolean = false, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourcesSequenceV2] = { + * Get one or several resources and return them as a sequence. + * + * @param resourceIris the IRIs of the resources to be queried. + * @param propertyIri if defined, requests only the values of the specified explicit property. + * @param valueUuid if defined, requests only the value with the specified UUID. + * @param versionDate if defined, requests the state of the resources at the specified time in the past. + * @param withDeleted if defined, indicates if the deleted resource and values should be returned or not. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[ReadResourcesSequenceV2]]. + */ + private def getResourcesV2( + resourceIris: Seq[IRI], + propertyIri: Option[SmartIri] = None, + valueUuid: Option[UUID] = None, + versionDate: Option[Instant] = None, + withDeleted: Boolean = false, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourcesSequenceV2] = { // eliminate duplicate Iris val resourceIrisDistinct: Seq[IRI] = resourceIris.distinct @@ -1490,15 +1585,16 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ) // If we're querying standoff, get XML-to standoff mappings. - mappingsAsMap: Map[IRI, MappingAndXSLTransformation] <- if (queryStandoff) { - getMappingsFromQueryResultsSeparated( - queryResultsSeparated = mainResourcesAndValueRdfData.resources, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - } else { - FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) - } + mappingsAsMap: Map[IRI, MappingAndXSLTransformation] <- + if (queryStandoff) { + getMappingsFromQueryResultsSeparated( + queryResultsSeparated = mainResourcesAndValueRdfData.resources, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } else { + FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) + } apiResponse: ReadResourcesSequenceV2 <- ConstructResponseUtilV2.createApiResponse( mainResourcesAndValueRdfData = mainResourcesAndValueRdfData, @@ -1524,7 +1620,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case Some(definedValueUuid) => if (!apiResponse.resources.exists(_.values.values.exists(_.exists(_.valueHasUUID == definedValueUuid)))) { throw NotFoundException( - s"Value with UUID ${stringFormatter.base64EncodeUuid(definedValueUuid)} not found (maybe you do not have permission to see it, or it is marked as deleted)") + s"Value with UUID ${stringFormatter.base64EncodeUuid(definedValueUuid)} not found (maybe you do not have permission to see it, or it is marked as deleted)" + ) } case None => () @@ -1535,19 +1632,21 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Get the preview of a resource. - * - * @param resourceIris the resource to query for. - * @param withDeleted indicates if the deleted resource should be returned or not. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the user making the request. - * @return a [[ReadResourcesSequenceV2]]. - */ - private def getResourcePreviewV2(resourceIris: Seq[IRI], - withDeleted: Boolean = false, - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourcesSequenceV2] = { + * Get the preview of a resource. + * + * @param resourceIris the resource to query for. + * @param withDeleted indicates if the deleted resource should be returned or not. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the user making the request. + * @return a [[ReadResourcesSequenceV2]]. + */ + private def getResourcePreviewV2( + resourceIris: Seq[IRI], + withDeleted: Boolean = false, + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourcesSequenceV2] = { // eliminate duplicate Iris val resourceIrisDistinct: Seq[IRI] = resourceIris.distinct @@ -1585,16 +1684,18 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Obtains a Gravsearch template from Sipi. - * - * @param gravsearchTemplateIri the Iri of the resource representing the Gravsearch template. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the Gravsearch template. - */ - private def getGravsearchTemplate(gravsearchTemplateIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[String] = { + * Obtains a Gravsearch template from Sipi. + * + * @param gravsearchTemplateIri the Iri of the resource representing the Gravsearch template. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the Gravsearch template. + */ + private def getGravsearchTemplate( + gravsearchTemplateIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[String] = { val gravsearchUrlFuture = for { resources: ReadResourcesSequenceV2 <- getResourcesV2( @@ -1612,7 +1713,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } (fileValueIri: IRI, gravsearchFileValueContent: TextFileValueContentV2) = resource.values.get( - OntologyConstants.KnoraBase.HasTextFileValue.toSmartIri) match { + OntologyConstants.KnoraBase.HasTextFileValue.toSmartIri + ) match { case Some(values: Seq[ReadValueV2]) if values.size == 1 => values.head match { case value: ReadValueV2 => @@ -1620,27 +1722,30 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case textRepr: TextFileValueContentV2 => (value.valueIri, textRepr) case _ => throw InconsistentRepositoryDataException( - s"Resource $gravsearchTemplateIri is supposed to have exactly one value of type ${OntologyConstants.KnoraBase.TextFileValue}") + s"Resource $gravsearchTemplateIri is supposed to have exactly one value of type ${OntologyConstants.KnoraBase.TextFileValue}" + ) } } case None => throw InconsistentRepositoryDataException( - s"Resource $gravsearchTemplateIri has no property ${OntologyConstants.KnoraBase.HasTextFileValue}") + s"Resource $gravsearchTemplateIri has no property ${OntologyConstants.KnoraBase.HasTextFileValue}" + ) } // check if gravsearchFileValueContent represents a text file _ = if (gravsearchFileValueContent.fileValue.internalMimeType != "text/plain") { throw BadRequestException( - s"Expected $fileValueIri to be a text file referring to a Gravsearch template, but it has MIME type ${gravsearchFileValueContent.fileValue.internalMimeType}") + s"Expected $fileValueIri to be a text file referring to a Gravsearch template, but it has MIME type ${gravsearchFileValueContent.fileValue.internalMimeType}" + ) } - gravsearchUrl: String = s"${settings.internalSipiBaseUrl}/${resource.projectADM.shortcode}/${gravsearchFileValueContent.fileValue.internalFilename}/file" + gravsearchUrl: String = + s"${settings.internalSipiBaseUrl}/${resource.projectADM.shortcode}/${gravsearchFileValueContent.fileValue.internalFilename}/file" } yield gravsearchUrl - val recoveredGravsearchUrlFuture = gravsearchUrlFuture.recover { - case notFound: NotFoundException => - throw BadRequestException(s"Gravsearch template $gravsearchTemplateIri not found: ${notFound.message}") + val recoveredGravsearchUrlFuture = gravsearchUrlFuture.recover { case notFound: NotFoundException => + throw BadRequestException(s"Gravsearch template $gravsearchTemplateIri not found: ${notFound.message}") } for { @@ -1657,34 +1762,35 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Returns a resource as TEI/XML. - * This makes only sense for resources that have a text value containing standoff that is to be converted to the TEI body. - * - * @param resourceIri the Iri of the resource to be converted to a TEI document (header and body). - * @param textProperty the Iri of the property (text value with standoff) to be converted to the body of the TEI document. - * @param mappingIri the Iri of the mapping to be used to convert standoff to XML, if a custom mapping is provided. The mapping is expected to contain an XSL transformation. - * @param gravsearchTemplateIri the Iri of the Gravsearch template to query for the metadata for the TEI header. The resource Iri is expected to be represented by the placeholder '$resourceIri' in a BIND. - * @param headerXSLTIri the Iri of the XSL template to convert the metadata properties to the TEI header. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[ResourceTEIGetResponseV2]]. - */ - private def getResourceAsTeiV2(resourceIri: IRI, - textProperty: SmartIri, - mappingIri: Option[IRI], - gravsearchTemplateIri: Option[IRI], - headerXSLTIri: Option[String], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ResourceTEIGetResponseV2] = { + * Returns a resource as TEI/XML. + * This makes only sense for resources that have a text value containing standoff that is to be converted to the TEI body. + * + * @param resourceIri the Iri of the resource to be converted to a TEI document (header and body). + * @param textProperty the Iri of the property (text value with standoff) to be converted to the body of the TEI document. + * @param mappingIri the Iri of the mapping to be used to convert standoff to XML, if a custom mapping is provided. The mapping is expected to contain an XSL transformation. + * @param gravsearchTemplateIri the Iri of the Gravsearch template to query for the metadata for the TEI header. The resource Iri is expected to be represented by the placeholder '$resourceIri' in a BIND. + * @param headerXSLTIri the Iri of the XSL template to convert the metadata properties to the TEI header. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[ResourceTEIGetResponseV2]]. + */ + private def getResourceAsTeiV2( + resourceIri: IRI, + textProperty: SmartIri, + mappingIri: Option[IRI], + gravsearchTemplateIri: Option[IRI], + headerXSLTIri: Option[String], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ResourceTEIGetResponseV2] = { /** - * Extract the text value to be converted to TEI/XML. - * - * @param readResource the resource which is expected to hold the text value. - * @return a [[TextValueContentV2]] representing the text value to be converted to TEI/XML. - */ - def getTextValueFromReadResource(readResource: ReadResourceV2): TextValueContentV2 = { - + * Extract the text value to be converted to TEI/XML. + * + * @param readResource the resource which is expected to hold the text value. + * @return a [[TextValueContentV2]] representing the text value to be converted to TEI/XML. + */ + def getTextValueFromReadResource(readResource: ReadResourceV2): TextValueContentV2 = readResource.values.get(textProperty) match { case Some(valObjs: Seq[ReadValueV2]) if valObjs.size == 1 => // make sure that the property has one instance and that it is of type TextValue and that is has standoff (markup) @@ -1693,117 +1799,118 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case _ => throw BadRequestException( - s"$textProperty to be of type ${OntologyConstants.KnoraBase.TextValue} with standoff (markup)") + s"$textProperty to be of type ${OntologyConstants.KnoraBase.TextValue} with standoff (markup)" + ) } case None => throw BadRequestException(s"$textProperty is expected to occur once on $resourceIri") } - } /** - * Given a resource's values, convert the date values to Gregorian. - * - * @param values the values to be processed. - * @return the resource's values with date values converted to Gregorian. - */ - def convertDateToGregorian(values: Map[SmartIri, Seq[ReadValueV2]]): Map[SmartIri, Seq[ReadValueV2]] = { - values.map { - case (propIri: SmartIri, valueObjs: Seq[ReadValueV2]) => - propIri -> valueObjs.map { - - // convert all dates to Gregorian calendar dates (standardization) - valueObj: ReadValueV2 => - valueObj match { - case readNonLinkValueV2: ReadOtherValueV2 => - readNonLinkValueV2.valueContent match { - case dateContent: DateValueContentV2 => - // date value - - readNonLinkValueV2.copy( - valueContent = dateContent.copy( - // act as if this was a Gregorian date - valueHasCalendar = CalendarNameGregorian - ) + * Given a resource's values, convert the date values to Gregorian. + * + * @param values the values to be processed. + * @return the resource's values with date values converted to Gregorian. + */ + def convertDateToGregorian(values: Map[SmartIri, Seq[ReadValueV2]]): Map[SmartIri, Seq[ReadValueV2]] = + values.map { case (propIri: SmartIri, valueObjs: Seq[ReadValueV2]) => + propIri -> valueObjs.map { + + // convert all dates to Gregorian calendar dates (standardization) + valueObj: ReadValueV2 => + valueObj match { + case readNonLinkValueV2: ReadOtherValueV2 => + readNonLinkValueV2.valueContent match { + case dateContent: DateValueContentV2 => + // date value + + readNonLinkValueV2.copy( + valueContent = dateContent.copy( + // act as if this was a Gregorian date + valueHasCalendar = CalendarNameGregorian ) + ) - case _ => valueObj - } + case _ => valueObj + } - case readLinkValueV2: ReadLinkValueV2 if readLinkValueV2.valueContent.nestedResource.nonEmpty => - // recursively process the values of the nested resource + case readLinkValueV2: ReadLinkValueV2 if readLinkValueV2.valueContent.nestedResource.nonEmpty => + // recursively process the values of the nested resource - val linkContent = readLinkValueV2.valueContent + val linkContent = readLinkValueV2.valueContent - readLinkValueV2.copy( - valueContent = linkContent.copy( - nestedResource = Some( - linkContent.nestedResource.get.copy( - // recursive call - values = convertDateToGregorian(linkContent.nestedResource.get.values) - ) + readLinkValueV2.copy( + valueContent = linkContent.copy( + nestedResource = Some( + linkContent.nestedResource.get.copy( + // recursive call + values = convertDateToGregorian(linkContent.nestedResource.get.values) ) ) ) + ) - case _ => valueObj - } - } + case _ => valueObj + } + } } - } for { // get the requested resource - resource: ReadResourceV2 <- if (gravsearchTemplateIri.nonEmpty) { + resource: ReadResourceV2 <- + if (gravsearchTemplateIri.nonEmpty) { - // check that there is an XSLT to create the TEI header - if (headerXSLTIri.isEmpty) - throw BadRequestException( - s"When a Gravsearch template Iri is provided, also a header XSLT Iri has to be provided.") + // check that there is an XSLT to create the TEI header + if (headerXSLTIri.isEmpty) + throw BadRequestException( + s"When a Gravsearch template Iri is provided, also a header XSLT Iri has to be provided." + ) - for { - // get the template - template <- getGravsearchTemplate( - gravsearchTemplateIri = gravsearchTemplateIri.get, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + for { + // get the template + template <- getGravsearchTemplate( + gravsearchTemplateIri = gravsearchTemplateIri.get, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) - // insert actual resource Iri, replacing the placeholder - gravsearchQuery = template.replace("$resourceIri", resourceIri) + // insert actual resource Iri, replacing the placeholder + gravsearchQuery = template.replace("$resourceIri", resourceIri) - // parse the Gravsearch query - constructQuery: ConstructQuery = GravsearchParser.parseQuery(gravsearchQuery) + // parse the Gravsearch query + constructQuery: ConstructQuery = GravsearchParser.parseQuery(gravsearchQuery) - // do a request to the SearchResponder - gravSearchResponse: ReadResourcesSequenceV2 <- (responderManager ? GravsearchRequestV2( - constructQuery = constructQuery, - targetSchema = ApiV2Complex, - schemaOptions = SchemaOptions.ForStandoffWithTextValues, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - )).mapTo[ReadResourcesSequenceV2] - } yield gravSearchResponse.toResource(resourceIri) + // do a request to the SearchResponder + gravSearchResponse: ReadResourcesSequenceV2 <- (responderManager ? GravsearchRequestV2( + constructQuery = constructQuery, + targetSchema = ApiV2Complex, + schemaOptions = SchemaOptions.ForStandoffWithTextValues, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + )).mapTo[ReadResourcesSequenceV2] + } yield gravSearchResponse.toResource(resourceIri) - } else { - // no Gravsearch template is provided + } else { + // no Gravsearch template is provided - // check that there is no XSLT for the header since there is no Gravsearch template - if (headerXSLTIri.nonEmpty) - throw BadRequestException( - s"When no Gravsearch template Iri is provided, no header XSLT Iri is expected to be provided either.") - - for { - // get requested resource - resource <- getResourcesV2( - resourceIris = Vector(resourceIri), - targetSchema = ApiV2Complex, - schemaOptions = SchemaOptions.ForStandoffWithTextValues, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ).map(_.toResource(resourceIri)) - } yield resource - } + // check that there is no XSLT for the header since there is no Gravsearch template + if (headerXSLTIri.nonEmpty) + throw BadRequestException( + s"When no Gravsearch template Iri is provided, no header XSLT Iri is expected to be provided either." + ) + + for { + // get requested resource + resource <- getResourcesV2( + resourceIris = Vector(resourceIri), + targetSchema = ApiV2Complex, + schemaOptions = SchemaOptions.ForStandoffWithTextValues, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ).map(_.toResource(resourceIri)) + } yield resource + } // get the value object representing the text value that is to be mapped to the body of the TEI document bodyTextValue: TextValueContentV2 = getTextValueFromReadResource(resource) @@ -1890,7 +1997,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt xslTransformationFuture.recover { case notFound: NotFoundException => throw SipiException( - s"Default XSL transformation <${teiMapping.mapping.defaultXSLTransformation.get}> not found for mapping <${teiMapping.mappingIri}>: ${notFound.message}") + s"Default XSL transformation <${teiMapping.mapping.defaultXSLTransformation.get}> not found for mapping <${teiMapping.mappingIri}>: ${notFound.message}" + ) case other => throw other } @@ -1916,73 +2024,81 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Gets a graph of resources that are reachable via links to or from a given resource. - * - * @param graphDataGetRequest a [[GraphDataGetRequestV2]] specifying the characteristics of the graph. - * @return a [[GraphDataGetResponseV2]] representing the requested graph. - */ + * Gets a graph of resources that are reachable via links to or from a given resource. + * + * @param graphDataGetRequest a [[GraphDataGetRequestV2]] specifying the characteristics of the graph. + * @return a [[GraphDataGetResponseV2]] representing the requested graph. + */ private def getGraphDataResponseV2(graphDataGetRequest: GraphDataGetRequestV2): Future[GraphDataGetResponseV2] = { val excludePropertyInternal = graphDataGetRequest.excludeProperty.map(_.toOntologySchema(InternalSchema)) /** - * The internal representation of a node returned by a SPARQL query generated by the `getGraphData` template. - * - * @param nodeIri the IRI of the node. - * @param nodeClass the IRI of the node's class. - * @param nodeLabel the node's label. - * @param nodeCreator the node's creator. - * @param nodeProject the node's project. - * @param nodePermissions the node's permissions. - */ - case class QueryResultNode(nodeIri: IRI, - nodeClass: SmartIri, - nodeLabel: String, - nodeCreator: IRI, - nodeProject: IRI, - nodePermissions: String) + * The internal representation of a node returned by a SPARQL query generated by the `getGraphData` template. + * + * @param nodeIri the IRI of the node. + * @param nodeClass the IRI of the node's class. + * @param nodeLabel the node's label. + * @param nodeCreator the node's creator. + * @param nodeProject the node's project. + * @param nodePermissions the node's permissions. + */ + case class QueryResultNode( + nodeIri: IRI, + nodeClass: SmartIri, + nodeLabel: String, + nodeCreator: IRI, + nodeProject: IRI, + nodePermissions: String + ) /** - * The internal representation of an edge returned by a SPARQL query generated by the `getGraphData` template. - * - * @param linkValueIri the IRI of the link value. - * @param sourceNodeIri the IRI of the source node. - * @param targetNodeIri the IRI of the target node. - * @param linkProp the IRI of the link property. - * @param linkValueCreator the link value's creator. - * @param sourceNodeProject the project of the source node. - * @param linkValuePermissions the link value's permissions. - */ - case class QueryResultEdge(linkValueIri: IRI, - sourceNodeIri: IRI, - targetNodeIri: IRI, - linkProp: SmartIri, - linkValueCreator: IRI, - sourceNodeProject: IRI, - linkValuePermissions: String) + * The internal representation of an edge returned by a SPARQL query generated by the `getGraphData` template. + * + * @param linkValueIri the IRI of the link value. + * @param sourceNodeIri the IRI of the source node. + * @param targetNodeIri the IRI of the target node. + * @param linkProp the IRI of the link property. + * @param linkValueCreator the link value's creator. + * @param sourceNodeProject the project of the source node. + * @param linkValuePermissions the link value's permissions. + */ + case class QueryResultEdge( + linkValueIri: IRI, + sourceNodeIri: IRI, + targetNodeIri: IRI, + linkProp: SmartIri, + linkValueCreator: IRI, + sourceNodeProject: IRI, + linkValuePermissions: String + ) /** - * Represents results returned by a SPARQL query generated by the `getGraphData` template. - * - * @param nodes the nodes that were returned by the query. - * @param edges the edges that were returned by the query. - */ - case class GraphQueryResults(nodes: Set[QueryResultNode] = Set.empty[QueryResultNode], - edges: Set[QueryResultEdge] = Set.empty[QueryResultEdge]) + * Represents results returned by a SPARQL query generated by the `getGraphData` template. + * + * @param nodes the nodes that were returned by the query. + * @param edges the edges that were returned by the query. + */ + case class GraphQueryResults( + nodes: Set[QueryResultNode] = Set.empty[QueryResultNode], + edges: Set[QueryResultEdge] = Set.empty[QueryResultEdge] + ) /** - * Recursively queries outbound or inbound links from/to a resource. - * - * @param startNode the node to use as the starting point of the query. The user is assumed to have permission - * to see this node. - * @param outbound `true` to get outbound links, `false` to get inbound links. - * @param depth the maximum depth of the query. - * @param traversedEdges edges that have already been traversed. - * @return a [[GraphQueryResults]]. - */ - def traverseGraph(startNode: QueryResultNode, - outbound: Boolean, - depth: Int, - traversedEdges: Set[QueryResultEdge] = Set.empty[QueryResultEdge]): Future[GraphQueryResults] = { + * Recursively queries outbound or inbound links from/to a resource. + * + * @param startNode the node to use as the starting point of the query. The user is assumed to have permission + * to see this node. + * @param outbound `true` to get outbound links, `false` to get inbound links. + * @param depth the maximum depth of the query. + * @param traversedEdges edges that have already been traversed. + * @return a [[GraphQueryResults]]. + */ + def traverseGraph( + startNode: QueryResultNode, + outbound: Boolean, + depth: Int, + traversedEdges: Set[QueryResultEdge] = Set.empty[QueryResultEdge] + ): Future[GraphQueryResults] = { if (depth < 1) Future.failed(AssertionException("Depth must be at least 1")) for { @@ -1997,7 +2113,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt outbound = outbound, // true to query outbound edges, false to query inbound edges limit = settings.maxGraphBreadth ) - .toString()) + .toString() + ) // _ = println(sparql) @@ -2005,13 +2122,13 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt rows: Seq[VariableResultsRow] = response.results.bindings // Did we get any results? - recursiveResults: GraphQueryResults <- if (rows.isEmpty) { - // No. Return nothing. - Future(GraphQueryResults()) - } else { - // Yes. Get the nodes from the query results. - val otherNodes: Seq[QueryResultNode] = rows - .map { row: VariableResultsRow => + recursiveResults: GraphQueryResults <- + if (rows.isEmpty) { + // No. Return nothing. + Future(GraphQueryResults()) + } else { + // Yes. Get the nodes from the query results. + val otherNodes: Seq[QueryResultNode] = rows.map { row: VariableResultsRow => val rowMap: Map[String, String] = row.rowMap QueryResultNode( @@ -2022,8 +2139,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt nodeProject = rowMap("nodeProject"), nodePermissions = rowMap("nodePermissions") ) - } - .filter { node: QueryResultNode => + }.filter { node: QueryResultNode => // Filter out the nodes that the user doesn't have permission to see. PermissionUtilADM .getUserPermissionADM( @@ -2035,12 +2151,11 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt .nonEmpty } - // Collect the IRIs of the nodes that the user has permission to see, including the start node. - val visibleNodeIris: Set[IRI] = otherNodes.map(_.nodeIri).toSet + startNode.nodeIri + // Collect the IRIs of the nodes that the user has permission to see, including the start node. + val visibleNodeIris: Set[IRI] = otherNodes.map(_.nodeIri).toSet + startNode.nodeIri - // Get the edges from the query results. - val edges: Set[QueryResultEdge] = rows - .map { row: VariableResultsRow => + // Get the edges from the query results. + val edges: Set[QueryResultEdge] = rows.map { row: VariableResultsRow => val rowMap: Map[String, String] = row.rowMap val nodeIri: IRI = rowMap("node") @@ -2061,73 +2176,70 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt sourceNodeProject = if (outbound) startNode.nodeProject else rowMap("nodeProject"), linkValuePermissions = rowMap("linkValuePermissions") ) - } - .filter { edge: QueryResultEdge => + }.filter { edge: QueryResultEdge => // Filter out the edges that the user doesn't have permission to see. To see an edge, // the user must have some permission on the link value and on the source and target // nodes. - val hasPermission: Boolean = visibleNodeIris.contains(edge.sourceNodeIri) && visibleNodeIris.contains( - edge.targetNodeIri) && - PermissionUtilADM - .getUserPermissionADM( - entityCreator = edge.linkValueCreator, - entityProject = edge.sourceNodeProject, - entityPermissionLiteral = edge.linkValuePermissions, - requestingUser = graphDataGetRequest.requestingUser - ) - .nonEmpty + val hasPermission: Boolean = + visibleNodeIris.contains(edge.sourceNodeIri) && visibleNodeIris.contains(edge.targetNodeIri) && + PermissionUtilADM + .getUserPermissionADM( + entityCreator = edge.linkValueCreator, + entityProject = edge.sourceNodeProject, + entityPermissionLiteral = edge.linkValuePermissions, + requestingUser = graphDataGetRequest.requestingUser + ) + .nonEmpty // Filter out edges we've already traversed. val isRedundant: Boolean = traversedEdges.contains(edge) // if (isRedundant) println(s"filtering out edge from ${edge.sourceNodeIri} to ${edge.targetNodeIri}") hasPermission && !isRedundant - } - .toSet - - // Include only nodes that are reachable via edges that we're going to traverse (i.e. the user - // has permission to see those edges, and we haven't already traversed them). - val visibleNodeIrisFromEdges: Set[IRI] = edges.map(_.sourceNodeIri) ++ edges.map(_.targetNodeIri) - val filteredOtherNodes: Seq[QueryResultNode] = - otherNodes.filter(node => visibleNodeIrisFromEdges.contains(node.nodeIri)) - - // Make a GraphQueryResults containing the resulting nodes and edges, including the start - // node. - val results = GraphQueryResults(nodes = filteredOtherNodes.toSet + startNode, edges = edges) - - // Have we reached the maximum depth? - if (depth == 1) { - // Yes. Just return the results we have. - Future(results) - } else { - // No. Recursively get results for each of the nodes we found. + }.toSet + + // Include only nodes that are reachable via edges that we're going to traverse (i.e. the user + // has permission to see those edges, and we haven't already traversed them). + val visibleNodeIrisFromEdges: Set[IRI] = edges.map(_.sourceNodeIri) ++ edges.map(_.targetNodeIri) + val filteredOtherNodes: Seq[QueryResultNode] = + otherNodes.filter(node => visibleNodeIrisFromEdges.contains(node.nodeIri)) + + // Make a GraphQueryResults containing the resulting nodes and edges, including the start + // node. + val results = GraphQueryResults(nodes = filteredOtherNodes.toSet + startNode, edges = edges) + + // Have we reached the maximum depth? + if (depth == 1) { + // Yes. Just return the results we have. + Future(results) + } else { + // No. Recursively get results for each of the nodes we found. - val traversedEdgesForRecursion: Set[QueryResultEdge] = traversedEdges ++ edges + val traversedEdgesForRecursion: Set[QueryResultEdge] = traversedEdges ++ edges - val lowerResultFutures: Seq[Future[GraphQueryResults]] = filteredOtherNodes.map { node => - traverseGraph( - startNode = node, - outbound = outbound, - depth = depth - 1, - traversedEdges = traversedEdgesForRecursion - ) - } + val lowerResultFutures: Seq[Future[GraphQueryResults]] = filteredOtherNodes.map { node => + traverseGraph( + startNode = node, + outbound = outbound, + depth = depth - 1, + traversedEdges = traversedEdgesForRecursion + ) + } - val lowerResultsFuture: Future[Seq[GraphQueryResults]] = Future.sequence(lowerResultFutures) + val lowerResultsFuture: Future[Seq[GraphQueryResults]] = Future.sequence(lowerResultFutures) - // Return those results plus the ones we found. + // Return those results plus the ones we found. - lowerResultsFuture.map { lowerResultsSeq: Seq[GraphQueryResults] => - lowerResultsSeq.foldLeft(results) { - case (acc: GraphQueryResults, lowerResults: GraphQueryResults) => + lowerResultsFuture.map { lowerResultsSeq: Seq[GraphQueryResults] => + lowerResultsSeq.foldLeft(results) { case (acc: GraphQueryResults, lowerResults: GraphQueryResults) => GraphQueryResults( nodes = acc.nodes ++ lowerResults.nodes, edges = acc.edges ++ lowerResults.edges ) + } } } } - } } yield recursiveResults } @@ -2143,7 +2255,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt outbound = true, limit = settings.maxGraphBreadth ) - .toString()) + .toString() + ) // _ = println(sparql) @@ -2166,39 +2279,44 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ) // Make sure the user has permission to see the start node. - _ = if (PermissionUtilADM - .getUserPermissionADM( - entityCreator = startNode.nodeCreator, - entityProject = startNode.nodeProject, - entityPermissionLiteral = startNode.nodePermissions, - requestingUser = graphDataGetRequest.requestingUser - ) - .isEmpty) { + _ = if ( + PermissionUtilADM + .getUserPermissionADM( + entityCreator = startNode.nodeCreator, + entityProject = startNode.nodeProject, + entityPermissionLiteral = startNode.nodePermissions, + requestingUser = graphDataGetRequest.requestingUser + ) + .isEmpty + ) { throw ForbiddenException( - s"User ${graphDataGetRequest.requestingUser.email} does not have permission to view resource <${graphDataGetRequest.resourceIri}>") + s"User ${graphDataGetRequest.requestingUser.email} does not have permission to view resource <${graphDataGetRequest.resourceIri}>" + ) } // Recursively get the graph containing outbound links. - outboundQueryResults: GraphQueryResults <- if (graphDataGetRequest.outbound) { - traverseGraph( - startNode = startNode, - outbound = true, - depth = graphDataGetRequest.depth - ) - } else { - FastFuture.successful(GraphQueryResults()) - } + outboundQueryResults: GraphQueryResults <- + if (graphDataGetRequest.outbound) { + traverseGraph( + startNode = startNode, + outbound = true, + depth = graphDataGetRequest.depth + ) + } else { + FastFuture.successful(GraphQueryResults()) + } // Recursively get the graph containing inbound links. - inboundQueryResults: GraphQueryResults <- if (graphDataGetRequest.inbound) { - traverseGraph( - startNode = startNode, - outbound = false, - depth = graphDataGetRequest.depth - ) - } else { - FastFuture.successful(GraphQueryResults()) - } + inboundQueryResults: GraphQueryResults <- + if (graphDataGetRequest.inbound) { + traverseGraph( + startNode = startNode, + outbound = false, + depth = graphDataGetRequest.depth + ) + } else { + FastFuture.successful(GraphQueryResults()) + } // Combine the outbound and inbound graphs into a single graph. nodes: Set[QueryResultNode] = outboundQueryResults.nodes ++ inboundQueryResults.nodes + startNode @@ -2209,7 +2327,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt GraphNodeV2( resourceIri = node.nodeIri, resourceClassIri = node.nodeClass, - resourceLabel = node.nodeLabel, + resourceLabel = node.nodeLabel ) }.toVector @@ -2218,26 +2336,26 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt GraphEdgeV2( source = edge.sourceNodeIri, propertyIri = edge.linkProp, - target = edge.targetNodeIri, + target = edge.targetNodeIri ) }.toVector - } yield - GraphDataGetResponseV2( - nodes = resultNodes, - edges = resultEdges, - ontologySchema = InternalSchema - ) + } yield GraphDataGetResponseV2( + nodes = resultNodes, + edges = resultEdges, + ontologySchema = InternalSchema + ) } /** - * Returns the version history of a resource. - * - * @param resourceHistoryRequest the version history request. - * @return the resource's version history. - */ + * Returns the version history of a resource. + * + * @param resourceHistoryRequest the version history request. + * @return the resource's version history. + */ def getResourceHistoryV2( - resourceHistoryRequest: ResourceVersionHistoryGetRequestV2): Future[ResourceVersionHistoryResponseV2] = { + resourceHistoryRequest: ResourceVersionHistoryGetRequestV2 + ): Future[ResourceVersionHistoryResponseV2] = for { // Get the resource preview, to make sure the user has permission to see the resource, and to get // its creation date. @@ -2274,7 +2392,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ResourceHistoryEntry( versionDate = stringFormatter.xsdDateTimeStampToInstant( versionDateStr, - throw InconsistentRepositoryDataException(s"Could not parse version date: $versionDateStr")), + throw InconsistentRepositoryDataException(s"Could not parse version date: $versionDateStr") + ), author = author ) } @@ -2282,28 +2401,26 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Figure out whether to add the resource's creation to the history. // Is there a requested start date that's after the resource's creation date? - historyEntriesWithResourceCreation: Seq[ResourceHistoryEntry] = if (resourceHistoryRequest.startDate.exists( - _.isAfter(resourcePreview.creationDate))) { - // Yes. No need to add the resource's creation. - valueHistoryEntries - } else { - // No. Does the value history contain the resource creation date? - if (valueHistoryEntries.nonEmpty && valueHistoryEntries.last.versionDate == resourcePreview.creationDate) { + historyEntriesWithResourceCreation: Seq[ResourceHistoryEntry] = + if (resourceHistoryRequest.startDate.exists(_.isAfter(resourcePreview.creationDate))) { // Yes. No need to add the resource's creation. valueHistoryEntries } else { - // No. Add a history entry for it. - valueHistoryEntries :+ ResourceHistoryEntry( - versionDate = resourcePreview.creationDate, - author = resourcePreview.attachedToUser - ) + // No. Does the value history contain the resource creation date? + if (valueHistoryEntries.nonEmpty && valueHistoryEntries.last.versionDate == resourcePreview.creationDate) { + // Yes. No need to add the resource's creation. + valueHistoryEntries + } else { + // No. Add a history entry for it. + valueHistoryEntries :+ ResourceHistoryEntry( + versionDate = resourcePreview.creationDate, + author = resourcePreview.attachedToUser + ) + } } - } - } yield - ResourceVersionHistoryResponseV2( - historyEntriesWithResourceCreation - ) - } + } yield ResourceVersionHistoryResponseV2( + historyEntriesWithResourceCreation + ) def getIIIFManifestV2(request: ResourceIIIFManifestGetRequestV2): Future[ResourceIIIFManifestGetResponseV2] = { // The implementation here is experimental. If we had a way of streaming the canvas URLs to the IIIF viewer, @@ -2320,7 +2437,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt .getIncomingImageLinks( resourceIri = request.resourceIri ) - .toString()) + .toString() + ) // Run the query. @@ -2338,92 +2456,91 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt incomingLinks: Seq[ReadValueV2] = resource.values .getOrElse(OntologyConstants.KnoraBase.HasIncomingLinkValue.toSmartIri, Seq.empty) - representations: Seq[ReadResourceV2] = incomingLinks.collect { - case readLinkValueV2: ReadLinkValueV2 => readLinkValueV2.valueContent.nestedResource + representations: Seq[ReadResourceV2] = incomingLinks.collect { case readLinkValueV2: ReadLinkValueV2 => + readLinkValueV2.valueContent.nestedResource }.flatten - } yield - ResourceIIIFManifestGetResponseV2( - JsonLDDocument( - body = JsonLDObject( - Map( - JsonLDKeywords.CONTEXT -> JsonLDString("http://iiif.io/api/presentation/3/context.json"), - "id" -> JsonLDString(s"${request.resourceIri}/manifest"), // Is this IRI OK? - "type" -> JsonLDString("Manifest"), - "label" -> JsonLDObject( - Map( - "en" -> JsonLDArray( - Seq( - JsonLDString(resource.label) - ) + } yield ResourceIIIFManifestGetResponseV2( + JsonLDDocument( + body = JsonLDObject( + Map( + JsonLDKeywords.CONTEXT -> JsonLDString("http://iiif.io/api/presentation/3/context.json"), + "id" -> JsonLDString(s"${request.resourceIri}/manifest"), // Is this IRI OK? + "type" -> JsonLDString("Manifest"), + "label" -> JsonLDObject( + Map( + "en" -> JsonLDArray( + Seq( + JsonLDString(resource.label) ) ) - ), - "behavior" -> JsonLDArray( - Seq( - JsonLDString("paged") - ) - ), - "items" -> JsonLDArray( - representations.map { representation: ReadResourceV2 => - val imageValue: ReadValueV2 = representation.values - .getOrElse( - OntologyConstants.KnoraBase.HasStillImageFileValue.toSmartIri, - throw InconsistentRepositoryDataException( - s"Representation ${representation.resourceIri} has no still image file value") + ) + ), + "behavior" -> JsonLDArray( + Seq( + JsonLDString("paged") + ) + ), + "items" -> JsonLDArray( + representations.map { representation: ReadResourceV2 => + val imageValue: ReadValueV2 = representation.values + .getOrElse( + OntologyConstants.KnoraBase.HasStillImageFileValue.toSmartIri, + throw InconsistentRepositoryDataException( + s"Representation ${representation.resourceIri} has no still image file value" ) - .head + ) + .head - val imageValueContent: StillImageFileValueContentV2 = imageValue.valueContent match { - case stillImageFileValueContentV2: StillImageFileValueContentV2 => stillImageFileValueContentV2 - case _ => throw AssertionException("Expected a StillImageFileValueContentV2") - } + val imageValueContent: StillImageFileValueContentV2 = imageValue.valueContent match { + case stillImageFileValueContentV2: StillImageFileValueContentV2 => stillImageFileValueContentV2 + case _ => throw AssertionException("Expected a StillImageFileValueContentV2") + } - val fileUrl: String = - imageValueContent.makeFileUrl(projectADM = representation.projectADM, settings = settings) - - JsonLDObject( - Map( - "id" -> JsonLDString(s"${representation.resourceIri}/canvas"), // Is this IRI OK? - "type" -> JsonLDString("Canvas"), - "label" -> JsonLDObject( - Map( - "en" -> JsonLDArray( - Seq( - JsonLDString(representation.label) - ) + val fileUrl: String = + imageValueContent.makeFileUrl(projectADM = representation.projectADM, settings = settings) + + JsonLDObject( + Map( + "id" -> JsonLDString(s"${representation.resourceIri}/canvas"), // Is this IRI OK? + "type" -> JsonLDString("Canvas"), + "label" -> JsonLDObject( + Map( + "en" -> JsonLDArray( + Seq( + JsonLDString(representation.label) ) ) - ), - "height" -> JsonLDInt(imageValueContent.dimY), - "width" -> JsonLDInt(imageValueContent.dimX), - "items" -> JsonLDArray( - Seq( - JsonLDObject( - Map( - "id" -> JsonLDString(s"${imageValue.valueIri}/image"), // Is this IRI OK? - "type" -> JsonLDString("AnnotationPage"), - "items" -> JsonLDArray( - Seq( - JsonLDObject( - Map( - "id" -> JsonLDString(imageValue.valueIri), - "type" -> JsonLDString("Annotation"), - "motivation" -> JsonLDString("painting"), - "body" -> JsonLDObject( - Map( - "id" -> JsonLDString(fileUrl), - "type" -> JsonLDString("Image"), - "format" -> JsonLDString("image/jpeg"), - "height" -> JsonLDInt(imageValueContent.dimY), - "width" -> JsonLDInt(imageValueContent.dimX), - "service" -> JsonLDArray( - Seq( - JsonLDObject( - Map( - "id" -> JsonLDString(settings.externalSipiIIIFGetUrl), - "type" -> JsonLDString("ImageService3"), - "profile" -> JsonLDString("level1") - ) + ) + ), + "height" -> JsonLDInt(imageValueContent.dimY), + "width" -> JsonLDInt(imageValueContent.dimX), + "items" -> JsonLDArray( + Seq( + JsonLDObject( + Map( + "id" -> JsonLDString(s"${imageValue.valueIri}/image"), // Is this IRI OK? + "type" -> JsonLDString("AnnotationPage"), + "items" -> JsonLDArray( + Seq( + JsonLDObject( + Map( + "id" -> JsonLDString(imageValue.valueIri), + "type" -> JsonLDString("Annotation"), + "motivation" -> JsonLDString("painting"), + "body" -> JsonLDObject( + Map( + "id" -> JsonLDString(fileUrl), + "type" -> JsonLDString("Image"), + "format" -> JsonLDString("image/jpeg"), + "height" -> JsonLDInt(imageValueContent.dimY), + "width" -> JsonLDInt(imageValueContent.dimX), + "service" -> JsonLDArray( + Seq( + JsonLDObject( + Map( + "id" -> JsonLDString(settings.externalSipiIIIFGetUrl), + "type" -> JsonLDString("ImageService3"), + "profile" -> JsonLDString("level1") ) ) ) @@ -2439,23 +2556,25 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ) ) ) - } - ) + ) + } ) - ), - keepStructure = true - ) + ) + ), + keepStructure = true ) + ) } /** - * Returns all events describing the history of a resource ordered by version date. - * - * @param resourceHistoryEventsGetRequest the request for events describing history of a resource. - * @return the events extracted from full representation of a resource at each time point in its history ordered by version date. - */ - def getResourceHistoryEvents(resourceHistoryEventsGetRequest: ResourceHistoryEventsGetRequestV2) - : Future[ResourceAndValueVersionHistoryResponseV2] = + * Returns all events describing the history of a resource ordered by version date. + * + * @param resourceHistoryEventsGetRequest the request for events describing history of a resource. + * @return the events extracted from full representation of a resource at each time point in its history ordered by version date. + */ + def getResourceHistoryEvents( + resourceHistoryEventsGetRequest: ResourceHistoryEventsGetRequestV2 + ): Future[ResourceAndValueVersionHistoryResponseV2] = for { resourceHistory: ResourceVersionHistoryResponseV2 <- getResourceHistoryV2( ResourceVersionHistoryGetRequestV2( @@ -2463,7 +2582,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt withDeletedResource = true, featureFactoryConfig = resourceHistoryEventsGetRequest.featureFactoryConfig, requestingUser = resourceHistoryEventsGetRequest.requestingUser - )) + ) + ) resourceFullHist: Seq[ResourceAndValueHistoryEvent] <- extractEventsFromHistory( resourceIri = resourceHistoryEventsGetRequest.resourceIri, resourceHistory = resourceHistory.history, @@ -2474,13 +2594,14 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } yield ResourceAndValueVersionHistoryResponseV2(historyEvents = sortedResourceHistory) /** - * Returns events representing the history of all resources and values belonging to a project ordered by date. - * - * @param projectResourceHistoryEventsGetRequest the request for history events of a project. - * @return the all history events of resources of a project ordered by version date. - */ - def getProjectResourceHistoryEvents(projectResourceHistoryEventsGetRequest: ProjectResourcesWithHistoryGetRequestV2) - : Future[ResourceAndValueVersionHistoryResponseV2] = + * Returns events representing the history of all resources and values belonging to a project ordered by date. + * + * @param projectResourceHistoryEventsGetRequest the request for history events of a project. + * @return the all history events of resources of a project ordered by version date. + */ + def getProjectResourceHistoryEvents( + projectResourceHistoryEventsGetRequest: ProjectResourcesWithHistoryGetRequestV2 + ): Future[ResourceAndValueVersionHistoryResponseV2] = for { // Get the project; checks if a project with given IRI exists. projectInfoResponse: ProjectGetResponseADM <- (responderManager ? ProjectGetRequestADM( @@ -2509,7 +2630,8 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt withDeletedResource = true, featureFactoryConfig = projectResourceHistoryEventsGetRequest.featureFactoryConfig, requestingUser = projectResourceHistoryEventsGetRequest.requestingUser - )) + ) + ) resourceFullHist: Seq[ResourceAndValueHistoryEvent] <- extractEventsFromHistory( resourceIri = resourceIri, resourceHistory = resourceHistory.history, @@ -2525,18 +2647,20 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } yield ResourceAndValueVersionHistoryResponseV2(historyEvents = sortedProjectHistory) /** - * Extract events from full representations of resource in each point of its history. - * - * @param resourceIri the IRI of the resource. - * @param resourceHistory the full representations of the resource in each point in its history. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the full history of resource as sequence of [[ResourceAndValueHistoryEvent]]. - */ - def extractEventsFromHistory(resourceIri: IRI, - resourceHistory: Seq[ResourceHistoryEntry], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Seq[ResourceAndValueHistoryEvent]] = + * Extract events from full representations of resource in each point of its history. + * + * @param resourceIri the IRI of the resource. + * @param resourceHistory the full representations of the resource in each point in its history. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the full history of resource as sequence of [[ResourceAndValueHistoryEvent]]. + */ + def extractEventsFromHistory( + resourceIri: IRI, + resourceHistory: Seq[ResourceHistoryEntry], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Seq[ResourceAndValueHistoryEvent]] = for { resourceHist: Seq[ResourceHistoryEntry] <- Future.successful(resourceHistory.reverse) // Collect the full representations of the resource for each version date @@ -2553,16 +2677,17 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // Create an event for the resource at creation time (creationTimeHist, resourceAtCreation) = fullReps.head - resourceCreationEvent: Seq[ResourceAndValueHistoryEvent] = getResourceCreationEvent(resourceAtCreation, - creationTimeHist) + resourceCreationEvent: Seq[ResourceAndValueHistoryEvent] = getResourceCreationEvent( + resourceAtCreation, + creationTimeHist + ) // If there is a version history for deletion of the event, create a delete resource event for it. - (deletionRep, resourceAtValueChanges) = fullReps.tail.partition { - case (resHist, resource) => - resource - .asInstanceOf[ReadResourceV2] - .deletionInfo - .exists(deletionInfo => deletionInfo.deleteDate == resHist.versionDate) + (deletionRep, resourceAtValueChanges) = fullReps.tail.partition { case (resHist, resource) => + resource + .asInstanceOf[ReadResourceV2] + .deletionInfo + .exists(deletionInfo => deletionInfo.deleteDate == resHist.versionDate) } resourceDeleteEvent = getResourceDeletionEvents(deletionRep) @@ -2575,23 +2700,26 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt resourceMetadataUpdateEvent: Seq[ResourceAndValueHistoryEvent] = getResourceMetadataUpdateEvent( fullReps.last, valuesEvents, - resourceDeleteEvent) + resourceDeleteEvent + ) } yield resourceCreationEvent ++ resourceDeleteEvent ++ valuesEvents ++ resourceMetadataUpdateEvent /** - * Returns the full representation of a resource at a given date. - * - * @param resourceIri the IRI of the resource. - * @param versionHist the history info of the version; i.e. versionDate and author. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the full representation of the resource at the given version date. - */ - private def getResourceAtGivenTime(resourceIri: IRI, - versionHist: ResourceHistoryEntry, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[(ResourceHistoryEntry, ReadResourceV2)] = + * Returns the full representation of a resource at a given date. + * + * @param resourceIri the IRI of the resource. + * @param versionHist the history info of the version; i.e. versionDate and author. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the full representation of the resource at the given version date. + */ + private def getResourceAtGivenTime( + resourceIri: IRI, + versionHist: ResourceHistoryEntry, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[(ResourceHistoryEntry, ReadResourceV2)] = for { resourceFullRepAtCreationTime: ReadResourcesSequenceV2 <- getResourcesV2( resourceIris = Seq(resourceIri), @@ -2606,15 +2734,16 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } yield versionHist -> resourceAtCreationTime /** - * Returns a createResource event as [[ResourceAndValueHistoryEvent]] with request body of the form [[ResourceEventBody]]. - * - * @param resourceAtTimeOfCreation the full representation of the resource at creation date. - * @param versionInfoAtCreation the history info of the version; i.e. versionDate and author. - * @return a createResource event. - */ + * Returns a createResource event as [[ResourceAndValueHistoryEvent]] with request body of the form [[ResourceEventBody]]. + * + * @param resourceAtTimeOfCreation the full representation of the resource at creation date. + * @param versionInfoAtCreation the history info of the version; i.e. versionDate and author. + * @return a createResource event. + */ private def getResourceCreationEvent( - resourceAtTimeOfCreation: ReadResourceV2, - versionInfoAtCreation: ResourceHistoryEntry): Seq[ResourceAndValueHistoryEvent] = { + resourceAtTimeOfCreation: ReadResourceV2, + versionInfoAtCreation: ResourceHistoryEntry + ): Seq[ResourceAndValueHistoryEvent] = { val requestBody: ResourceEventBody = ResourceEventBody( resourceIri = resourceAtTimeOfCreation.resourceIri, @@ -2634,48 +2763,49 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt versionDate = versionInfoAtCreation.versionDate, author = versionInfoAtCreation.author, eventBody = requestBody - )) + ) + ) } /** - * Returns resourceDeletion events as Seq[[ResourceAndValueHistoryEvent]] with request body of the form [[ResourceEventBody]]. - * - * @param resourceDeletionInfo A sequence of resource deletion info containing version history of deletion and - * the full representation of resource at time of deletion. - * @return a seq of deleteResource events. - */ + * Returns resourceDeletion events as Seq[[ResourceAndValueHistoryEvent]] with request body of the form [[ResourceEventBody]]. + * + * @param resourceDeletionInfo A sequence of resource deletion info containing version history of deletion and + * the full representation of resource at time of deletion. + * @return a seq of deleteResource events. + */ private def getResourceDeletionEvents( - resourceDeletionInfo: Seq[(ResourceHistoryEntry, ReadResourceV2)]): Seq[ResourceAndValueHistoryEvent] = { - resourceDeletionInfo.map { - case (delHist, fullRepresentation) => - val requestBody: ResourceEventBody = ResourceEventBody( - resourceIri = fullRepresentation.resourceIri, - resourceClassIri = fullRepresentation.resourceClassIri, - projectADM = fullRepresentation.projectADM, - lastModificationDate = fullRepresentation.lastModificationDate, - deletionInfo = fullRepresentation.deletionInfo - ) - ResourceAndValueHistoryEvent( - eventType = ResourceAndValueEventsUtil.DELETE_RESOURCE_EVENT, - versionDate = delHist.versionDate, - author = delHist.author, - eventBody = requestBody - ) + resourceDeletionInfo: Seq[(ResourceHistoryEntry, ReadResourceV2)] + ): Seq[ResourceAndValueHistoryEvent] = + resourceDeletionInfo.map { case (delHist, fullRepresentation) => + val requestBody: ResourceEventBody = ResourceEventBody( + resourceIri = fullRepresentation.resourceIri, + resourceClassIri = fullRepresentation.resourceClassIri, + projectADM = fullRepresentation.projectADM, + lastModificationDate = fullRepresentation.lastModificationDate, + deletionInfo = fullRepresentation.deletionInfo + ) + ResourceAndValueHistoryEvent( + eventType = ResourceAndValueEventsUtil.DELETE_RESOURCE_EVENT, + versionDate = delHist.versionDate, + author = delHist.author, + eventBody = requestBody + ) } - } /** - * Returns a value event as [[ResourceAndValueHistoryEvent]] with body of the form [[ValueEventBody]]. - * - * @param resourceAtGivenTime the full representation of the resource at the given time. - * @param versionHist the history info of the version; i.e. versionDate and author. - * @param allResourceVersions all full representations of resource for each version date in its history. - * @return a create/update/delete value event. - */ + * Returns a value event as [[ResourceAndValueHistoryEvent]] with body of the form [[ValueEventBody]]. + * + * @param resourceAtGivenTime the full representation of the resource at the given time. + * @param versionHist the history info of the version; i.e. versionDate and author. + * @param allResourceVersions all full representations of resource for each version date in its history. + * @return a create/update/delete value event. + */ private def getValueEvents( - resourceAtGivenTime: ReadResourceV2, - versionHist: ResourceHistoryEntry, - allResourceVersions: Seq[(ResourceHistoryEntry, ReadResourceV2)]): Seq[ResourceAndValueHistoryEvent] = { + resourceAtGivenTime: ReadResourceV2, + versionHist: ResourceHistoryEntry, + allResourceVersions: Seq[(ResourceHistoryEntry, ReadResourceV2)] + ): Seq[ResourceAndValueHistoryEvent] = { val resourceIri = resourceAtGivenTime.resourceIri /** returns the values of the resource which have the given version date. */ @@ -2685,7 +2815,9 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt val valuesWithGivenVersion: Seq[ReadValueV2] = readValue.filter(readValue => readValue.valueCreationDate == versionHist.versionDate || readValue.deletionInfo.exists(deleteInfo => - deleteInfo.deleteDate == versionHist.versionDate)) + deleteInfo.deleteDate == versionHist.versionDate + ) + ) if (valuesWithGivenVersion.nonEmpty) { acc + (propIri -> valuesWithGivenVersion.head) } else { acc } @@ -2696,100 +2828,106 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } val valuesWithAskedVersionDate: Map[SmartIri, ReadValueV2] = findValuesWithGivenVersionDate( - resourceAtGivenTime.values) - val valueEvents: Seq[ResourceAndValueHistoryEvent] = valuesWithAskedVersionDate.map { - case (propIri, readValue) => - val event = - //Is the given date a deletion date? - if (readValue.deletionInfo.exists(deletionInfo => deletionInfo.deleteDate == versionHist.versionDate)) { - // Yes. Return a deleteValue event - val deleteValueRequestBody = ValueEventBody( + resourceAtGivenTime.values + ) + val valueEvents: Seq[ResourceAndValueHistoryEvent] = valuesWithAskedVersionDate.map { case (propIri, readValue) => + val event = + //Is the given date a deletion date? + if (readValue.deletionInfo.exists(deletionInfo => deletionInfo.deleteDate == versionHist.versionDate)) { + // Yes. Return a deleteValue event + val deleteValueRequestBody = ValueEventBody( + resourceIri = resourceIri, + resourceClassIri = resourceAtGivenTime.resourceClassIri, + projectADM = resourceAtGivenTime.projectADM, + propertyIri = propIri, + valueIri = readValue.valueIri, + valueTypeIri = readValue.valueContent.valueType, + deletionInfo = readValue.deletionInfo, + previousValueIri = readValue.previousValueIri + ) + ResourceAndValueHistoryEvent( + eventType = ResourceAndValueEventsUtil.DELETE_VALUE_EVENT, + versionDate = versionHist.versionDate, + author = versionHist.author, + eventBody = deleteValueRequestBody + ) + } else { + // No. Is the given date a creation date, i.e. value does not have a previous version? + if (readValue.previousValueIri.isEmpty) { + // Yes. return a createValue event with its request body + val createValueRequestBody = ValueEventBody( resourceIri = resourceIri, resourceClassIri = resourceAtGivenTime.resourceClassIri, projectADM = resourceAtGivenTime.projectADM, propertyIri = propIri, valueIri = readValue.valueIri, valueTypeIri = readValue.valueContent.valueType, - deletionInfo = readValue.deletionInfo, - previousValueIri = readValue.previousValueIri + valueContent = Some(readValue.valueContent), + valueUUID = Some(readValue.valueHasUUID), + valueCreationDate = Some(readValue.valueCreationDate), + permissions = Some(readValue.permissions), + valueComment = readValue.valueContent.comment ) ResourceAndValueHistoryEvent( - eventType = ResourceAndValueEventsUtil.DELETE_VALUE_EVENT, + eventType = ResourceAndValueEventsUtil.CREATE_VALUE_EVENT, versionDate = versionHist.versionDate, author = versionHist.author, - eventBody = deleteValueRequestBody + eventBody = createValueRequestBody ) } else { - // No. Is the given date a creation date, i.e. value does not have a previous version? - if (readValue.previousValueIri.isEmpty) { - // Yes. return a createValue event with its request body - val createValueRequestBody = ValueEventBody( - resourceIri = resourceIri, - resourceClassIri = resourceAtGivenTime.resourceClassIri, - projectADM = resourceAtGivenTime.projectADM, - propertyIri = propIri, - valueIri = readValue.valueIri, - valueTypeIri = readValue.valueContent.valueType, - valueContent = Some(readValue.valueContent), - valueUUID = Some(readValue.valueHasUUID), - valueCreationDate = Some(readValue.valueCreationDate), - permissions = Some(readValue.permissions), - valueComment = readValue.valueContent.comment - ) - ResourceAndValueHistoryEvent( - eventType = ResourceAndValueEventsUtil.CREATE_VALUE_EVENT, - versionDate = versionHist.versionDate, - author = versionHist.author, - eventBody = createValueRequestBody - ) - } else { - // No. return updateValue event - val (updateEventType: String, updateEventRequestBody: ValueEventBody) = - getValueUpdateEventType(propIri, readValue, allResourceVersions, resourceAtGivenTime) - ResourceAndValueHistoryEvent( - eventType = updateEventType, - versionDate = versionHist.versionDate, - author = versionHist.author, - eventBody = updateEventRequestBody - ) - } + // No. return updateValue event + val (updateEventType: String, updateEventRequestBody: ValueEventBody) = + getValueUpdateEventType(propIri, readValue, allResourceVersions, resourceAtGivenTime) + ResourceAndValueHistoryEvent( + eventType = updateEventType, + versionDate = versionHist.versionDate, + author = versionHist.author, + eventBody = updateEventRequestBody + ) } - event + } + event }.toSeq valueEvents } /** - * Since update value operation can be used to update value content or value permissions, using the previous versions - * of the value, it determines the type of the update and returns eventType: updateValuePermission/updateValueContent - * together with the request body necessary to do the update. - * - * @param propertyIri the IRI of the property. - * @param currentVersionOfValue the current value version. - * @param allResourceVersions all versions of resource. - * @param resourceAtGivenTime the full representation of the resource at time of value update. - * @return (eventType, update event request body) - */ - private def getValueUpdateEventType(propertyIri: SmartIri, - currentVersionOfValue: ReadValueV2, - allResourceVersions: Seq[(ResourceHistoryEntry, ReadResourceV2)], - resourceAtGivenTime: ReadResourceV2): (String, ValueEventBody) = { + * Since update value operation can be used to update value content or value permissions, using the previous versions + * of the value, it determines the type of the update and returns eventType: updateValuePermission/updateValueContent + * together with the request body necessary to do the update. + * + * @param propertyIri the IRI of the property. + * @param currentVersionOfValue the current value version. + * @param allResourceVersions all versions of resource. + * @param resourceAtGivenTime the full representation of the resource at time of value update. + * @return (eventType, update event request body) + */ + private def getValueUpdateEventType( + propertyIri: SmartIri, + currentVersionOfValue: ReadValueV2, + allResourceVersions: Seq[(ResourceHistoryEntry, ReadResourceV2)], + resourceAtGivenTime: ReadResourceV2 + ): (String, ValueEventBody) = { val previousValueIri: IRI = currentVersionOfValue.previousValueIri.getOrElse( - throw BadRequestException("No previous value IRI found for the value, Please report this as a bug.")) + throw BadRequestException("No previous value IRI found for the value, Please report this as a bug.") + ) //find the version of resource which has a value with previousValueIri val (previousVersionDate, previousVersionOfResource): (ResourceHistoryEntry, ReadResourceV2) = allResourceVersions .find(resourceWithHist => resourceWithHist._2.values.exists(item => - item._1 == propertyIri && item._2.exists(value => value.valueIri == previousValueIri))) + item._1 == propertyIri && item._2.exists(value => value.valueIri == previousValueIri) + ) + ) .getOrElse(throw NotFoundException(s"Could not find the previous value of ${currentVersionOfValue.valueIri}")) // check that the version date of the previousValue is before the version date of the current value. if (previousVersionDate.versionDate.isAfter(currentVersionOfValue.valueCreationDate)) { throw ForbiddenException( s"Previous version of the value ${currentVersionOfValue.valueIri} that has previousValueIRI ${previousValueIri} " + - s"has a date after the current value.") + s"has a date after the current value." + ) } // get the previous value @@ -2831,19 +2969,20 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Returns an updateResourceMetadata event as [[ResourceAndValueHistoryEvent]] with request body of the form - * [[ResourceMetadataEventBody]] with information necessary to make update metadata of resource request with a - * given modification date. - * - * @param latestVersionOfResource the full representation of the resource. - * @param valueEvents the events describing value operations. - * @param resourceDeleteEvents the events describing resource deletion operations. - * @return an updateResourceMetadata event. - */ + * Returns an updateResourceMetadata event as [[ResourceAndValueHistoryEvent]] with request body of the form + * [[ResourceMetadataEventBody]] with information necessary to make update metadata of resource request with a + * given modification date. + * + * @param latestVersionOfResource the full representation of the resource. + * @param valueEvents the events describing value operations. + * @param resourceDeleteEvents the events describing resource deletion operations. + * @return an updateResourceMetadata event. + */ private def getResourceMetadataUpdateEvent( - latestVersionOfResource: (ResourceHistoryEntry, ReadResourceV2), - valueEvents: Seq[ResourceAndValueHistoryEvent], - resourceDeleteEvents: Seq[ResourceAndValueHistoryEvent]): Seq[ResourceAndValueHistoryEvent] = { + latestVersionOfResource: (ResourceHistoryEntry, ReadResourceV2), + valueEvents: Seq[ResourceAndValueHistoryEvent], + resourceDeleteEvents: Seq[ResourceAndValueHistoryEvent] + ): Seq[ResourceAndValueHistoryEvent] = { val readResource: ReadResourceV2 = latestVersionOfResource._2 val author: IRI = latestVersionOfResource._1.author // Is lastModificationDate of resource None diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResponderWithStandoffV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResponderWithStandoffV2.scala index 8a195a09ec..ae584ae2a8 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResponderWithStandoffV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResponderWithStandoffV2.scala @@ -38,27 +38,27 @@ import org.knora.webapi.responders.Responder import scala.concurrent.Future /** - * An abstract class with standoff utility methods for v2 responders. - */ + * An abstract class with standoff utility methods for v2 responders. + */ abstract class ResponderWithStandoffV2(responderData: ResponderData) extends Responder(responderData) { /** - * Gets mappings referred to in query results [[Map[IRI, ResourceWithValueRdfData]]]. - * - * @param queryResultsSeparated query results referring to mappings. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the referred mappings. - */ + * Gets mappings referred to in query results [[Map[IRI, ResourceWithValueRdfData]]]. + * + * @param queryResultsSeparated query results referring to mappings. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the referred mappings. + */ protected def getMappingsFromQueryResultsSeparated( - queryResultsSeparated: Map[IRI, ResourceWithValueRdfData], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Map[IRI, MappingAndXSLTransformation]] = { + queryResultsSeparated: Map[IRI, ResourceWithValueRdfData], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Map[IRI, MappingAndXSLTransformation]] = { // collect the Iris of the mappings referred to in the resources' text values - val mappingIris: Set[IRI] = queryResultsSeparated.flatMap { - case (_, assertions: ResourceWithValueRdfData) => - ConstructResponseUtilV2.getMappingIrisFromValuePropertyAssertions(assertions.valuePropertyAssertions) + val mappingIris: Set[IRI] = queryResultsSeparated.flatMap { case (_, assertions: ResourceWithValueRdfData) => + ConstructResponseUtilV2.getMappingIrisFromValuePropertyAssertions(assertions.valuePropertyAssertions) }.toSet // get all the mappings @@ -80,29 +80,33 @@ abstract class ResponderWithStandoffV2(responderData: ResponderData) extends Res mapping: GetMappingResponseV2 => for { // if given, get the default XSL transformation - xsltOption: Option[String] <- if (mapping.mapping.defaultXSLTransformation.nonEmpty) { - val xslTransformationFuture = for { - xslTransformation: GetXSLTransformationResponseV2 <- (responderManager ? GetXSLTransformationRequestV2( - mapping.mapping.defaultXSLTransformation.get, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - )).mapTo[GetXSLTransformationResponseV2] - } yield Some(xslTransformation.xslt) + xsltOption: Option[String] <- + if (mapping.mapping.defaultXSLTransformation.nonEmpty) { + val xslTransformationFuture = for { + xslTransformation: GetXSLTransformationResponseV2 <- + (responderManager ? GetXSLTransformationRequestV2( + mapping.mapping.defaultXSLTransformation.get, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + )).mapTo[GetXSLTransformationResponseV2] + } yield Some(xslTransformation.xslt) - xslTransformationFuture.recover { - case notFound: NotFoundException => - throw SipiException( - s"Default XSL transformation <${mapping.mapping.defaultXSLTransformation.get}> not found for mapping <${mapping.mappingIri}>: ${notFound.message}") + xslTransformationFuture.recover { + case notFound: NotFoundException => + throw SipiException( + s"Default XSL transformation <${mapping.mapping.defaultXSLTransformation.get}> not found for mapping <${mapping.mappingIri}>: ${notFound.message}" + ) - case other => throw other + case other => throw other + } + } else { + FastFuture.successful(None) } - } else { - FastFuture.successful(None) - } - } yield - mapping.mappingIri -> MappingAndXSLTransformation(mapping = mapping.mapping, - standoffEntities = mapping.standoffEntities, - XSLTransformation = xsltOption) + } yield mapping.mappingIri -> MappingAndXSLTransformation( + mapping = mapping.mapping, + standoffEntities = mapping.standoffEntities, + XSLTransformation = xsltOption + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/SearchResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/SearchResponderV2.scala index 12f5e3c782..e03fcfd49a 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/SearchResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/SearchResponderV2.scala @@ -65,32 +65,38 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand private val gravsearchTypeInspectionRunner = new GravsearchTypeInspectionRunner(responderData) /** - * Receives a message of type [[SearchResponderRequestV2]], and returns an appropriate response message. - */ + * Receives a message of type [[SearchResponderRequestV2]], and returns an appropriate response message. + */ def receive(msg: SearchResponderRequestV2): Future[KnoraJsonLDResponseV2] = msg match { - case FullTextSearchCountRequestV2(searchValue, - limitToProject, - limitToResourceClass, - limitToStandoffClass, - featureFactoryConfig, - requestingUser) => - fulltextSearchCountV2(searchValue, - limitToProject, - limitToResourceClass, - limitToStandoffClass, - featureFactoryConfig, - requestingUser) - - case FulltextSearchRequestV2(searchValue, - offset, - limitToProject, - limitToResourceClass, - limitToStandoffClass, - returnFiles, - targetSchema, - schemaOptions, - featureFactoryConfig, - requestingUser) => + case FullTextSearchCountRequestV2( + searchValue, + limitToProject, + limitToResourceClass, + limitToStandoffClass, + featureFactoryConfig, + requestingUser + ) => + fulltextSearchCountV2( + searchValue, + limitToProject, + limitToResourceClass, + limitToStandoffClass, + featureFactoryConfig, + requestingUser + ) + + case FulltextSearchRequestV2( + searchValue, + offset, + limitToProject, + limitToResourceClass, + limitToStandoffClass, + returnFiles, + targetSchema, + schemaOptions, + featureFactoryConfig, + requestingUser + ) => fulltextSearchV2( searchValue, offset, @@ -105,42 +111,54 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand ) case GravsearchCountRequestV2(query, featureFactoryConfig, requestingUser) => - gravsearchCountV2(inputQuery = query, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser) + gravsearchCountV2( + inputQuery = query, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) case GravsearchRequestV2(query, targetSchema, schemaOptions, featureFactoryConfig, requestingUser) => - gravsearchV2(inputQuery = query, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser) - - case SearchResourceByLabelCountRequestV2(searchValue, - limitToProject, - limitToResourceClass, - featureFactoryConfig, - requestingUser) => - searchResourcesByLabelCountV2(searchValue, - limitToProject, - limitToResourceClass, - featureFactoryConfig, - requestingUser) - - case SearchResourceByLabelRequestV2(searchValue, - offset, - limitToProject, - limitToResourceClass, - targetSchema, - featureFactoryConfig, - requestingUser) => - searchResourcesByLabelV2(searchValue, - offset, - limitToProject, - limitToResourceClass, - targetSchema, - featureFactoryConfig, - requestingUser) + gravsearchV2( + inputQuery = query, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + case SearchResourceByLabelCountRequestV2( + searchValue, + limitToProject, + limitToResourceClass, + featureFactoryConfig, + requestingUser + ) => + searchResourcesByLabelCountV2( + searchValue, + limitToProject, + limitToResourceClass, + featureFactoryConfig, + requestingUser + ) + + case SearchResourceByLabelRequestV2( + searchValue, + offset, + limitToProject, + limitToResourceClass, + targetSchema, + featureFactoryConfig, + requestingUser + ) => + searchResourcesByLabelV2( + searchValue, + offset, + limitToProject, + limitToResourceClass, + targetSchema, + featureFactoryConfig, + requestingUser + ) case resourcesInProjectGetRequestV2: SearchResourcesByProjectAndClassRequestV2 => searchResourcesByProjectAndClassV2(resourcesInProjectGetRequestV2) @@ -149,24 +167,26 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } /** - * Performs a fulltext search and returns the resources count (how many resources match the search criteria), - * without taking into consideration permission checking. - * - * This method does not return the resources themselves. - * - * @param searchValue the values to search for. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the client making the request. - * @return a [[ResourceCountV2]] representing the number of resources that have been found. - */ - private def fulltextSearchCountV2(searchValue: String, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - limitToStandoffClass: Option[SmartIri], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ResourceCountV2] = { + * Performs a fulltext search and returns the resources count (how many resources match the search criteria), + * without taking into consideration permission checking. + * + * This method does not return the resources themselves. + * + * @param searchValue the values to search for. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the client making the request. + * @return a [[ResourceCountV2]] representing the number of resources that have been found. + */ + private def fulltextSearchCountV2( + searchValue: String, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + limitToStandoffClass: Option[SmartIri], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ResourceCountV2] = { val searchTerms: LuceneQueryString = LuceneQueryString(searchValue) @@ -185,7 +205,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand offset = 0, countQuery = true // do not get the resources themselves, but the sum of results ) - .toString()) + .toString() + ) // _ = println(countSparql) @@ -194,7 +215,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // query response should contain one result with one row with the name "count" _ = if (countResponse.results.bindings.length != 1) { throw GravsearchException( - s"Fulltext count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given") + s"Fulltext count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given" + ) } count = countResponse.results.bindings.head.rowMap("count") @@ -203,29 +225,31 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } /** - * Performs a fulltext search (simple search). - * - * @param searchValue the values to search for. - * @param offset the offset to be used for paging. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param returnFiles if true, return any file value attached to each matching resource. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the client making the request. - * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. - */ - private def fulltextSearchV2(searchValue: String, - offset: Int, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - limitToStandoffClass: Option[SmartIri], - returnFiles: Boolean, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourcesSequenceV2] = { + * Performs a fulltext search (simple search). + * + * @param searchValue the values to search for. + * @param offset the offset to be used for paging. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param returnFiles if true, return any file value attached to each matching resource. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the client making the request. + * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. + */ + private def fulltextSearchV2( + searchValue: String, + offset: Int, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + limitToStandoffClass: Option[SmartIri], + returnFiles: Boolean, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourcesSequenceV2] = { import org.knora.webapi.messages.util.search.FullTextMainQueryGenerator.FullTextSearchConstants val groupConcatSeparator = StringFormatter.INFORMATION_SEPARATOR_ONE @@ -247,7 +271,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand offset = offset * settings.v2ResultsPerPage, // determine the actual offset countQuery = false ) - .toString()) + .toString() + ) // _ = println(searchSparql) @@ -267,88 +292,95 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } // If the prequery returned some results, prepare a main query. - mainResourcesAndValueRdfData: ConstructResponseUtilV2.MainResourcesAndValueRdfData <- if (resourceIris.nonEmpty) { + mainResourcesAndValueRdfData: ConstructResponseUtilV2.MainResourcesAndValueRdfData <- + if (resourceIris.nonEmpty) { - // for each resource, create a Set of value object IRIs - val valueObjectIrisPerResource: Map[IRI, Set[IRI]] = - prequeryResponse.results.bindings.foldLeft(Map.empty[IRI, Set[IRI]]) { - (acc: Map[IRI, Set[IRI]], resultRow: VariableResultsRow) => - val mainResIri: IRI = resultRow.rowMap(FullTextSearchConstants.resourceVar.variableName) + // for each resource, create a Set of value object IRIs + val valueObjectIrisPerResource: Map[IRI, Set[IRI]] = + prequeryResponse.results.bindings.foldLeft(Map.empty[IRI, Set[IRI]]) { + (acc: Map[IRI, Set[IRI]], resultRow: VariableResultsRow) => + val mainResIri: IRI = resultRow.rowMap(FullTextSearchConstants.resourceVar.variableName) - resultRow.rowMap.get(FullTextSearchConstants.valueObjectConcatVar.variableName) match { + resultRow.rowMap.get(FullTextSearchConstants.valueObjectConcatVar.variableName) match { - case Some(valObjIris) => - // Filter out empty IRIs (which we could get if a variable used in GROUP_CONCAT is unbound) - acc + (mainResIri -> valObjIris.split(groupConcatSeparator).toSet.filterNot(_.isEmpty)) + case Some(valObjIris) => + // Filter out empty IRIs (which we could get if a variable used in GROUP_CONCAT is unbound) + acc + (mainResIri -> valObjIris.split(groupConcatSeparator).toSet.filterNot(_.isEmpty)) - case None => acc - } - } + case None => acc + } + } - // println(valueObjectIrisPerResource) + // println(valueObjectIrisPerResource) - // collect all value object IRIs - val allValueObjectIris = valueObjectIrisPerResource.values.flatten.toSet + // collect all value object IRIs + val allValueObjectIris = valueObjectIrisPerResource.values.flatten.toSet - // create a CONSTRUCT query to query resources and their values - val mainQuery = FullTextMainQueryGenerator.createMainQuery( - resourceIris = resourceIris.toSet, - valueObjectIris = allValueObjectIris, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - settings = settings - ) + // create a CONSTRUCT query to query resources and their values + val mainQuery = FullTextMainQueryGenerator.createMainQuery( + resourceIris = resourceIris.toSet, + valueObjectIris = allValueObjectIris, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + settings = settings + ) - val triplestoreSpecificQueryPatternTransformerConstruct: ConstructToConstructTransformer = { - if (settings.triplestoreType.startsWith("graphdb")) { - // GraphDB - new SparqlTransformer.GraphDBConstructToConstructTransformer - } else { - // Other - new SparqlTransformer.NoInferenceConstructToConstructTransformer + val triplestoreSpecificQueryPatternTransformerConstruct: ConstructToConstructTransformer = { + if (settings.triplestoreType.startsWith("graphdb")) { + // GraphDB + new SparqlTransformer.GraphDBConstructToConstructTransformer + } else { + // Other + new SparqlTransformer.NoInferenceConstructToConstructTransformer + } } - } - val triplestoreSpecificQuery = QueryTraverser.transformConstructToConstruct( - inputQuery = mainQuery, - transformer = triplestoreSpecificQueryPatternTransformerConstruct - ) + val triplestoreSpecificQuery = QueryTraverser.transformConstructToConstruct( + inputQuery = mainQuery, + transformer = triplestoreSpecificQueryPatternTransformerConstruct + ) - // println(triplestoreSpecificQuery.toSparql) + // println(triplestoreSpecificQuery.toSparql) - for { - searchResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = triplestoreSpecificQuery.toSparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] + for { + searchResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( + sparql = triplestoreSpecificQuery.toSparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] - // separate resources and value objects - queryResultsSep: ConstructResponseUtilV2.MainResourcesAndValueRdfData = ConstructResponseUtilV2 - .splitMainResourcesAndValueRdfData(constructQueryResults = searchResponse, requestingUser = requestingUser) - } yield queryResultsSep - } else { + // separate resources and value objects + queryResultsSep: ConstructResponseUtilV2.MainResourcesAndValueRdfData = ConstructResponseUtilV2 + .splitMainResourcesAndValueRdfData( + constructQueryResults = searchResponse, + requestingUser = requestingUser + ) + } yield queryResultsSep + } else { - // the prequery returned no results, no further query is necessary - Future( - ConstructResponseUtilV2.MainResourcesAndValueRdfData(resources = Map.empty) - ) - } + // the prequery returned no results, no further query is necessary + Future( + ConstructResponseUtilV2.MainResourcesAndValueRdfData(resources = Map.empty) + ) + } // Find out whether to query standoff along with text values. This boolean value will be passed to // ConstructResponseUtilV2.makeTextValueContentV2. - queryStandoff: Boolean = SchemaOptions.queryStandoffWithTextValues(targetSchema = targetSchema, - schemaOptions = schemaOptions) + queryStandoff: Boolean = SchemaOptions.queryStandoffWithTextValues( + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) // If we're querying standoff, get XML-to standoff mappings. - mappingsAsMap: Map[IRI, MappingAndXSLTransformation] <- if (queryStandoff) { - getMappingsFromQueryResultsSeparated( - queryResultsSeparated = mainResourcesAndValueRdfData.resources, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - } else { - FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) - } + mappingsAsMap: Map[IRI, MappingAndXSLTransformation] <- + if (queryStandoff) { + getMappingsFromQueryResultsSeparated( + queryResultsSeparated = mainResourcesAndValueRdfData.resources, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } else { + FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) + } // _ = println(mappingsAsMap) @@ -371,16 +403,18 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } /** - * Performs a count query for a Gravsearch query provided by the user. - * - * @param inputQuery a Gravsearch query provided by the client. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the client making the request. - * @return a [[ResourceCountV2]] representing the number of resources that have been found. - */ - private def gravsearchCountV2(inputQuery: ConstructQuery, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ResourceCountV2] = { + * Performs a count query for a Gravsearch query provided by the user. + * + * @param inputQuery a Gravsearch query provided by the client. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the client making the request. + * @return a [[ResourceCountV2]] representing the number of resources that have been found. + */ + private def gravsearchCountV2( + inputQuery: ConstructQuery, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ResourceCountV2] = { // make sure that OFFSET is 0 if (inputQuery.offset != 0) @@ -391,10 +425,12 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // Do type inspection and remove type annotations from the WHERE clause. typeInspectionResult: GravsearchTypeInspectionResult <- gravsearchTypeInspectionRunner.inspectTypes( inputQuery.whereClause, - requestingUser) + requestingUser + ) whereClauseWithoutAnnotations: WhereClause = GravsearchTypeInspectionUtil.removeTypeAnnotations( - inputQuery.whereClause) + inputQuery.whereClause + ) // Validate schemas and predicates in the CONSTRUCT clause. _ = GravsearchQueryChecker.checkConstructClause( @@ -404,12 +440,13 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // Create a Select prequery - nonTriplestoreSpecificConstructToSelectTransformer: NonTriplestoreSpecificGravsearchToCountPrequeryTransformer = new NonTriplestoreSpecificGravsearchToCountPrequeryTransformer( - constructClause = inputQuery.constructClause, - typeInspectionResult = typeInspectionResult, - querySchema = inputQuery.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema")), - featureFactoryConfig = featureFactoryConfig - ) + nonTriplestoreSpecificConstructToSelectTransformer: NonTriplestoreSpecificGravsearchToCountPrequeryTransformer = + new NonTriplestoreSpecificGravsearchToCountPrequeryTransformer( + constructClause = inputQuery.constructClause, + typeInspectionResult = typeInspectionResult, + querySchema = inputQuery.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema")), + featureFactoryConfig = featureFactoryConfig + ) nonTriplestoreSpecificPrequery: SelectQuery = QueryTraverser.transformConstructToSelect( inputQuery = inputQuery.copy( @@ -448,7 +485,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // query response should contain one result with one row with the name "count" _ = if (countResponse.results.bindings.length != 1) { throw GravsearchException( - s"Count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given") + s"Count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given" + ) } count: String = countResponse.results.bindings.head.rowMap("count") @@ -458,29 +496,33 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } /** - * Performs a search using a Gravsearch query provided by the client. - * - * @param inputQuery a Gravsearch query provided by the client. - * @param targetSchema the target API schema. - * @param schemaOptions the schema options submitted with the request. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the client making the request. - * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. - */ - private def gravsearchV2(inputQuery: ConstructQuery, - targetSchema: ApiV2Schema, - schemaOptions: Set[SchemaOption], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourcesSequenceV2] = { + * Performs a search using a Gravsearch query provided by the client. + * + * @param inputQuery a Gravsearch query provided by the client. + * @param targetSchema the target API schema. + * @param schemaOptions the schema options submitted with the request. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the client making the request. + * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. + */ + private def gravsearchV2( + inputQuery: ConstructQuery, + targetSchema: ApiV2Schema, + schemaOptions: Set[SchemaOption], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourcesSequenceV2] = { import org.knora.webapi.messages.util.search.gravsearch.mainquery.GravsearchMainQueryGenerator for { // Do type inspection and remove type annotations from the WHERE clause. typeInspectionResult: GravsearchTypeInspectionResult <- gravsearchTypeInspectionRunner.inspectTypes( inputQuery.whereClause, - requestingUser) + requestingUser + ) whereClauseWithoutAnnotations: WhereClause = GravsearchTypeInspectionUtil.removeTypeAnnotations( - inputQuery.whereClause) + inputQuery.whereClause + ) // Validate schemas and predicates in the CONSTRUCT clause. _ = GravsearchQueryChecker.checkConstructClause( @@ -490,13 +532,14 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // Create a Select prequery - nonTriplestoreSpecificConstructToSelectTransformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer = new NonTriplestoreSpecificGravsearchToPrequeryTransformer( - constructClause = inputQuery.constructClause, - typeInspectionResult = typeInspectionResult, - querySchema = inputQuery.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema")), - settings = settings, - featureFactoryConfig = featureFactoryConfig - ) + nonTriplestoreSpecificConstructToSelectTransformer: NonTriplestoreSpecificGravsearchToPrequeryTransformer = + new NonTriplestoreSpecificGravsearchToPrequeryTransformer( + constructClause = inputQuery.constructClause, + typeInspectionResult = typeInspectionResult, + querySchema = inputQuery.querySchema.getOrElse(throw AssertionException(s"WhereClause has no querySchema")), + settings = settings, + featureFactoryConfig = featureFactoryConfig + ) // TODO: if the ORDER BY criterion is a property whose occurrence is not 1, then the logic does not work correctly // TODO: the ORDER BY criterion has to be included in a GROUP BY statement, returning more than one row if property occurs more than once @@ -535,12 +578,15 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand _ = log.debug(triplestoreSpecificPrequerySparql) prequeryResponseNotMerged: SparqlSelectResult <- (storeManager ? SparqlSelectRequest( - triplestoreSpecificPrequerySparql)).mapTo[SparqlSelectResult] + triplestoreSpecificPrequerySparql + )).mapTo[SparqlSelectResult] pageSizeBeforeFiltering: Int = prequeryResponseNotMerged.results.bindings.size // Merge rows with the same main resource IRI. This could happen if there are unbound variables in a UNION. - prequeryResponse = mergePrequeryResults(prequeryResponseNotMerged = prequeryResponseNotMerged, - mainResourceVar = mainResourceVar) + prequeryResponse = mergePrequeryResults( + prequeryResponseNotMerged = prequeryResponseNotMerged, + mainResourceVar = mainResourceVar + ) // a sequence of resource IRIs that match the search criteria // attention: no permission checking has been done so far @@ -548,129 +594,138 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand resultRow.rowMap(mainResourceVar.variableName) } - mainQueryResults: ConstructResponseUtilV2.MainResourcesAndValueRdfData <- if (mainResourceIris.nonEmpty) { - // at least one resource matched the prequery + mainQueryResults: ConstructResponseUtilV2.MainResourcesAndValueRdfData <- + if (mainResourceIris.nonEmpty) { + // at least one resource matched the prequery - // get all the IRIs for variables representing dependent resources per main resource - val dependentResourceIrisPerMainResource: GravsearchMainQueryGenerator.DependentResourcesPerMainResource = - GravsearchMainQueryGenerator.getDependentResourceIrisPerMainResource( - prequeryResponse = prequeryResponse, - transformer = nonTriplestoreSpecificConstructToSelectTransformer, - mainResourceVar = mainResourceVar - ) + // get all the IRIs for variables representing dependent resources per main resource + val dependentResourceIrisPerMainResource: GravsearchMainQueryGenerator.DependentResourcesPerMainResource = + GravsearchMainQueryGenerator.getDependentResourceIrisPerMainResource( + prequeryResponse = prequeryResponse, + transformer = nonTriplestoreSpecificConstructToSelectTransformer, + mainResourceVar = mainResourceVar + ) - // collect all variables representing resources - val allResourceVariablesFromTypeInspection: Set[QueryVariable] = typeInspectionResult.entities.collect { - case (queryVar: TypeableVariable, nonPropTypeInfo: NonPropertyTypeInfo) if nonPropTypeInfo.isResourceType => - QueryVariable(queryVar.variableName) - }.toSet - - // the user may have defined IRIs of dependent resources in the input query (type annotations) - // only add them if they are mentioned in a positive context (not negated like in a FILTER NOT EXISTS or MINUS) - val dependentResourceIrisFromTypeInspection: Set[IRI] = typeInspectionResult.entities.collect { - case (iri: TypeableIri, _: NonPropertyTypeInfo) - if whereClauseWithoutAnnotations.positiveEntities.contains(IriRef(iri.iri)) => - iri.iri.toString - }.toSet - - // the IRIs of all dependent resources for all main resources - val allDependentResourceIris - : Set[IRI] = dependentResourceIrisPerMainResource.dependentResourcesPerMainResource.values.flatten.toSet ++ dependentResourceIrisFromTypeInspection - - // for each main resource, create a Map of value object variables and their Iris - val valueObjectVarsAndIrisPerMainResource: GravsearchMainQueryGenerator.ValueObjectVariablesAndValueObjectIris = - GravsearchMainQueryGenerator.getValueObjectVarsAndIrisPerMainResource( - prequeryResponse = prequeryResponse, - transformer = nonTriplestoreSpecificConstructToSelectTransformer, - mainResourceVar = mainResourceVar - ) + // collect all variables representing resources + val allResourceVariablesFromTypeInspection: Set[QueryVariable] = typeInspectionResult.entities.collect { + case (queryVar: TypeableVariable, nonPropTypeInfo: NonPropertyTypeInfo) if nonPropTypeInfo.isResourceType => + QueryVariable(queryVar.variableName) + }.toSet + + // the user may have defined IRIs of dependent resources in the input query (type annotations) + // only add them if they are mentioned in a positive context (not negated like in a FILTER NOT EXISTS or MINUS) + val dependentResourceIrisFromTypeInspection: Set[IRI] = typeInspectionResult.entities.collect { + case (iri: TypeableIri, _: NonPropertyTypeInfo) + if whereClauseWithoutAnnotations.positiveEntities.contains(IriRef(iri.iri)) => + iri.iri.toString + }.toSet + + // the IRIs of all dependent resources for all main resources + val allDependentResourceIris: Set[IRI] = + dependentResourceIrisPerMainResource.dependentResourcesPerMainResource.values.flatten.toSet ++ dependentResourceIrisFromTypeInspection + + // for each main resource, create a Map of value object variables and their Iris + val valueObjectVarsAndIrisPerMainResource + : GravsearchMainQueryGenerator.ValueObjectVariablesAndValueObjectIris = + GravsearchMainQueryGenerator.getValueObjectVarsAndIrisPerMainResource( + prequeryResponse = prequeryResponse, + transformer = nonTriplestoreSpecificConstructToSelectTransformer, + mainResourceVar = mainResourceVar + ) - // collect all value objects IRIs (for all main resources and for all value object variables) - val allValueObjectIris: Set[IRI] = - valueObjectVarsAndIrisPerMainResource.valueObjectVariablesAndValueObjectIris.values.foldLeft(Set.empty[IRI]) { - case (acc: Set[IRI], valObjIrisForQueryVar: Map[QueryVariable, Set[IRI]]) => + // collect all value objects IRIs (for all main resources and for all value object variables) + val allValueObjectIris: Set[IRI] = + valueObjectVarsAndIrisPerMainResource.valueObjectVariablesAndValueObjectIris.values.foldLeft( + Set.empty[IRI] + ) { case (acc: Set[IRI], valObjIrisForQueryVar: Map[QueryVariable, Set[IRI]]) => acc ++ valObjIrisForQueryVar.values.flatten.toSet - } + } - // create the main query - // it is a Union of two sets: the main resources and the dependent resources - val mainQuery: ConstructQuery = GravsearchMainQueryGenerator.createMainQuery( - mainResourceIris = mainResourceIris.map(iri => IriRef(iri.toSmartIri)).toSet, - dependentResourceIris = allDependentResourceIris.map(iri => IriRef(iri.toSmartIri)), - valueObjectIris = allValueObjectIris, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - settings = settings - ) + // create the main query + // it is a Union of two sets: the main resources and the dependent resources + val mainQuery: ConstructQuery = GravsearchMainQueryGenerator.createMainQuery( + mainResourceIris = mainResourceIris.map(iri => IriRef(iri.toSmartIri)).toSet, + dependentResourceIris = allDependentResourceIris.map(iri => IriRef(iri.toSmartIri)), + valueObjectIris = allValueObjectIris, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + settings = settings + ) - val triplestoreSpecificQueryPatternTransformerConstruct: ConstructToConstructTransformer = { - if (settings.triplestoreType.startsWith("graphdb")) { - // GraphDB - new SparqlTransformer.GraphDBConstructToConstructTransformer - } else { - // Other - new SparqlTransformer.NoInferenceConstructToConstructTransformer + val triplestoreSpecificQueryPatternTransformerConstruct: ConstructToConstructTransformer = { + if (settings.triplestoreType.startsWith("graphdb")) { + // GraphDB + new SparqlTransformer.GraphDBConstructToConstructTransformer + } else { + // Other + new SparqlTransformer.NoInferenceConstructToConstructTransformer + } } - } - - val triplestoreSpecificMainQuery = QueryTraverser.transformConstructToConstruct( - inputQuery = mainQuery, - transformer = triplestoreSpecificQueryPatternTransformerConstruct - ) - // Convert the result to a SPARQL string and send it to the triplestore. - val triplestoreSpecificMainQuerySparql: String = triplestoreSpecificMainQuery.toSparql - log.debug(triplestoreSpecificMainQuerySparql) - - for { - mainQueryResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = triplestoreSpecificMainQuerySparql, - featureFactoryConfig = featureFactoryConfig, - )).mapTo[SparqlExtendedConstructResponse] - - // Filter out values that the user doesn't have permission to see. - queryResultsFilteredForPermissions: ConstructResponseUtilV2.MainResourcesAndValueRdfData = ConstructResponseUtilV2 - .splitMainResourcesAndValueRdfData( - constructQueryResults = mainQueryResponse, - requestingUser = requestingUser - ) + val triplestoreSpecificMainQuery = QueryTraverser.transformConstructToConstruct( + inputQuery = mainQuery, + transformer = triplestoreSpecificQueryPatternTransformerConstruct + ) - // filter out those value objects that the user does not want to be returned by the query (not present in the input query's CONSTRUCT clause) - queryResWithFullGraphPatternOnlyRequestedValues: Map[IRI, ConstructResponseUtilV2.ResourceWithValueRdfData] = MainQueryResultProcessor - .getRequestedValuesFromResultsWithFullGraphPattern( - queryResultsFilteredForPermissions.resources, - valueObjectVarsAndIrisPerMainResource, - allResourceVariablesFromTypeInspection, - dependentResourceIrisFromTypeInspection, - nonTriplestoreSpecificConstructToSelectTransformer, - typeInspectionResult, - inputQuery - ) - } yield - queryResultsFilteredForPermissions.copy( + // Convert the result to a SPARQL string and send it to the triplestore. + val triplestoreSpecificMainQuerySparql: String = triplestoreSpecificMainQuery.toSparql + log.debug(triplestoreSpecificMainQuerySparql) + + for { + mainQueryResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( + sparql = triplestoreSpecificMainQuerySparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] + + // Filter out values that the user doesn't have permission to see. + queryResultsFilteredForPermissions: ConstructResponseUtilV2.MainResourcesAndValueRdfData = + ConstructResponseUtilV2 + .splitMainResourcesAndValueRdfData( + constructQueryResults = mainQueryResponse, + requestingUser = requestingUser + ) + + // filter out those value objects that the user does not want to be returned by the query (not present in the input query's CONSTRUCT clause) + queryResWithFullGraphPatternOnlyRequestedValues: Map[ + IRI, + ConstructResponseUtilV2.ResourceWithValueRdfData + ] = MainQueryResultProcessor + .getRequestedValuesFromResultsWithFullGraphPattern( + queryResultsFilteredForPermissions.resources, + valueObjectVarsAndIrisPerMainResource, + allResourceVariablesFromTypeInspection, + dependentResourceIrisFromTypeInspection, + nonTriplestoreSpecificConstructToSelectTransformer, + typeInspectionResult, + inputQuery + ) + } yield queryResultsFilteredForPermissions.copy( resources = queryResWithFullGraphPatternOnlyRequestedValues ) - } else { - // the prequery returned no results, no further query is necessary - Future(ConstructResponseUtilV2.MainResourcesAndValueRdfData(resources = Map.empty)) - } + } else { + // the prequery returned no results, no further query is necessary + Future(ConstructResponseUtilV2.MainResourcesAndValueRdfData(resources = Map.empty)) + } // Find out whether to query standoff along with text values. This boolean value will be passed to // ConstructResponseUtilV2.makeTextValueContentV2. - queryStandoff: Boolean = SchemaOptions.queryStandoffWithTextValues(targetSchema = targetSchema, - schemaOptions = schemaOptions) + queryStandoff: Boolean = SchemaOptions.queryStandoffWithTextValues( + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) // If we're querying standoff, get XML-to standoff mappings. - mappingsAsMap: Map[IRI, MappingAndXSLTransformation] <- if (queryStandoff) { - getMappingsFromQueryResultsSeparated( - queryResultsSeparated = mainQueryResults.resources, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - } else { - FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) - } + mappingsAsMap: Map[IRI, MappingAndXSLTransformation] <- + if (queryStandoff) { + getMappingsFromQueryResultsSeparated( + queryResultsSeparated = mainQueryResults.resources, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + } else { + FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) + } apiResponse: ReadResourcesSequenceV2 <- ConstructResponseUtilV2.createApiResponse( mainResourcesAndValueRdfData = mainQueryResults, @@ -691,13 +746,14 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } /** - * Gets resources from a project. - * - * @param resourcesInProjectGetRequestV2 the request message. - * @return a [[ReadResourcesSequenceV2]]. - */ + * Gets resources from a project. + * + * @param resourcesInProjectGetRequestV2 the request message. + * @return a [[ReadResourcesSequenceV2]]. + */ private def searchResourcesByProjectAndClassV2( - resourcesInProjectGetRequestV2: SearchResourcesByProjectAndClassRequestV2): Future[ReadResourcesSequenceV2] = { + resourcesInProjectGetRequestV2: SearchResourcesByProjectAndClassRequestV2 + ): Future[ReadResourcesSequenceV2] = { val internalClassIri = resourcesInProjectGetRequestV2.resourceClass.toOntologySchema(InternalSchema) val maybeInternalOrderByPropertyIri: Option[SmartIri] = resourcesInProjectGetRequestV2.orderByProperty.map(_.toOntologySchema(InternalSchema)) @@ -722,21 +778,25 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand entityInfoResponse.propertyInfoMap(internalOrderByPropertyIri) // Ensure that the ORDER BY property is one that we can sort by. - if (!internalOrderByPropertyDef.isResourceProp || internalOrderByPropertyDef.isLinkProp || internalOrderByPropertyDef.isLinkValueProp || internalOrderByPropertyDef.isFileValueProp) { + if ( + !internalOrderByPropertyDef.isResourceProp || internalOrderByPropertyDef.isLinkProp || internalOrderByPropertyDef.isLinkValueProp || internalOrderByPropertyDef.isFileValueProp + ) { throw BadRequestException(s"Cannot sort by property <${resourcesInProjectGetRequestV2.orderByProperty}>") } // Ensure that the resource class has a cardinality on the ORDER BY property. if (!classDef.knoraResourceProperties.contains(internalOrderByPropertyIri)) { throw BadRequestException( - s"Class <${resourcesInProjectGetRequestV2.resourceClass}> has no cardinality on property <${resourcesInProjectGetRequestV2.orderByProperty}>") + s"Class <${resourcesInProjectGetRequestV2.resourceClass}> has no cardinality on property <${resourcesInProjectGetRequestV2.orderByProperty}>" + ) } // Get the value class that's the object of the knora-base:objectClassConstraint of the ORDER BY property. val orderByValueType: SmartIri = internalOrderByPropertyDef.entityInfoContent.requireIriObject( OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, throw InconsistentRepositoryDataException( - s"Property <$internalOrderByPropertyIri> has no knora-base:objectClassConstraint") + s"Property <$internalOrderByPropertyIri> has no knora-base:objectClassConstraint" + ) ) // Determine which subproperty of knora-base:valueHas corresponds to that value class. @@ -775,9 +835,10 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // Find out whether to query standoff along with text values. This boolean value will be passed to // ConstructResponseUtilV2.makeTextValueContentV2. - queryStandoff: Boolean = SchemaOptions.queryStandoffWithTextValues(targetSchema = ApiV2Complex, - schemaOptions = - resourcesInProjectGetRequestV2.schemaOptions) + queryStandoff: Boolean = SchemaOptions.queryStandoffWithTextValues( + targetSchema = ApiV2Complex, + schemaOptions = resourcesInProjectGetRequestV2.schemaOptions + ) // If we're supposed to query standoff, get the indexes delimiting the first page of standoff. (Subsequent // pages, if any, will be queried separately.) @@ -788,86 +849,93 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand ) // Are there any matching resources? - apiResponse: ReadResourcesSequenceV2 <- if (mainResourceIris.nonEmpty) { - for { - // Yes. Do a CONSTRUCT query to get the contents of those resources. If we're querying standoff, get - // at most one page of standoff per text value. - resourceRequestSparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getResourcePropertiesAndValues( - triplestore = settings.triplestoreType, - resourceIris = mainResourceIris, - preview = false, - withDeleted = false, - queryAllNonStandoff = true, - maybePropertyIri = None, - maybeVersionDate = None, - maybeStandoffMinStartIndex = maybeStandoffMinStartIndex, - maybeStandoffMaxStartIndex = maybeStandoffMaxStartIndex, - stringFormatter = stringFormatter - ) - .toString()) + apiResponse: ReadResourcesSequenceV2 <- + if (mainResourceIris.nonEmpty) { + for { + // Yes. Do a CONSTRUCT query to get the contents of those resources. If we're querying standoff, get + // at most one page of standoff per text value. + resourceRequestSparql <- Future( + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getResourcePropertiesAndValues( + triplestore = settings.triplestoreType, + resourceIris = mainResourceIris, + preview = false, + withDeleted = false, + queryAllNonStandoff = true, + maybePropertyIri = None, + maybeVersionDate = None, + maybeStandoffMinStartIndex = maybeStandoffMinStartIndex, + maybeStandoffMaxStartIndex = maybeStandoffMaxStartIndex, + stringFormatter = stringFormatter + ) + .toString() + ) + + // _ = println(resourceRequestSparql) - // _ = println(resourceRequestSparql) + resourceRequestResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( + sparql = resourceRequestSparql, + featureFactoryConfig = resourcesInProjectGetRequestV2.featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] - resourceRequestResponse: SparqlExtendedConstructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = resourceRequestSparql, - featureFactoryConfig = resourcesInProjectGetRequestV2.featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] + // separate resources and values + mainResourcesAndValueRdfData: ConstructResponseUtilV2.MainResourcesAndValueRdfData = ConstructResponseUtilV2 + .splitMainResourcesAndValueRdfData( + constructQueryResults = resourceRequestResponse, + requestingUser = resourcesInProjectGetRequestV2.requestingUser + ) - // separate resources and values - mainResourcesAndValueRdfData: ConstructResponseUtilV2.MainResourcesAndValueRdfData = ConstructResponseUtilV2 - .splitMainResourcesAndValueRdfData(constructQueryResults = resourceRequestResponse, - requestingUser = resourcesInProjectGetRequestV2.requestingUser) + // If we're querying standoff, get XML-to standoff mappings. + mappings: Map[IRI, MappingAndXSLTransformation] <- + if (queryStandoff) { + getMappingsFromQueryResultsSeparated( + mainResourcesAndValueRdfData.resources, + featureFactoryConfig = resourcesInProjectGetRequestV2.featureFactoryConfig, + resourcesInProjectGetRequestV2.requestingUser + ) + } else { + FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) + } - // If we're querying standoff, get XML-to standoff mappings. - mappings: Map[IRI, MappingAndXSLTransformation] <- if (queryStandoff) { - getMappingsFromQueryResultsSeparated( - mainResourcesAndValueRdfData.resources, + // Construct a ReadResourceV2 for each resource that the user has permission to see. + readResourcesSequence: ReadResourcesSequenceV2 <- ConstructResponseUtilV2.createApiResponse( + mainResourcesAndValueRdfData = mainResourcesAndValueRdfData, + orderByResourceIri = mainResourceIris, + pageSizeBeforeFiltering = mainResourceIris.size, + mappings = mappings, + queryStandoff = maybeStandoffMinStartIndex.nonEmpty, + versionDate = None, + calculateMayHaveMoreResults = true, + responderManager = responderManager, + targetSchema = resourcesInProjectGetRequestV2.targetSchema, + settings = settings, featureFactoryConfig = resourcesInProjectGetRequestV2.featureFactoryConfig, - resourcesInProjectGetRequestV2.requestingUser + requestingUser = resourcesInProjectGetRequestV2.requestingUser ) - } else { - FastFuture.successful(Map.empty[IRI, MappingAndXSLTransformation]) - } - - // Construct a ReadResourceV2 for each resource that the user has permission to see. - readResourcesSequence: ReadResourcesSequenceV2 <- ConstructResponseUtilV2.createApiResponse( - mainResourcesAndValueRdfData = mainResourcesAndValueRdfData, - orderByResourceIri = mainResourceIris, - pageSizeBeforeFiltering = mainResourceIris.size, - mappings = mappings, - queryStandoff = maybeStandoffMinStartIndex.nonEmpty, - versionDate = None, - calculateMayHaveMoreResults = true, - responderManager = responderManager, - targetSchema = resourcesInProjectGetRequestV2.targetSchema, - settings = settings, - featureFactoryConfig = resourcesInProjectGetRequestV2.featureFactoryConfig, - requestingUser = resourcesInProjectGetRequestV2.requestingUser - ) - } yield readResourcesSequence - } else { - FastFuture.successful(ReadResourcesSequenceV2(Vector.empty[ReadResourceV2])) - } + } yield readResourcesSequence + } else { + FastFuture.successful(ReadResourcesSequenceV2(Vector.empty[ReadResourceV2])) + } } yield apiResponse } /** - * Performs a count query for a search for resources by their rdfs:label. - * - * @param searchValue the values to search for. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the client making the request. - * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. - */ - private def searchResourcesByLabelCountV2(searchValue: String, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ResourceCountV2] = { + * Performs a count query for a search for resources by their rdfs:label. + * + * @param searchValue the values to search for. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the client making the request. + * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. + */ + private def searchResourcesByLabelCountV2( + searchValue: String, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ResourceCountV2] = { val searchPhrase: MatchStringWhileTyping = MatchStringWhileTyping(searchValue) for { @@ -882,7 +950,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand offset = 0, countQuery = true ) - .toString()) + .toString() + ) // _ = println(countSparql) @@ -891,37 +960,39 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // query response should contain one result with one row with the name "count" _ = if (countResponse.results.bindings.length != 1) { throw GravsearchException( - s"Fulltext count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given") + s"Fulltext count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given" + ) } count = countResponse.results.bindings.head.rowMap("count") - } yield - ResourceCountV2( - numberOfResources = count.toInt - ) + } yield ResourceCountV2( + numberOfResources = count.toInt + ) } /** - * Performs a search for resources by their rdfs:label. - * - * @param searchValue the values to search for. - * @param offset the offset to be used for paging. - * @param limitToProject limit search to given project. - * @param limitToResourceClass limit search to given resource class. - * @param targetSchema the schema of the response. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the the client making the request. - * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. - */ - private def searchResourcesByLabelV2(searchValue: String, - offset: Int, - limitToProject: Option[IRI], - limitToResourceClass: Option[SmartIri], - targetSchema: ApiV2Schema, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourcesSequenceV2] = { + * Performs a search for resources by their rdfs:label. + * + * @param searchValue the values to search for. + * @param offset the offset to be used for paging. + * @param limitToProject limit search to given project. + * @param limitToResourceClass limit search to given resource class. + * @param targetSchema the schema of the response. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the the client making the request. + * @return a [[ReadResourcesSequenceV2]] representing the resources that have been found. + */ + private def searchResourcesByLabelV2( + searchValue: String, + offset: Int, + limitToProject: Option[IRI], + limitToResourceClass: Option[SmartIri], + targetSchema: ApiV2Schema, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourcesSequenceV2] = { val searchPhrase: MatchStringWhileTyping = MatchStringWhileTyping(searchValue) @@ -937,7 +1008,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand offset = offset * settings.v2ResultsPerPage, countQuery = false ) - .toString()) + .toString() + ) // _ = println(searchResourceByLabelSparql) @@ -973,7 +1045,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand // separate resources and value objects mainResourcesAndValueRdfData = ConstructResponseUtilV2.splitMainResourcesAndValueRdfData( constructQueryResults = searchResourceByLabelResponse, - requestingUser = requestingUser) + requestingUser = requestingUser + ) //_ = println(queryResultsSeparated) @@ -995,48 +1068,53 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand } /** - * Given a prequery result, merges rows with the same main resource IRI. This could happen if there are unbound - * variables in `GROUP_CONCAT` expressions. - * - * @param prequeryResponseNotMerged the prequery response before merging. - * @param mainResourceVar the name of the column representing the main resource. - * @return the merged results. - */ - private def mergePrequeryResults(prequeryResponseNotMerged: SparqlSelectResult, - mainResourceVar: QueryVariable): SparqlSelectResult = { + * Given a prequery result, merges rows with the same main resource IRI. This could happen if there are unbound + * variables in `GROUP_CONCAT` expressions. + * + * @param prequeryResponseNotMerged the prequery response before merging. + * @param mainResourceVar the name of the column representing the main resource. + * @return the merged results. + */ + private def mergePrequeryResults( + prequeryResponseNotMerged: SparqlSelectResult, + mainResourceVar: QueryVariable + ): SparqlSelectResult = { // Make a Map of merged results per main resource IRI. - val prequeryRowsMergedMap: Map[IRI, VariableResultsRow] = prequeryResponseNotMerged.results.bindings - .groupBy { row => + val prequeryRowsMergedMap: Map[IRI, VariableResultsRow] = prequeryResponseNotMerged.results.bindings.groupBy { + row => // Get the rows for each main resource IRI. row.rowMap(mainResourceVar.variableName) - } - .map { - case (resourceIri: IRI, rows: Seq[VariableResultsRow]) => - // Make a Set of all the column names in the rows to be merged. - val columnNamesToMerge: Set[String] = rows.flatMap(_.rowMap.keySet).toSet - - // Make a Map of column names to merged values. - val mergedRowMap: Map[String, String] = columnNamesToMerge.map { columnName => - // For each column name, get the values to be merged. - val columnValues: Seq[String] = rows.flatMap(_.rowMap.get(columnName)) - - // Is this is the column containing the main resource IRI? - val mergedColumnValue: String = if (columnName == mainResourceVar.variableName) { - // Yes. Use that IRI as the merged value. - resourceIri - } else { - // No. This must be a column resulting from GROUP_CONCAT, so use the GROUP_CONCAT - // separator to concatenate the column values. - columnValues.mkString(AbstractPrequeryGenerator.groupConcatSeparator.toString) - } + }.map { case (resourceIri: IRI, rows: Seq[VariableResultsRow]) => + // Make a Set of all the column names in the rows to be merged. + val columnNamesToMerge: Set[String] = rows.flatMap(_.rowMap.keySet).toSet + + // Make a Map of column names to merged values. + val mergedRowMap: Map[String, String] = columnNamesToMerge.map { columnName => + // For each column name, get the values to be merged. + val columnValues: Seq[String] = rows.flatMap(_.rowMap.get(columnName)) + + // Is this is the column containing the main resource IRI? + val mergedColumnValue: String = if (columnName == mainResourceVar.variableName) { + // Yes. Use that IRI as the merged value. + resourceIri + } else { + // No. This must be a column resulting from GROUP_CONCAT, so use the GROUP_CONCAT + // separator to concatenate the column values. + columnValues.mkString(AbstractPrequeryGenerator.groupConcatSeparator.toString) + } - columnName -> mergedColumnValue - }.toMap + columnName -> mergedColumnValue + }.toMap - resourceIri -> VariableResultsRow(new ErrorHandlingMap(mergedRowMap, { key: String => + resourceIri -> VariableResultsRow( + new ErrorHandlingMap( + mergedRowMap, + { key: String => s"No value found for SPARQL query variable '$key' in query result row" - })) - } + } + ) + ) + } // Construct a sequence of the distinct main resource IRIs in the query results, preserving the // order of the result rows. diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/StandoffResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/StandoffResponderV2.scala index 353c791257..ea3900d0be 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/StandoffResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/StandoffResponderV2.scala @@ -61,8 +61,8 @@ import scala.concurrent.{ExecutionContext, Future} import scala.xml.{Elem, Node, NodeSeq, XML} /** - * Responds to requests relating to the creation of mappings from XML elements and attributes to standoff classes and properties. - */ + * Responds to requests relating to the creation of mappings from XML elements and attributes to standoff classes and properties. + */ class StandoffResponderV2(responderData: ResponderData) extends Responder(responderData) { /* actor materializer needed for http requests */ @@ -74,20 +74,22 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon ) /** - * Receives a message of type [[StandoffResponderRequestV2]], and returns an appropriate response message. - */ + * Receives a message of type [[StandoffResponderRequestV2]], and returns an appropriate response message. + */ def receive(msg: StandoffResponderRequestV2) = msg match { case getStandoffPageRequestV2: GetStandoffPageRequestV2 => getStandoffV2(getStandoffPageRequestV2) case getRemainingStandoffFromTextValueRequestV2: GetRemainingStandoffFromTextValueRequestV2 => getRemainingStandoffFromTextValueV2(getRemainingStandoffFromTextValueRequestV2) case CreateMappingRequestV2(metadata, xml, featureFactoryConfig, requestingUser, uuid) => - createMappingV2(xml.xml, - metadata.label, - metadata.projectIri, - metadata.mappingName, - featureFactoryConfig, - requestingUser, - uuid) + createMappingV2( + xml.xml, + metadata.label, + metadata.projectIri, + metadata.mappingName, + featureFactoryConfig, + requestingUser, + uuid + ) case GetMappingRequestV2(mappingIri, featureFactoryConfig, requestingUser) => getMappingV2(mappingIri, featureFactoryConfig, requestingUser) case GetXSLTransformationRequestV2(xsltTextReprIri, featureFactoryConfig, requestingUser) => @@ -116,7 +118,8 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon maybeStandoffMaxStartIndex = Some(requestMaxStartIndex), stringFormatter = stringFormatter ) - .toString()) + .toString() + ) // _ = println("=================================") // _ = println(resourceRequestSparql) @@ -134,8 +137,10 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon // separate resources and values mainResourcesAndValueRdfData: ConstructResponseUtilV2.MainResourcesAndValueRdfData = ConstructResponseUtilV2 - .splitMainResourcesAndValueRdfData(constructQueryResults = resourceRequestResponse, - requestingUser = getStandoffRequestV2.requestingUser) + .splitMainResourcesAndValueRdfData( + constructQueryResults = resourceRequestResponse, + requestingUser = getStandoffRequestV2.requestingUser + ) readResourcesSequenceV2: ReadResourcesSequenceV2 <- ConstructResponseUtilV2.createApiResponse( mainResourcesAndValueRdfData = mainResourcesAndValueRdfData, @@ -156,14 +161,18 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon valueObj: ReadValueV2 = readResourceV2.values.values.flatten .find(_.valueIri == getStandoffRequestV2.valueIri) - .getOrElse(throw NotFoundException( - s"Value <${getStandoffRequestV2.valueIri}> not found in resource <${getStandoffRequestV2.resourceIri}> (maybe you do not have permission to see it, or it is marked as deleted)")) + .getOrElse( + throw NotFoundException( + s"Value <${getStandoffRequestV2.valueIri}> not found in resource <${getStandoffRequestV2.resourceIri}> (maybe you do not have permission to see it, or it is marked as deleted)" + ) + ) textValueObj: ReadTextValueV2 = valueObj match { case textVal: ReadTextValueV2 => textVal case _ => throw BadRequestException( - s"Value <${getStandoffRequestV2.valueIri}> not found in resource <${getStandoffRequestV2.resourceIri}> is not a text value") + s"Value <${getStandoffRequestV2.valueIri}> not found in resource <${getStandoffRequestV2.resourceIri}> is not a text value" + ) } nextOffset: Option[Int] = textValueObj.valueHasMaxStandoffStartIndex match { @@ -176,25 +185,26 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon case None => None } - } yield - GetStandoffResponseV2( - valueIri = textValueObj.valueIri, - standoff = textValueObj.valueContent.standoff, - nextOffset = nextOffset - ) + } yield GetStandoffResponseV2( + valueIri = textValueObj.valueIri, + standoff = textValueObj.valueContent.standoff, + nextOffset = nextOffset + ) } /** - * If not already in the cache, retrieves a `knora-base:XSLTransformation` in the triplestore and requests the corresponding XSL transformation file from Sipi. - * - * @param xslTransformationIri the IRI of the resource representing the XSL Transformation (a [[OntologyConstants.KnoraBase.XSLTransformation]]). - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[GetXSLTransformationResponseV2]]. - */ - private def getXSLTransformation(xslTransformationIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[GetXSLTransformationResponseV2] = { + * If not already in the cache, retrieves a `knora-base:XSLTransformation` in the triplestore and requests the corresponding XSL transformation file from Sipi. + * + * @param xslTransformationIri the IRI of the resource representing the XSL Transformation (a [[OntologyConstants.KnoraBase.XSLTransformation]]). + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[GetXSLTransformationResponseV2]]. + */ + private def getXSLTransformation( + xslTransformationIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[GetXSLTransformationResponseV2] = { val xsltUrlFuture = for { @@ -209,11 +219,13 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon _ = if (resource.resourceClassIri.toString != OntologyConstants.KnoraBase.XSLTransformation) { throw BadRequestException( - s"Resource $xslTransformationIri is not a ${OntologyConstants.KnoraBase.XSLTransformation}") + s"Resource $xslTransformationIri is not a ${OntologyConstants.KnoraBase.XSLTransformation}" + ) } (fileValueIri: IRI, xsltFileValueContent: TextFileValueContentV2) = resource.values.get( - OntologyConstants.KnoraBase.HasTextFileValue.toSmartIri) match { + OntologyConstants.KnoraBase.HasTextFileValue.toSmartIri + ) match { case Some(values: Seq[ReadValueV2]) if values.size == 1 => values.head match { case value: ReadValueV2 => @@ -221,28 +233,31 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon case textRepr: TextFileValueContentV2 => (value.valueIri, textRepr) case _ => throw InconsistentRepositoryDataException( - s"${OntologyConstants.KnoraBase.XSLTransformation} $xslTransformationIri is supposed to have exactly one value of type ${OntologyConstants.KnoraBase.TextFileValue}") + s"${OntologyConstants.KnoraBase.XSLTransformation} $xslTransformationIri is supposed to have exactly one value of type ${OntologyConstants.KnoraBase.TextFileValue}" + ) } } case None => throw InconsistentRepositoryDataException( - s"${OntologyConstants.KnoraBase.XSLTransformation} has no property ${OntologyConstants.KnoraBase.HasTextFileValue}") + s"${OntologyConstants.KnoraBase.XSLTransformation} has no property ${OntologyConstants.KnoraBase.HasTextFileValue}" + ) } // check if xsltFileValueContent represents an XSL transformation _ = if (!xmlMimeTypes.contains(xsltFileValueContent.fileValue.internalMimeType)) { throw BadRequestException( - s"Expected $fileValueIri to be an XML file referring to an XSL transformation, but it has MIME type ${xsltFileValueContent.fileValue.internalMimeType}") + s"Expected $fileValueIri to be an XML file referring to an XSL transformation, but it has MIME type ${xsltFileValueContent.fileValue.internalMimeType}" + ) } - xsltUrl: String = s"${settings.internalSipiBaseUrl}/${resource.projectADM.shortcode}/${xsltFileValueContent.fileValue.internalFilename}/file" + xsltUrl: String = + s"${settings.internalSipiBaseUrl}/${resource.projectADM.shortcode}/${xsltFileValueContent.fileValue.internalFilename}/file" } yield xsltUrl - val recoveredXsltUrlFuture = xsltUrlFuture.recover { - case notFound: NotFoundException => - throw BadRequestException(s"XSL transformation $xslTransformationIri not found: ${notFound.message}") + val recoveredXsltUrlFuture = xsltUrlFuture.recover { case notFound: NotFoundException => + throw BadRequestException(s"XSL transformation $xslTransformationIri not found: ${notFound.message}") } for { @@ -252,45 +267,50 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon xsltMaybe: Option[String] = CacheUtil.get[String](cacheName = xsltCacheName, key = xsltFileUrl) - xslt: String <- if (xsltMaybe.nonEmpty) { - // XSL transformation is cached - Future(xsltMaybe.get) - } else { - for { - response: SipiGetTextFileResponse <- (storeManager ? SipiGetTextFileRequest( - fileUrl = xsltFileUrl, - requestingUser = KnoraSystemInstances.Users.SystemUser, - senderName = this.getClass.getName - )).mapTo[SipiGetTextFileResponse] - _ = CacheUtil.put(cacheName = xsltCacheName, key = xsltFileUrl, value = response.content) - } yield response.content - } + xslt: String <- + if (xsltMaybe.nonEmpty) { + // XSL transformation is cached + Future(xsltMaybe.get) + } else { + for { + response: SipiGetTextFileResponse <- (storeManager ? SipiGetTextFileRequest( + fileUrl = xsltFileUrl, + requestingUser = KnoraSystemInstances.Users.SystemUser, + senderName = this.getClass.getName + )).mapTo[SipiGetTextFileResponse] + _ = CacheUtil.put(cacheName = xsltCacheName, key = xsltFileUrl, value = response.content) + } yield response.content + } } yield GetXSLTransformationResponseV2(xslt = xslt) } /** - * Creates a mapping between XML elements and attributes to standoff classes and properties. - * The mapping is used to convert XML documents to texts with standoff and back. - * - * @param xml the provided mapping. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the client that made the request. - */ - private def createMappingV2(xml: String, - label: String, - projectIri: SmartIri, - mappingName: String, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM, - apiRequestID: UUID): Future[CreateMappingResponseV2] = { - - def createMappingAndCheck(xml: String, - label: String, - mappingIri: IRI, - namedGraph: String, - requestingUser: UserADM): Future[CreateMappingResponseV2] = { + * Creates a mapping between XML elements and attributes to standoff classes and properties. + * The mapping is used to convert XML documents to texts with standoff and back. + * + * @param xml the provided mapping. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the client that made the request. + */ + private def createMappingV2( + xml: String, + label: String, + projectIri: SmartIri, + mappingName: String, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM, + apiRequestID: UUID + ): Future[CreateMappingResponseV2] = { + + def createMappingAndCheck( + xml: String, + label: String, + mappingIri: IRI, + namedGraph: String, + requestingUser: UserADM + ): Future[CreateMappingResponseV2] = { val createMappingFuture = for { @@ -359,7 +379,8 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon val separatorRequired: Boolean = stringFormatter.validateBoolean( separatorBooleanAsString, - throw BadRequestException(s" could not be converted to Boolean: $separatorBooleanAsString")) + throw BadRequestException(s" could not be converted to Boolean: $separatorBooleanAsString") + ) // get the standoff class IRI val standoffClassIri = (curMappingEle \ "standoffClass" \ "classIri").headOption @@ -387,13 +408,16 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon MappingXMLAttribute( attributeName = stringFormatter.toSparqlEncodedString( attrName, - throw BadRequestException(s"tagname $attrName contains invalid characters")), + throw BadRequestException(s"tagname $attrName contains invalid characters") + ), namespace = stringFormatter.toSparqlEncodedString( attributeNamespace, - throw BadRequestException(s"tagname $attributeNamespace contains invalid characters")), + throw BadRequestException(s"tagname $attributeNamespace contains invalid characters") + ), standoffProperty = stringFormatter.validateAndEscapeIri( propIri, - throw BadRequestException(s"standoff class IRI $standoffClassIri is not a valid IRI")), + throw BadRequestException(s"standoff class IRI $standoffClassIri is not a valid IRI") + ), mappingXMLAttributeElementIri = stringFormatter.makeRandomMappingElementIri(mappingIri) ) @@ -409,8 +433,10 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon .text val dataType: StandoffDataTypeClasses.Value = - StandoffDataTypeClasses.lookup(dataTypeXML, - throw BadRequestException(s"Invalid data type provided for $tagName")) + StandoffDataTypeClasses.lookup( + dataTypeXML, + throw BadRequestException(s"Invalid data type provided for $tagName") + ) val dataTypeAttribute: String = (datatypeMaybe \ "attributeName").headOption .getOrElse(throw BadRequestException(s"no '' given for datatype")) .text @@ -420,9 +446,11 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon datatype = dataType.toString, // safe because it is an enumeration attributeName = stringFormatter.toSparqlEncodedString( dataTypeAttribute, - throw BadRequestException(s"tagname $dataTypeAttribute contains invalid characters")), + throw BadRequestException(s"tagname $dataTypeAttribute contains invalid characters") + ), mappingStandoffDataTypeClassElementIri = stringFormatter.makeRandomMappingElementIri(mappingIri) - )) + ) + ) } else { None } @@ -430,16 +458,20 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon MappingElement( tagName = stringFormatter.toSparqlEncodedString( tagName, - throw BadRequestException(s"tagname $tagName contains invalid characters")), + throw BadRequestException(s"tagname $tagName contains invalid characters") + ), namespace = stringFormatter.toSparqlEncodedString( tagNamespace, - throw BadRequestException(s"namespace $tagNamespace contains invalid characters")), + throw BadRequestException(s"namespace $tagNamespace contains invalid characters") + ), className = stringFormatter.toSparqlEncodedString( className, - throw BadRequestException(s"classname $className contains invalid characters")), + throw BadRequestException(s"classname $className contains invalid characters") + ), standoffClass = stringFormatter.validateAndEscapeIri( standoffClassIri, - throw BadRequestException(s"standoff class IRI $standoffClassIri is not a valid IRI")), + throw BadRequestException(s"standoff class IRI $standoffClassIri is not a valid IRI") + ), attributes = attributes, standoffDataTypeClass = standoffDataTypeOption, mappingElementIri = stringFormatter.makeRandomMappingElementIri(mappingIri), @@ -450,8 +482,10 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon // transform mappingElements to the structure that is used internally to convert to or from standoff // in order to check for duplicates (checks are done during transformation) - mappingXMLToStandoff: MappingXMLtoStandoff = transformMappingElementsToMappingXMLtoStandoff(mappingElements, - None) + mappingXMLToStandoff: MappingXMLtoStandoff = transformMappingElementsToMappingXMLtoStandoff( + mappingElements, + None + ) // get the standoff entities used in the mapping // checks if the standoff classes exist in the ontology @@ -499,9 +533,11 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon _ = if (newMappingResponse.statements.isEmpty) { log.error( - s"Attempted a SPARQL update to create a new resource, but it inserted no rows:\n\n$newMappingResponse") + s"Attempted a SPARQL update to create a new resource, but it inserted no rows:\n\n$newMappingResponse" + ) throw UpdateNotPerformedException( - s"Resource $mappingIri was not created. Please report this as a possible bug.") + s"Resource $mappingIri was not created. Please report this as a possible bug." + ) } // get the mapping from the triplestore and cache it thereby @@ -562,7 +598,9 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon result: CreateMappingResponseV2 <- IriLocker.runWithIriLock( apiRequestID, stringFormatter - .createMappingLockIriForProject(projectIri.toString), // use a special project specific IRI to lock the creation of mappings for the given project + .createMappingLockIriForProject( + projectIri.toString + ), // use a special project specific IRI to lock the creation of mappings for the given project () => createMappingAndCheck( xml = xml, @@ -570,7 +608,7 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon mappingIri = mappingIri, namedGraph = namedGraph, requestingUser = requestingUser - ) + ) ) } yield result @@ -578,127 +616,138 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon } /** - * Transforms a mapping represented as a Seq of [[MappingElement]] to a [[MappingXMLtoStandoff]]. - * This method is called when reading a mapping back from the triplestore. - * - * @param mappingElements the Seq of MappingElement to be transformed. - * @return a [[MappingXMLtoStandoff]]. - */ + * Transforms a mapping represented as a Seq of [[MappingElement]] to a [[MappingXMLtoStandoff]]. + * This method is called when reading a mapping back from the triplestore. + * + * @param mappingElements the Seq of MappingElement to be transformed. + * @return a [[MappingXMLtoStandoff]]. + */ private def transformMappingElementsToMappingXMLtoStandoff( - mappingElements: Seq[MappingElement], - defaultXSLTransformation: Option[IRI]): MappingXMLtoStandoff = { + mappingElements: Seq[MappingElement], + defaultXSLTransformation: Option[IRI] + ): MappingXMLtoStandoff = { val mappingXMLToStandoff = mappingElements.foldLeft( - MappingXMLtoStandoff(namespace = Map.empty[String, Map[String, Map[String, XMLTag]]], - defaultXSLTransformation = None)) { - case (acc: MappingXMLtoStandoff, curEle: MappingElement) => - // get the name of the XML tag - val tagname = curEle.tagName - - // get the namespace the tag is defined in - val namespace = curEle.namespace + MappingXMLtoStandoff( + namespace = Map.empty[String, Map[String, Map[String, XMLTag]]], + defaultXSLTransformation = None + ) + ) { case (acc: MappingXMLtoStandoff, curEle: MappingElement) => + // get the name of the XML tag + val tagname = curEle.tagName - // get the class the tag is combined with - val classname = curEle.className + // get the namespace the tag is defined in + val namespace = curEle.namespace - // get tags from this namespace if already existent, otherwise create an empty map - val namespaceMap: Map[String, Map[String, XMLTag]] = - acc.namespace.getOrElse(namespace, Map.empty[String, Map[String, XMLTag]]) + // get the class the tag is combined with + val classname = curEle.className - // get the standoff class IRI - val standoffClassIri = curEle.standoffClass + // get tags from this namespace if already existent, otherwise create an empty map + val namespaceMap: Map[String, Map[String, XMLTag]] = + acc.namespace.getOrElse(namespace, Map.empty[String, Map[String, XMLTag]]) - // get a collection containing all the attributes - val attributeNodes: Seq[MappingXMLAttribute] = curEle.attributes + // get the standoff class IRI + val standoffClassIri = curEle.standoffClass - // group attributes by their namespace - val attributeNodesByNamespace: Map[String, Seq[MappingXMLAttribute]] = attributeNodes.groupBy { - attr: MappingXMLAttribute => - attr.namespace - } + // get a collection containing all the attributes + val attributeNodes: Seq[MappingXMLAttribute] = curEle.attributes - // create attribute entries for each given namespace - val attributes: Map[String, Map[String, IRI]] = attributeNodesByNamespace.map { - case (namespace: String, attrNodes: Seq[MappingXMLAttribute]) => - // collect all the attributes for the current namespace - val attributesInNamespace: Map[String, IRI] = attrNodes.foldLeft(Map.empty[String, IRI]) { - case (acc: Map[String, IRI], attrEle: MappingXMLAttribute) => - // get the current attribute's name - val attrName = attrEle.attributeName - - // check if the current attribute already exists in this namespace - if (acc.get(attrName).nonEmpty) { - throw BadRequestException("Duplicate attribute name in namespace") - } - - // get the standoff property IRI for the current attribute - val propIri = attrEle.standoffProperty - - // add the current attribute to the collection - acc + (attrName -> propIri) - } + // group attributes by their namespace + val attributeNodesByNamespace: Map[String, Seq[MappingXMLAttribute]] = attributeNodes.groupBy { + attr: MappingXMLAttribute => + attr.namespace + } - namespace -> attributesInNamespace - } + // create attribute entries for each given namespace + val attributes: Map[String, Map[String, IRI]] = attributeNodesByNamespace.map { + case (namespace: String, attrNodes: Seq[MappingXMLAttribute]) => + // collect all the attributes for the current namespace + val attributesInNamespace: Map[String, IRI] = attrNodes.foldLeft(Map.empty[String, IRI]) { + case (acc: Map[String, IRI], attrEle: MappingXMLAttribute) => + // get the current attribute's name + val attrName = attrEle.attributeName + + // check if the current attribute already exists in this namespace + if (acc.get(attrName).nonEmpty) { + throw BadRequestException("Duplicate attribute name in namespace") + } - // if "datatype" is given, create a `XMLStandoffDataTypeClass` - val dataTypeOption: Option[XMLStandoffDataTypeClass] = curEle.standoffDataTypeClass match { + // get the standoff property IRI for the current attribute + val propIri = attrEle.standoffProperty - case Some(dataTypeClass: MappingStandoffDatatypeClass) => - val dataType = - StandoffDataTypeClasses.lookup(dataTypeClass.datatype, - throw BadRequestException(s"Invalid data type provided for $tagname")) + // add the current attribute to the collection + acc + (attrName -> propIri) + } - val dataTypeAttribute = dataTypeClass.attributeName + namespace -> attributesInNamespace + } - Some( - XMLStandoffDataTypeClass( - standoffDataTypeClass = dataType, - dataTypeXMLAttribute = dataTypeAttribute - )) + // if "datatype" is given, create a `XMLStandoffDataTypeClass` + val dataTypeOption: Option[XMLStandoffDataTypeClass] = curEle.standoffDataTypeClass match { - case None => None - } + case Some(dataTypeClass: MappingStandoffDatatypeClass) => + val dataType = + StandoffDataTypeClasses.lookup( + dataTypeClass.datatype, + throw BadRequestException(s"Invalid data type provided for $tagname") + ) - // add the current tag to the map - val newNamespaceMap: Map[String, Map[String, XMLTag]] = namespaceMap.get(tagname) match { - case Some(tagMap: Map[String, XMLTag]) => - tagMap.get(classname) match { - case Some(existingClassname) => - throw BadRequestException("Duplicate tag and classname combination in the same namespace") - case None => - // create the definition for the current element - val xmlElementDef = XMLTag( - name = tagname, - mapping = XMLTagToStandoffClass(standoffClassIri = standoffClassIri, - attributesToProps = attributes, - dataType = dataTypeOption), - separatorRequired = curEle.separatorRequired - ) + val dataTypeAttribute = dataTypeClass.attributeName - // combine the definition for the this classname with the existing definitions beloning to the same element - val combinedClassDef: Map[String, XMLTag] = namespaceMap(tagname) + (classname -> xmlElementDef) + Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = dataType, + dataTypeXMLAttribute = dataTypeAttribute + ) + ) - // combine all elements for this namespace - namespaceMap + (tagname -> combinedClassDef) + case None => None + } - } - case None => - namespaceMap + (tagname -> Map( - classname -> XMLTag( + // add the current tag to the map + val newNamespaceMap: Map[String, Map[String, XMLTag]] = namespaceMap.get(tagname) match { + case Some(tagMap: Map[String, XMLTag]) => + tagMap.get(classname) match { + case Some(existingClassname) => + throw BadRequestException("Duplicate tag and classname combination in the same namespace") + case None => + // create the definition for the current element + val xmlElementDef = XMLTag( name = tagname, - mapping = XMLTagToStandoffClass(standoffClassIri = standoffClassIri, - attributesToProps = attributes, - dataType = dataTypeOption), + mapping = XMLTagToStandoffClass( + standoffClassIri = standoffClassIri, + attributesToProps = attributes, + dataType = dataTypeOption + ), separatorRequired = curEle.separatorRequired - ))) - } + ) - // recreate the whole structure for all namespaces - MappingXMLtoStandoff( - namespace = acc.namespace + (namespace -> newNamespaceMap), - defaultXSLTransformation = defaultXSLTransformation - ) + // combine the definition for the this classname with the existing definitions beloning to the same element + val combinedClassDef: Map[String, XMLTag] = namespaceMap(tagname) + (classname -> xmlElementDef) + + // combine all elements for this namespace + namespaceMap + (tagname -> combinedClassDef) + + } + case None => + namespaceMap + (tagname -> Map( + classname -> XMLTag( + name = tagname, + mapping = XMLTagToStandoffClass( + standoffClassIri = standoffClassIri, + attributesToProps = attributes, + dataType = dataTypeOption + ), + separatorRequired = curEle.separatorRequired + ) + )) + } + + // recreate the whole structure for all namespaces + MappingXMLtoStandoff( + namespace = acc.namespace + (namespace -> newNamespaceMap), + defaultXSLTransformation = defaultXSLTransformation + ) } @@ -711,21 +760,23 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon } /** - * The name of the mapping cache. - */ + * The name of the mapping cache. + */ val mappingCacheName = "mappingCache" /** - * Gets a mapping either from the cache or by making a request to the triplestore. - * - * @param mappingIri the IRI of the mapping to retrieve. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[MappingXMLtoStandoff]]. - */ - private def getMappingV2(mappingIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[GetMappingResponseV2] = { + * Gets a mapping either from the cache or by making a request to the triplestore. + * + * @param mappingIri the IRI of the mapping to retrieve. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[MappingXMLtoStandoff]]. + */ + private def getMappingV2( + mappingIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[GetMappingResponseV2] = { val mappingFuture: Future[GetMappingResponseV2] = CacheUtil.get[MappingXMLtoStandoff](cacheName = mappingCacheName, key = mappingIri) match { @@ -734,12 +785,11 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon entities: StandoffEntityInfoGetResponseV2 <- getStandoffEntitiesFromMappingV2(mapping, requestingUser) - } yield - GetMappingResponseV2( - mappingIri = mappingIri, - mapping = mapping, - standoffEntities = entities - ) + } yield GetMappingResponseV2( + mappingIri = mappingIri, + mapping = mapping, + standoffEntities = entities + ) case None => for { @@ -751,17 +801,15 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon entities: StandoffEntityInfoGetResponseV2 <- getStandoffEntitiesFromMappingV2(mapping, requestingUser) - } yield - GetMappingResponseV2( - mappingIri = mappingIri, - mapping = mapping, - standoffEntities = entities - ) + } yield GetMappingResponseV2( + mappingIri = mappingIri, + mapping = mapping, + standoffEntities = entities + ) } - val mappingRecovered: Future[GetMappingResponseV2] = mappingFuture.recover { - case e: Exception => - throw BadRequestException(s"An error occurred when requesting mapping $mappingIri: ${e.getMessage}") + val mappingRecovered: Future[GetMappingResponseV2] = mappingFuture.recover { case e: Exception => + throw BadRequestException(s"An error occurred when requesting mapping $mappingIri: ${e.getMessage}") } for { @@ -771,17 +819,18 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon } /** - * - * Gets a mapping from the triplestore. - * - * @param mappingIri the IRI of the mapping to retrieve. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[MappingXMLtoStandoff]]. - */ - private def getMappingFromTriplestore(mappingIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[MappingXMLtoStandoff] = { + * Gets a mapping from the triplestore. + * + * @param mappingIri the IRI of the mapping to retrieve. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[MappingXMLtoStandoff]]. + */ + private def getMappingFromTriplestore( + mappingIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[MappingXMLtoStandoff] = { val getMappingSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt .getMapping( @@ -794,7 +843,7 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon mappingResponse: SparqlConstructResponse <- (storeManager ? SparqlConstructRequest( sparql = getMappingSparql, - featureFactoryConfig = featureFactoryConfig, + featureFactoryConfig = featureFactoryConfig )).mapTo[SparqlConstructResponse] // if the result is empty, the mapping does not exist @@ -803,10 +852,9 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon } // separate MappingElements from other statements (attributes and datatypes) - (mappingElementStatements: Map[IRI, Seq[(IRI, String)]], otherStatements: Map[IRI, Seq[(IRI, String)]]) = mappingResponse.statements - .partition { - case (subjectIri: IRI, assertions: Seq[(IRI, String)]) => - assertions.contains((OntologyConstants.Rdf.Type, OntologyConstants.KnoraBase.MappingElement)) + (mappingElementStatements: Map[IRI, Seq[(IRI, String)]], otherStatements: Map[IRI, Seq[(IRI, String)]]) = + mappingResponse.statements.partition { case (subjectIri: IRI, assertions: Seq[(IRI, String)]) => + assertions.contains((OntologyConstants.Rdf.Type, OntologyConstants.KnoraBase.MappingElement)) } mappingElements: Seq[MappingElement] = mappingElementStatements.map { @@ -815,22 +863,18 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon val assertionsAsMap: Map[IRI, String] = assertions.toMap // check for attributes - val attributes: Seq[MappingXMLAttribute] = assertions - .filter { - case (propIri, obj) => - propIri == OntologyConstants.KnoraBase.MappingHasXMLAttribute - } - .map { - case (attrProp: IRI, attributeElementIri: String) => - val attributeStatementsAsMap: Map[IRI, String] = otherStatements(attributeElementIri).toMap - - MappingXMLAttribute( - attributeName = attributeStatementsAsMap(OntologyConstants.KnoraBase.MappingHasXMLAttributename), - namespace = attributeStatementsAsMap(OntologyConstants.KnoraBase.MappingHasXMLNamespace), - standoffProperty = attributeStatementsAsMap(OntologyConstants.KnoraBase.MappingHasStandoffProperty), - mappingXMLAttributeElementIri = attributeElementIri - ) - } + val attributes: Seq[MappingXMLAttribute] = assertions.filter { case (propIri, obj) => + propIri == OntologyConstants.KnoraBase.MappingHasXMLAttribute + }.map { case (attrProp: IRI, attributeElementIri: String) => + val attributeStatementsAsMap: Map[IRI, String] = otherStatements(attributeElementIri).toMap + + MappingXMLAttribute( + attributeName = attributeStatementsAsMap(OntologyConstants.KnoraBase.MappingHasXMLAttributename), + namespace = attributeStatementsAsMap(OntologyConstants.KnoraBase.MappingHasXMLNamespace), + standoffProperty = attributeStatementsAsMap(OntologyConstants.KnoraBase.MappingHasStandoffProperty), + mappingXMLAttributeElementIri = attributeElementIri + ) + } // check for standoff data type class val dataTypeOption: Option[IRI] = @@ -851,7 +895,8 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon datatype = dataTypeAssertionsAsMap(OntologyConstants.KnoraBase.MappingHasStandoffClass), attributeName = dataTypeAssertionsAsMap(OntologyConstants.KnoraBase.MappingHasXMLAttributename), mappingStandoffDataTypeClassElementIri = dataTypeElementIri - )) + ) + ) case None => None }, attributes = attributes, @@ -861,18 +906,16 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon }.toSeq // check if there is a default XSL transformation - defaultXSLTransformationOption: Option[IRI] = otherStatements(mappingIri) - .find { - case (pred: IRI, obj: String) => - pred == OntologyConstants.KnoraBase.MappingHasDefaultXSLTransformation - } - .map { - case (hasDefaultTransformation: IRI, xslTransformationIri: IRI) => - xslTransformationIri - } + defaultXSLTransformationOption: Option[IRI] = otherStatements(mappingIri).find { case (pred: IRI, obj: String) => + pred == OntologyConstants.KnoraBase.MappingHasDefaultXSLTransformation + }.map { case (hasDefaultTransformation: IRI, xslTransformationIri: IRI) => + xslTransformationIri + } - mappingXMLToStandoff = transformMappingElementsToMappingXMLtoStandoff(mappingElements, - defaultXSLTransformationOption) + mappingXMLToStandoff = transformMappingElementsToMappingXMLtoStandoff( + mappingElements, + defaultXSLTransformationOption + ) // add the mapping to the cache _ = CacheUtil.put(cacheName = mappingCacheName, key = mappingIri, value = mappingXMLToStandoff) @@ -882,14 +925,16 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon } /** - * Gets the required standoff entities (classes and properties) from the mapping and requests information about these entities from the ontology responder. - * - * @param mappingXMLtoStandoff the mapping to be used. - * @param requestingUser the client that made the request. - * @return a [[StandoffEntityInfoGetResponseV2]] holding information about standoff classes and properties. - */ - private def getStandoffEntitiesFromMappingV2(mappingXMLtoStandoff: MappingXMLtoStandoff, - requestingUser: UserADM): Future[StandoffEntityInfoGetResponseV2] = { + * Gets the required standoff entities (classes and properties) from the mapping and requests information about these entities from the ontology responder. + * + * @param mappingXMLtoStandoff the mapping to be used. + * @param requestingUser the client that made the request. + * @return a [[StandoffEntityInfoGetResponseV2]] holding information about standoff classes and properties. + */ + private def getStandoffEntitiesFromMappingV2( + mappingXMLtoStandoff: MappingXMLtoStandoff, + requestingUser: UserADM + ): Future[StandoffEntityInfoGetResponseV2] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -910,149 +955,159 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon // make sure that the mapping does not contain system or data type standoff properties as attributes // these standoff properties can only be used via the standoff base tag and standoff data type classes val systemOrDatatypePropsAsAttr: Set[IRI] = standoffPropertyIrisFromMapping.intersect( - StandoffProperties.systemProperties ++ StandoffProperties.dataTypeProperties) + StandoffProperties.systemProperties ++ StandoffProperties.dataTypeProperties + ) if (systemOrDatatypePropsAsAttr.nonEmpty) throw InvalidStandoffException( - s"attempt to define attributes for system or data type properties: ${systemOrDatatypePropsAsAttr.mkString(", ")}") + s"attempt to define attributes for system or data type properties: ${systemOrDatatypePropsAsAttr.mkString(", ")}" + ) for { // request information about standoff classes that should be created standoffClassEntities: StandoffEntityInfoGetResponseV2 <- (responderManager ? StandoffEntityInfoGetRequestV2( standoffClassIris = standoffTagIrisFromMapping.map(_.toSmartIri), - requestingUser = requestingUser)).mapTo[StandoffEntityInfoGetResponseV2] + requestingUser = requestingUser + )).mapTo[StandoffEntityInfoGetResponseV2] // check that the ontology responder returned the information for all the standoff classes it was asked for // if the ontology responder does not return a standoff class it was asked for, then this standoff class does not exist _ = if (standoffTagIrisFromMapping.map(_.toSmartIri) != standoffClassEntities.standoffClassInfoMap.keySet) { throw NotFoundException( s"the ontology responder could not find information about these standoff classes: ${(standoffTagIrisFromMapping - .map(_.toSmartIri) -- standoffClassEntities.standoffClassInfoMap.keySet).mkString(", ")}") + .map(_.toSmartIri) -- standoffClassEntities.standoffClassInfoMap.keySet).mkString(", ")}" + ) } // get the property Iris that are defined on the standoff classes returned by the ontology responder standoffPropertyIrisFromOntologyResponder: Set[SmartIri] = standoffClassEntities.standoffClassInfoMap.foldLeft( - Set.empty[SmartIri]) { - case (acc, (standoffClassIri, standoffClassEntity: ReadClassInfoV2)) => - val props = standoffClassEntity.allCardinalities.keySet - acc ++ props + Set.empty[SmartIri] + ) { case (acc, (standoffClassIri, standoffClassEntity: ReadClassInfoV2)) => + val props = standoffClassEntity.allCardinalities.keySet + acc ++ props } // request information about the standoff properties standoffPropertyEntities: StandoffEntityInfoGetResponseV2 <- (responderManager ? StandoffEntityInfoGetRequestV2( standoffPropertyIris = standoffPropertyIrisFromOntologyResponder, - requestingUser = requestingUser)).mapTo[StandoffEntityInfoGetResponseV2] + requestingUser = requestingUser + )).mapTo[StandoffEntityInfoGetResponseV2] // check that the ontology responder returned the information for all the standoff properties it was asked for // if the ontology responder does not return a standoff property it was asked for, then this standoff property does not exist - propertyDefinitionsFromMappingFoundInOntology: Set[SmartIri] = standoffPropertyEntities.standoffPropertyInfoMap.keySet - .intersect(standoffPropertyIrisFromMapping.map(_.toSmartIri)) + propertyDefinitionsFromMappingFoundInOntology: Set[SmartIri] = + standoffPropertyEntities.standoffPropertyInfoMap.keySet + .intersect(standoffPropertyIrisFromMapping.map(_.toSmartIri)) _ = if (standoffPropertyIrisFromMapping.map(_.toSmartIri) != propertyDefinitionsFromMappingFoundInOntology) { throw NotFoundException( s"the ontology responder could not find information about these standoff properties: " + s"${(standoffPropertyIrisFromMapping.map(_.toSmartIri) -- propertyDefinitionsFromMappingFoundInOntology) - .mkString(", ")}") + .mkString(", ")}" + ) } // check that for each standoff property defined in the mapping element for a standoff class, a corresponding cardinality exists in the ontology - _ = mappingStandoffToXML.foreach { - case (standoffClass: IRI, xmlTag: XMLTagItem) => - // collect all the standoff properties defined for this standoff class - val standoffPropertiesForStandoffClass: Set[SmartIri] = xmlTag.attributes.keySet.map(_.toSmartIri) - - // check that the current standoff class has cardinalities for all the properties defined - val cardinalitiesFound = standoffClassEntities - .standoffClassInfoMap(standoffClass.toSmartIri) - .allCardinalities - .keySet - .intersect(standoffPropertiesForStandoffClass) - - if (standoffPropertiesForStandoffClass != cardinalitiesFound) { - throw NotFoundException( - s"the following standoff properties have no cardinality for $standoffClass: ${(standoffPropertiesForStandoffClass -- cardinalitiesFound) - .mkString(", ")}") - } + _ = mappingStandoffToXML.foreach { case (standoffClass: IRI, xmlTag: XMLTagItem) => + // collect all the standoff properties defined for this standoff class + val standoffPropertiesForStandoffClass: Set[SmartIri] = xmlTag.attributes.keySet.map(_.toSmartIri) + + // check that the current standoff class has cardinalities for all the properties defined + val cardinalitiesFound = standoffClassEntities + .standoffClassInfoMap(standoffClass.toSmartIri) + .allCardinalities + .keySet + .intersect(standoffPropertiesForStandoffClass) + + if (standoffPropertiesForStandoffClass != cardinalitiesFound) { + throw NotFoundException( + s"the following standoff properties have no cardinality for $standoffClass: ${(standoffPropertiesForStandoffClass -- cardinalitiesFound) + .mkString(", ")}" + ) + } - // collect the required standoff properties for the standoff class - val requiredPropsForClass: Set[SmartIri] = standoffClassEntities - .standoffClassInfoMap(standoffClass.toSmartIri) - .allCardinalities - .filter { - case (property: SmartIri, card: KnoraCardinalityInfo) => - card.cardinality == Cardinality.MustHaveOne || card.cardinality == Cardinality.MustHaveSome - } - .keySet -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.dataTypeProperties - .map(_.toSmartIri) - - // check that all the required standoff properties exist in the mapping - if (standoffPropertiesForStandoffClass.intersect(requiredPropsForClass) != requiredPropsForClass) { - throw NotFoundException( - s"the following required standoff properties are not defined for the standoff class $standoffClass: ${(requiredPropsForClass -- standoffPropertiesForStandoffClass) - .mkString(", ")}") + // collect the required standoff properties for the standoff class + val requiredPropsForClass: Set[SmartIri] = standoffClassEntities + .standoffClassInfoMap(standoffClass.toSmartIri) + .allCardinalities + .filter { case (property: SmartIri, card: KnoraCardinalityInfo) => + card.cardinality == Cardinality.MustHaveOne || card.cardinality == Cardinality.MustHaveSome } + .keySet -- StandoffProperties.systemProperties.map(_.toSmartIri) -- StandoffProperties.dataTypeProperties + .map(_.toSmartIri) + + // check that all the required standoff properties exist in the mapping + if (standoffPropertiesForStandoffClass.intersect(requiredPropsForClass) != requiredPropsForClass) { + throw NotFoundException( + s"the following required standoff properties are not defined for the standoff class $standoffClass: ${(requiredPropsForClass -- standoffPropertiesForStandoffClass) + .mkString(", ")}" + ) + } - // check if the standoff class's data type is correct in the mapping - standoffClassEntities.standoffClassInfoMap(standoffClass.toSmartIri).standoffDataType match { - case Some(dataType: StandoffDataTypeClasses.Value) => - // check if this corresponds to the datatype in the mapping - val dataTypeFromMapping: XMLStandoffDataTypeClass = xmlTag.tagItem.mapping.dataType.getOrElse( - throw InvalidStandoffException(s"no data type provided for $standoffClass, but $dataType required")) - if (dataTypeFromMapping.standoffDataTypeClass != dataType) { - throw InvalidStandoffException( - s"wrong data type ${dataTypeFromMapping.standoffDataTypeClass} provided for $standoffClass, but $dataType required") - } - case None => - if (xmlTag.tagItem.mapping.dataType.nonEmpty) { - throw InvalidStandoffException( - s"no data type expected for $standoffClass, but ${xmlTag.tagItem.mapping.dataType.get.standoffDataTypeClass} given") - } - } + // check if the standoff class's data type is correct in the mapping + standoffClassEntities.standoffClassInfoMap(standoffClass.toSmartIri).standoffDataType match { + case Some(dataType: StandoffDataTypeClasses.Value) => + // check if this corresponds to the datatype in the mapping + val dataTypeFromMapping: XMLStandoffDataTypeClass = xmlTag.tagItem.mapping.dataType.getOrElse( + throw InvalidStandoffException(s"no data type provided for $standoffClass, but $dataType required") + ) + if (dataTypeFromMapping.standoffDataTypeClass != dataType) { + throw InvalidStandoffException( + s"wrong data type ${dataTypeFromMapping.standoffDataTypeClass} provided for $standoffClass, but $dataType required" + ) + } + case None => + if (xmlTag.tagItem.mapping.dataType.nonEmpty) { + throw InvalidStandoffException( + s"no data type expected for $standoffClass, but ${xmlTag.tagItem.mapping.dataType.get.standoffDataTypeClass} given" + ) + } + } } - } yield - StandoffEntityInfoGetResponseV2( - standoffClassInfoMap = standoffClassEntities.standoffClassInfoMap, - standoffPropertyInfoMap = standoffPropertyEntities.standoffPropertyInfoMap - ) + } yield StandoffEntityInfoGetResponseV2( + standoffClassInfoMap = standoffClassEntities.standoffClassInfoMap, + standoffPropertyInfoMap = standoffPropertyEntities.standoffPropertyInfoMap + ) } /** - * A [[TaskResult]] containing a page of standoff queried from a text value. - * - * @param underlyingResult the underlying standoff result. - * @param nextTask the next task, or `None` if there is no more standoff to query in the text value. - */ + * A [[TaskResult]] containing a page of standoff queried from a text value. + * + * @param underlyingResult the underlying standoff result. + * @param nextTask the next task, or `None` if there is no more standoff to query in the text value. + */ case class StandoffTaskResult(underlyingResult: StandoffTaskUnderlyingResult, nextTask: Option[GetStandoffTask]) extends TaskResult[StandoffTaskUnderlyingResult] /** - * The underlying result type contained in a [[StandoffTaskResult]]. - * - * @param standoff the standoff that was queried. - */ + * The underlying result type contained in a [[StandoffTaskResult]]. + * + * @param standoff the standoff that was queried. + */ case class StandoffTaskUnderlyingResult(standoff: Vector[StandoffTagV2]) /** - * A task that gets a page of standoff from a text value. - * - * @param resourceIri the IRI of the resource containing the value. - * @param valueIri the IRI of the value. - * @param offset the start index of the first standoff tag to be returned. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ - case class GetStandoffTask(resourceIri: IRI, - valueIri: IRI, - offset: Int, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends Task[StandoffTaskUnderlyingResult] { - override def runTask(previousResult: Option[TaskResult[StandoffTaskUnderlyingResult]])( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TaskResult[StandoffTaskUnderlyingResult]] = { + * A task that gets a page of standoff from a text value. + * + * @param resourceIri the IRI of the resource containing the value. + * @param valueIri the IRI of the value. + * @param offset the start index of the first standoff tag to be returned. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ + case class GetStandoffTask( + resourceIri: IRI, + valueIri: IRI, + offset: Int, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ) extends Task[StandoffTaskUnderlyingResult] { + override def runTask( + previousResult: Option[TaskResult[StandoffTaskUnderlyingResult]] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TaskResult[StandoffTaskUnderlyingResult]] = for { // Get a page of standoff. standoffResponse <- getStandoffV2( @@ -1072,34 +1127,32 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon definedPreviousResult.underlyingResult.standoff ++ standoffResponse.standoff case None => standoffResponse.standoff.toVector } - } yield - standoffResponse.nextOffset match { - case Some(definedNextOffset) => - // There is more standoff to query. Return the collected standoff and the next task. - StandoffTaskResult( - underlyingResult = StandoffTaskUnderlyingResult(collectedStandoff), - nextTask = Some(copy(offset = definedNextOffset)) - ) + } yield standoffResponse.nextOffset match { + case Some(definedNextOffset) => + // There is more standoff to query. Return the collected standoff and the next task. + StandoffTaskResult( + underlyingResult = StandoffTaskUnderlyingResult(collectedStandoff), + nextTask = Some(copy(offset = definedNextOffset)) + ) - case None => - // There is no more standoff to query. Just return the collected standoff. - StandoffTaskResult( - underlyingResult = StandoffTaskUnderlyingResult(collectedStandoff), - nextTask = None - ) - } - } + case None => + // There is no more standoff to query. Just return the collected standoff. + StandoffTaskResult( + underlyingResult = StandoffTaskUnderlyingResult(collectedStandoff), + nextTask = None + ) + } } /** - * Returns all pages of standoff markup from a text value, except for the first page. - * - * @param getRemainingStandoffFromTextValueRequestV2 the request message. - * @return the text value's standoff markup. - */ + * Returns all pages of standoff markup from a text value, except for the first page. + * + * @param getRemainingStandoffFromTextValueRequestV2 the request message. + * @return the text value's standoff markup. + */ private def getRemainingStandoffFromTextValueV2( - getRemainingStandoffFromTextValueRequestV2: GetRemainingStandoffFromTextValueRequestV2) - : Future[GetStandoffResponseV2] = { + getRemainingStandoffFromTextValueRequestV2: GetRemainingStandoffFromTextValueRequestV2 + ): Future[GetStandoffResponseV2] = { val firstTask = GetStandoffTask( resourceIri = getRemainingStandoffFromTextValueRequestV2.resourceIri, valueIri = getRemainingStandoffFromTextValueRequestV2.valueIri, @@ -1110,9 +1163,10 @@ class StandoffResponderV2(responderData: ResponderData) extends Responder(respon for { result: TaskResult[StandoffTaskUnderlyingResult] <- ActorUtil.runTasks(firstTask) - } yield - GetStandoffResponseV2(valueIri = getRemainingStandoffFromTextValueRequestV2.valueIri, - standoff = result.underlyingResult.standoff, - nextOffset = None) + } yield GetStandoffResponseV2( + valueIri = getRemainingStandoffFromTextValueRequestV2.valueIri, + standoff = result.underlyingResult.standoff, + nextOffset = None + ) } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala index 67548d18f6..d1a8c11621 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala @@ -49,22 +49,22 @@ import org.knora.webapi.util.ActorUtil import scala.concurrent.Future /** - * Handles requests to read and write Knora values. - */ + * Handles requests to read and write Knora values. + */ class ValuesResponderV2(responderData: ResponderData) extends Responder(responderData) { /** - * The IRI and content of a new value or value version whose existence in the triplestore has been verified. - * - * @param newValueIri the IRI that was assigned to the new value. - * @param value the content of the new value. - * @param permissions the permissions attached to the new value. - */ + * The IRI and content of a new value or value version whose existence in the triplestore has been verified. + * + * @param newValueIri the IRI that was assigned to the new value. + * @param value the content of the new value. + * @param permissions the permissions attached to the new value. + */ private case class VerifiedValueV2(newValueIri: IRI, value: ValueContentV2, permissions: String) /** - * Receives a message of type [[ValuesResponderRequestV2]], and returns an appropriate response message. - */ + * Receives a message of type [[ValuesResponderRequestV2]], and returns an appropriate response message. + */ def receive(msg: ValuesResponderRequestV2) = msg match { case createValueRequest: CreateValueRequestV2 => createValueV2(createValueRequest) case updateValueRequest: UpdateValueRequestV2 => updateValueV2(updateValueRequest) @@ -75,17 +75,18 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Creates a new value in an existing resource. - * - * @param createValueRequest the request to create the value. - * @return a [[CreateValueResponseV2]]. - */ + * Creates a new value in an existing resource. + * + * @param createValueRequest the request to create the value. + * @return a [[CreateValueResponseV2]]. + */ private def createValueV2(createValueRequest: CreateValueRequestV2): Future[CreateValueResponseV2] = { def makeTaskFuture: Future[CreateValueResponseV2] = { for { // Convert the submitted value to the internal schema. submittedInternalPropertyIri: SmartIri <- Future( - createValueRequest.createValue.propertyIri.toOntologySchema(InternalSchema)) + createValueRequest.createValue.propertyIri.toOntologySchema(InternalSchema) + ) submittedInternalValueContent: ValueContentV2 = createValueRequest.createValue.valueContent .toOntologySchema(InternalSchema) @@ -97,21 +98,27 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = createValueRequest.requestingUser ) - propertyInfoResponseForSubmittedProperty: ReadOntologyV2 <- (responderManager ? propertyInfoRequestForSubmittedProperty) - .mapTo[ReadOntologyV2] + propertyInfoResponseForSubmittedProperty: ReadOntologyV2 <- + (responderManager ? propertyInfoRequestForSubmittedProperty) + .mapTo[ReadOntologyV2] propertyInfoForSubmittedProperty: ReadPropertyInfoV2 = propertyInfoResponseForSubmittedProperty.properties( - submittedInternalPropertyIri) + submittedInternalPropertyIri + ) // Don't accept link properties. _ = if (propertyInfoForSubmittedProperty.isLinkProp) { throw BadRequestException( - s"Invalid property <${createValueRequest.createValue.propertyIri}>. Use a link value property to submit a link.") + s"Invalid property <${createValueRequest.createValue.propertyIri}>. Use a link value property to submit a link." + ) } // Don't accept knora-api:hasStandoffLinkToValue. - _ = if (createValueRequest.createValue.propertyIri.toString == OntologyConstants.KnoraApiV2Complex.HasStandoffLinkToValue) { + _ = if ( + createValueRequest.createValue.propertyIri.toString == OntologyConstants.KnoraApiV2Complex.HasStandoffLinkToValue + ) { throw BadRequestException( - s"Values of <${createValueRequest.createValue.propertyIri}> cannot be created directly") + s"Values of <${createValueRequest.createValue.propertyIri}> cannot be created directly" + ) } // Make an adjusted version of the submitted property: if it's a link value property, substitute the @@ -147,10 +154,14 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Check that the resource has the rdf:type that the client thinks it has. - _ = if (resourceInfo.resourceClassIri != createValueRequest.createValue.resourceClassIri.toOntologySchema( - InternalSchema)) { + _ = if ( + resourceInfo.resourceClassIri != createValueRequest.createValue.resourceClassIri.toOntologySchema( + InternalSchema + ) + ) { throw BadRequestException( - s"The rdf:type of resource <${createValueRequest.createValue.resourceIri}> is not <${createValueRequest.createValue.resourceClassIri}>") + s"The rdf:type of resource <${createValueRequest.createValue.resourceIri}> is not <${createValueRequest.createValue.resourceClassIri}>" + ) } // Get the definition of the resource class. @@ -169,8 +180,9 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde cardinalityInfo: Cardinality.KnoraCardinalityInfo = classInfo.allCardinalities.getOrElse( submittedInternalPropertyIri, throw BadRequestException( - s"Resource <${createValueRequest.createValue.resourceIri}> belongs to class <${resourceInfo.resourceClassIri.toOntologySchema( - ApiV2Complex)}>, which has no cardinality for property <${createValueRequest.createValue.propertyIri}>") + s"Resource <${createValueRequest.createValue.resourceIri}> belongs to class <${resourceInfo.resourceClassIri + .toOntologySchema(ApiV2Complex)}>, which has no cardinality for property <${createValueRequest.createValue.propertyIri}>" + ) ) // Check that the object of the adjusted property (the value to be created, or the target of the link to be created) will have @@ -197,22 +209,33 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde currentValuesForProp: Seq[ReadValueV2] = resourceInfo.values .getOrElse(submittedInternalPropertyIri, Seq.empty[ReadValueV2]) - _ = if ((cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome) && currentValuesForProp.isEmpty) { - throw InconsistentRepositoryDataException(s"Resource class <${resourceInfo.resourceClassIri.toOntologySchema( - ApiV2Complex)}> has a cardinality of ${cardinalityInfo.cardinality} on property <${createValueRequest.createValue.propertyIri}>, but resource <${createValueRequest.createValue.resourceIri}> has no value for that property") + _ = if ( + (cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome) && currentValuesForProp.isEmpty + ) { + throw InconsistentRepositoryDataException( + s"Resource class <${resourceInfo.resourceClassIri + .toOntologySchema(ApiV2Complex)}> has a cardinality of ${cardinalityInfo.cardinality} on property <${createValueRequest.createValue.propertyIri}>, but resource <${createValueRequest.createValue.resourceIri}> has no value for that property" + ) } - _ = if (cardinalityInfo.cardinality == Cardinality.MustHaveOne || (cardinalityInfo.cardinality == Cardinality.MayHaveOne && currentValuesForProp.nonEmpty)) { - throw OntologyConstraintException(s"Resource class <${resourceInfo.resourceClassIri.toOntologySchema( - ApiV2Complex)}> has a cardinality of ${cardinalityInfo.cardinality} on property <${createValueRequest.createValue.propertyIri}>, and this does not allow a value to be added for that property to resource <${createValueRequest.createValue.resourceIri}>") + _ = if ( + cardinalityInfo.cardinality == Cardinality.MustHaveOne || (cardinalityInfo.cardinality == Cardinality.MayHaveOne && currentValuesForProp.nonEmpty) + ) { + throw OntologyConstraintException( + s"Resource class <${resourceInfo.resourceClassIri + .toOntologySchema(ApiV2Complex)}> has a cardinality of ${cardinalityInfo.cardinality} on property <${createValueRequest.createValue.propertyIri}>, and this does not allow a value to be added for that property to resource <${createValueRequest.createValue.resourceIri}>" + ) } // Check that the new value would not duplicate an existing value. unescapedSubmittedInternalValueContent = submittedInternalValueContent.unescape - _ = if (currentValuesForProp.exists(currentVal => - unescapedSubmittedInternalValueContent.wouldDuplicateOtherValue(currentVal.valueContent))) { + _ = if ( + currentValuesForProp.exists(currentVal => + unescapedSubmittedInternalValueContent.wouldDuplicateOtherValue(currentVal.valueContent) + ) + ) { throw DuplicateValueException() } @@ -251,8 +274,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde ) // Is the requesting user a system admin, or an admin of this project? - _ = if (!(createValueRequest.requestingUser.permissions.isProjectAdmin( - createValueRequest.requestingUser.id) || createValueRequest.requestingUser.permissions.isSystemAdmin)) { + _ = if ( + !(createValueRequest.requestingUser.permissions.isProjectAdmin( + createValueRequest.requestingUser.id + ) || createValueRequest.requestingUser.permissions.isSystemAdmin) + ) { // No. Make sure they don't give themselves higher permissions than they would get from the default permissions. @@ -266,7 +292,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde if (permissionComparisonResult == AGreaterThanB) { throw ForbiddenException( - s"The specified value permissions would give a value's creator a higher permission on the value than the default permissions") + s"The specified value permissions would give a value's creator a higher permission on the value than the default permissions" + ) } } } yield validatedCustomPermissions @@ -303,14 +330,13 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde featureFactoryConfig = createValueRequest.featureFactoryConfig, requestingUser = createValueRequest.requestingUser ) - } yield - CreateValueResponseV2( - valueIri = verifiedValue.newValueIri, - valueType = verifiedValue.value.valueType, - valueUUID = unverifiedValue.newValueUUID, - valueCreationDate = unverifiedValue.creationDate, - projectADM = resourceInfo.projectADM - ) + } yield CreateValueResponseV2( + valueIri = verifiedValue.newValueIri, + valueType = verifiedValue.value.valueType, + valueUUID = unverifiedValue.newValueUUID, + valueCreationDate = unverifiedValue.creationDate, + projectADM = resourceInfo.projectADM + ) } val triplestoreUpdateFuture: Future[CreateValueResponseV2] = for { @@ -349,35 +375,37 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Creates a new value (either an ordinary value or a link), using an existing transaction, assuming that - * pre-update checks have already been done. - * - * @param dataNamedGraph the named graph in which the value is to be created. - * @param projectIri the IRI of the project in which to create the value. - * @param resourceInfo information about the the resource in which to create the value. - * @param propertyIri the IRI of the property that will point from the resource to the value, or, if - * the value is a link value, the IRI of the link property. - * @param value the value to create. - * @param valueIri the optional custom IRI supplied for the value. - * @param valueUUID the optional custom UUID supplied for the value. - * @param valueCreationDate the optional custom creation date supplied for the value. - * @param valueCreator the IRI of the new value's owner. - * @param valuePermissions the literal that should be used as the object of the new value's - * `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return an [[UnverifiedValueV2]]. - */ - private def createValueV2AfterChecks(dataNamedGraph: IRI, - projectIri: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - value: ValueContentV2, - valueIri: Option[SmartIri], - valueUUID: Option[UUID], - valueCreationDate: Option[Instant], - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[UnverifiedValueV2] = { + * Creates a new value (either an ordinary value or a link), using an existing transaction, assuming that + * pre-update checks have already been done. + * + * @param dataNamedGraph the named graph in which the value is to be created. + * @param projectIri the IRI of the project in which to create the value. + * @param resourceInfo information about the the resource in which to create the value. + * @param propertyIri the IRI of the property that will point from the resource to the value, or, if + * the value is a link value, the IRI of the link property. + * @param value the value to create. + * @param valueIri the optional custom IRI supplied for the value. + * @param valueUUID the optional custom UUID supplied for the value. + * @param valueCreationDate the optional custom creation date supplied for the value. + * @param valueCreator the IRI of the new value's owner. + * @param valuePermissions the literal that should be used as the object of the new value's + * `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return an [[UnverifiedValueV2]]. + */ + private def createValueV2AfterChecks( + dataNamedGraph: IRI, + projectIri: IRI, + resourceInfo: ReadResourceV2, + propertyIri: SmartIri, + value: ValueContentV2, + valueIri: Option[SmartIri], + valueUUID: Option[UUID], + valueCreationDate: Option[Instant], + valueCreator: IRI, + valuePermissions: String, + requestingUser: UserADM + ): Future[UnverifiedValueV2] = value match { case linkValueContent: LinkValueContentV2 => createLinkValueV2AfterChecks( @@ -407,32 +435,33 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = requestingUser ) } - } /** - * Creates an ordinary value (i.e. not a link), using an existing transaction, assuming that pre-update checks have already been done. - * - * @param resourceInfo information about the the resource in which to create the value. - * @param propertyIri the property that should point to the value. - * @param value an [[ValueContentV2]] describing the value. - * @param maybeValueIri the optional custom IRI supplied for the value. - * @param maybeValueUUID the optional custom UUID supplied for the value. - * @param maybeValueCreationDate the optional custom creation date supplied for the value. - * @param valueCreator the IRI of the new value's owner. - * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return an [[UnverifiedValueV2]]. - */ - private def createOrdinaryValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - value: ValueContentV2, - maybeValueIri: Option[SmartIri], - maybeValueUUID: Option[UUID], - maybeValueCreationDate: Option[Instant], - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[UnverifiedValueV2] = { + * Creates an ordinary value (i.e. not a link), using an existing transaction, assuming that pre-update checks have already been done. + * + * @param resourceInfo information about the the resource in which to create the value. + * @param propertyIri the property that should point to the value. + * @param value an [[ValueContentV2]] describing the value. + * @param maybeValueIri the optional custom IRI supplied for the value. + * @param maybeValueUUID the optional custom UUID supplied for the value. + * @param maybeValueCreationDate the optional custom creation date supplied for the value. + * @param valueCreator the IRI of the new value's owner. + * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return an [[UnverifiedValueV2]]. + */ + private def createOrdinaryValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + propertyIri: SmartIri, + value: ValueContentV2, + maybeValueIri: Option[SmartIri], + maybeValueUUID: Option[UUID], + maybeValueCreationDate: Option[Instant], + valueCreator: IRI, + valuePermissions: String, + requestingUser: UserADM + ): Future[UnverifiedValueV2] = for { // Make a new value UUID. @@ -441,7 +470,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Make an IRI for the new value. newValueIri: IRI <- checkOrCreateEntityIri( maybeValueIri, - stringFormatter.makeRandomValueIri(resourceInfo.resourceIri, Some(newValueUUID))) + stringFormatter.makeRandomValueIri(resourceInfo.resourceIri, Some(newValueUUID)) + ) // Make a creation date for the new value creationDate: Instant = maybeValueCreationDate match { @@ -496,40 +526,40 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Do the update. _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV2( - newValueIri = newValueIri, - newValueUUID = newValueUUID, - valueContent = value.unescape, - permissions = valuePermissions, - creationDate = creationDate - ) - } + } yield UnverifiedValueV2( + newValueIri = newValueIri, + newValueUUID = newValueUUID, + valueContent = value.unescape, + permissions = valuePermissions, + creationDate = creationDate + ) /** - * Creates a link, using an existing transaction, assuming that pre-update checks have already been done. - * - * @param dataNamedGraph the named graph in which the link is to be created. - * @param resourceInfo information about the the resource in which to create the value. - * @param linkPropertyIri the link property. - * @param linkValueContent a [[LinkValueContentV2]] specifying the target resource. - * @param maybeValueIri the optional custom IRI supplied for the value. - * @param maybeValueUUID the optional custom UUID supplied for the value. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return an [[UnverifiedValueV2]]. - */ - private def createLinkValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - linkPropertyIri: SmartIri, - linkValueContent: LinkValueContentV2, - maybeValueIri: Option[SmartIri], - maybeValueUUID: Option[UUID], - maybeCreationDate: Option[Instant], - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[UnverifiedValueV2] = { + * Creates a link, using an existing transaction, assuming that pre-update checks have already been done. + * + * @param dataNamedGraph the named graph in which the link is to be created. + * @param resourceInfo information about the the resource in which to create the value. + * @param linkPropertyIri the link property. + * @param linkValueContent a [[LinkValueContentV2]] specifying the target resource. + * @param maybeValueIri the optional custom IRI supplied for the value. + * @param maybeValueUUID the optional custom UUID supplied for the value. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return an [[UnverifiedValueV2]]. + */ + private def createLinkValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + linkPropertyIri: SmartIri, + linkValueContent: LinkValueContentV2, + maybeValueIri: Option[SmartIri], + maybeValueUUID: Option[UUID], + maybeCreationDate: Option[Instant], + valueCreator: IRI, + valuePermissions: String, + requestingUser: UserADM + ): Future[UnverifiedValueV2] = { // Make a new value UUID. val newValueUUID: UUID = makeNewValueUUID(maybeValueIri, maybeValueUUID) @@ -571,42 +601,42 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Do the update. _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV2( - newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, - newValueUUID = newValueUUID, - valueContent = linkValueContent.unescape, - permissions = valuePermissions, - creationDate = creationDate - ) + } yield UnverifiedValueV2( + newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, + newValueUUID = newValueUUID, + valueContent = linkValueContent.unescape, + permissions = valuePermissions, + creationDate = creationDate + ) } /** - * Represents SPARQL generated to create one of multiple values in a new resource. - * - * @param insertSparql the generated SPARQL. - * @param unverifiedValue an [[UnverifiedValueV2]] representing the value that is to be created. - */ + * Represents SPARQL generated to create one of multiple values in a new resource. + * + * @param insertSparql the generated SPARQL. + * @param unverifiedValue an [[UnverifiedValueV2]] representing the value that is to be created. + */ private case class InsertSparqlWithUnverifiedValue(insertSparql: String, unverifiedValue: UnverifiedValueV2) /** - * Generates SPARQL for creating multiple values. - * - * @param createMultipleValuesRequest the request to create multiple values. - * @return a [[GenerateSparqlToCreateMultipleValuesResponseV2]] containing the generated SPARQL and information - * about the values to be created. - */ + * Generates SPARQL for creating multiple values. + * + * @param createMultipleValuesRequest the request to create multiple values. + * @return a [[GenerateSparqlToCreateMultipleValuesResponseV2]] containing the generated SPARQL and information + * about the values to be created. + */ private def generateSparqlToCreateMultipleValuesV2( - createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV2) - : Future[GenerateSparqlToCreateMultipleValuesResponseV2] = { + createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV2 + ): Future[GenerateSparqlToCreateMultipleValuesResponseV2] = for { // Generate SPARQL to create links and LinkValues for standoff links in text values. sparqlForStandoffLinks: Option[String] <- generateInsertSparqlForStandoffLinksInMultipleValues( - createMultipleValuesRequest) + createMultipleValuesRequest + ) // Generate SPARQL for each value. - sparqlForPropertyValueFutures: Map[SmartIri, Seq[Future[InsertSparqlWithUnverifiedValue]]] = createMultipleValuesRequest.values - .map { + sparqlForPropertyValueFutures: Map[SmartIri, Seq[Future[InsertSparqlWithUnverifiedValue]]] = + createMultipleValuesRequest.values.map { case (propertyIri: SmartIri, valuesToCreate: Seq[GenerateSparqlForValueInNewResourceV2]) => propertyIri -> valuesToCreate.zipWithIndex.map { case (valueToCreate: GenerateSparqlForValueInNewResourceV2, valueHasOrder: Int) => @@ -622,7 +652,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } sparqlForPropertyValues: Map[SmartIri, Seq[InsertSparqlWithUnverifiedValue]] <- ActorUtil.sequenceSeqFuturesInMap( - sparqlForPropertyValueFutures) + sparqlForPropertyValueFutures + ) // Concatenate all the generated SPARQL. allInsertSparql: String = sparqlForPropertyValues.values.flatten @@ -634,39 +665,41 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde case (propertyIri, unverifiedValuesWithSparql) => propertyIri -> unverifiedValuesWithSparql.map(_.unverifiedValue) } - } yield - GenerateSparqlToCreateMultipleValuesResponseV2( - insertSparql = allInsertSparql, - unverifiedValues = unverifiedValues, - hasStandoffLink = sparqlForStandoffLinks.isDefined - ) - } + } yield GenerateSparqlToCreateMultipleValuesResponseV2( + insertSparql = allInsertSparql, + unverifiedValues = unverifiedValues, + hasStandoffLink = sparqlForStandoffLinks.isDefined + ) /** - * Generates SPARQL to create one of multiple values in a new resource. - * - * @param resourceIri the IRI of the resource. - * @param propertyIri the IRI of the property that will point to the value. - * @param valueToCreate the value to be created. - * @param valueHasOrder the value's `knora-base:valueHasOrder`. - * @param resourceCreationDate the creation date of the resource. - * @param requestingUser the user making the request. - * @return a [[InsertSparqlWithUnverifiedValue]] containing the generated SPARQL and an [[UnverifiedValueV2]]. - */ + * Generates SPARQL to create one of multiple values in a new resource. + * + * @param resourceIri the IRI of the resource. + * @param propertyIri the IRI of the property that will point to the value. + * @param valueToCreate the value to be created. + * @param valueHasOrder the value's `knora-base:valueHasOrder`. + * @param resourceCreationDate the creation date of the resource. + * @param requestingUser the user making the request. + * @return a [[InsertSparqlWithUnverifiedValue]] containing the generated SPARQL and an [[UnverifiedValueV2]]. + */ private def generateInsertSparqlWithUnverifiedValue( - resourceIri: IRI, - propertyIri: SmartIri, - valueToCreate: GenerateSparqlForValueInNewResourceV2, - valueHasOrder: Int, - resourceCreationDate: Instant, - requestingUser: UserADM): Future[InsertSparqlWithUnverifiedValue] = { + resourceIri: IRI, + propertyIri: SmartIri, + valueToCreate: GenerateSparqlForValueInNewResourceV2, + valueHasOrder: Int, + resourceCreationDate: Instant, + requestingUser: UserADM + ): Future[InsertSparqlWithUnverifiedValue] = for { // Make new value UUID. newValueUUID: UUID <- Future.successful( - makeNewValueUUID(valueToCreate.customValueIri, valueToCreate.customValueUUID)) + makeNewValueUUID(valueToCreate.customValueIri, valueToCreate.customValueUUID) + ) - newValueIri: IRI <- checkOrCreateEntityIri(valueToCreate.customValueIri, - stringFormatter.makeRandomValueIri(resourceIri, Some(newValueUUID))) + newValueIri: IRI <- checkOrCreateEntityIri( + valueToCreate.customValueIri, + stringFormatter.makeRandomValueIri(resourceIri, Some(newValueUUID)) + ) // Make a creation date for the value. If a custom creation date is given for a value, consider that otherwise // use resource creation date for the value. @@ -719,7 +752,10 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde value = otherValueContentV2, newValueIri = newValueIri, newValueUUID = newValueUUID, - linkUpdates = Seq.empty[SparqlTemplateLinkUpdate], // This is empty because we have to generate SPARQL for standoff links separately. + linkUpdates = + Seq.empty[ + SparqlTemplateLinkUpdate + ], // This is empty because we have to generate SPARQL for standoff links separately. valueCreator = requestingUser.id, valuePermissions = valueToCreate.permissions, creationDate = valueCreationDate, @@ -728,27 +764,26 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde ) .toString() } - } yield - InsertSparqlWithUnverifiedValue( - insertSparql = insertSparql, - unverifiedValue = UnverifiedValueV2( - newValueIri = newValueIri, - newValueUUID = newValueUUID, - valueContent = valueToCreate.valueContent.unescape, - permissions = valueToCreate.permissions, - creationDate = valueCreationDate - ) + } yield InsertSparqlWithUnverifiedValue( + insertSparql = insertSparql, + unverifiedValue = UnverifiedValueV2( + newValueIri = newValueIri, + newValueUUID = newValueUUID, + valueContent = valueToCreate.valueContent.unescape, + permissions = valueToCreate.permissions, + creationDate = valueCreationDate ) - } + ) /** - * When processing a request to create multiple values, generates SPARQL for standoff links in text values. - * - * @param createMultipleValuesRequest the request to create multiple values. - * @return SPARQL INSERT statements. - */ + * When processing a request to create multiple values, generates SPARQL for standoff links in text values. + * + * @param createMultipleValuesRequest the request to create multiple values. + * @return SPARQL INSERT statements. + */ private def generateInsertSparqlForStandoffLinksInMultipleValues( - createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV2): Future[Option[String]] = { + createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV2 + ): Future[Option[String]] = { // To create LinkValues for the standoff links in the values to be created, we need to compute // the initial reference count of each LinkValue. This is equal to the number of TextValues in the resource // that have standoff links to a particular target resource. @@ -788,21 +823,21 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde case (targetIri, initialReferenceCount) => for { newValueIri <- makeUnusedValueIri(createMultipleValuesRequest.resourceIri) - } yield - SparqlTemplateLinkUpdate( - linkPropertyIri = OntologyConstants.KnoraBase.HasStandoffLinkTo.toSmartIri, - directLinkExists = false, - insertDirectLink = true, - deleteDirectLink = false, - linkValueExists = false, - linkTargetExists = true, // doesn't matter, the generateInsertStatementsForStandoffLinks template doesn't use it - newLinkValueIri = newValueIri, - linkTargetIri = targetIri, - currentReferenceCount = 0, - newReferenceCount = initialReferenceCount, - newLinkValueCreator = OntologyConstants.KnoraAdmin.SystemUser, - newLinkValuePermissions = standoffLinkValuePermissions - ) + } yield SparqlTemplateLinkUpdate( + linkPropertyIri = OntologyConstants.KnoraBase.HasStandoffLinkTo.toSmartIri, + directLinkExists = false, + insertDirectLink = true, + deleteDirectLink = false, + linkValueExists = false, + linkTargetExists = + true, // doesn't matter, the generateInsertStatementsForStandoffLinks template doesn't use it + newLinkValueIri = newValueIri, + linkTargetIri = targetIri, + currentReferenceCount = 0, + newReferenceCount = initialReferenceCount, + newLinkValueCreator = OntologyConstants.KnoraAdmin.SystemUser, + newLinkValuePermissions = standoffLinkValuePermissions + ) } for { standoffLinkUpdates: Seq[SparqlTemplateLinkUpdate] <- Future.sequence(standoffLinkUpdatesFutures) @@ -822,45 +857,49 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Creates a new version of an existing value. - * - * @param updateValueRequest the request to update the value. - * @return a [[UpdateValueResponseV2]]. - */ + * Creates a new version of an existing value. + * + * @param updateValueRequest the request to update the value. + * @return a [[UpdateValueResponseV2]]. + */ private def updateValueV2(updateValueRequest: UpdateValueRequestV2): Future[UpdateValueResponseV2] = { /** - * Information about a resource, a submitted property, and a value of the property. - * - * @param resource the contents of the resource. - * @param submittedInternalPropertyIri the internal IRI of the submitted property. - * @param adjustedInternalPropertyInfo the internal definition of the submitted property, adjusted - * as follows: an adjusted version of the submitted property: - * if it's a link value property, substitute the - * corresponding link property. - * @param value the requested value. - */ - case class ResourcePropertyValue(resource: ReadResourceV2, - submittedInternalPropertyIri: SmartIri, - adjustedInternalPropertyInfo: ReadPropertyInfoV2, - value: ReadValueV2) + * Information about a resource, a submitted property, and a value of the property. + * + * @param resource the contents of the resource. + * @param submittedInternalPropertyIri the internal IRI of the submitted property. + * @param adjustedInternalPropertyInfo the internal definition of the submitted property, adjusted + * as follows: an adjusted version of the submitted property: + * if it's a link value property, substitute the + * corresponding link property. + * @param value the requested value. + */ + case class ResourcePropertyValue( + resource: ReadResourceV2, + submittedInternalPropertyIri: SmartIri, + adjustedInternalPropertyInfo: ReadPropertyInfoV2, + value: ReadValueV2 + ) /** - * Gets information about a resource, a submitted property, and a value of the property, and does - * some checks to see if the submitted information is correct. - * - * @param resourceIri the IRI of the resource. - * @param submittedExternalResourceClassIri the submitted external IRI of the resource class. - * @param submittedExternalPropertyIri the submitted external IRI of the property. - * @param valueIri the IRI of the value. - * @param submittedExternalValueType the submitted external IRI of the value type. - * @return a [[ResourcePropertyValue]]. - */ - def getResourcePropertyValue(resourceIri: IRI, - submittedExternalResourceClassIri: SmartIri, - submittedExternalPropertyIri: SmartIri, - valueIri: IRI, - submittedExternalValueType: SmartIri): Future[ResourcePropertyValue] = { + * Gets information about a resource, a submitted property, and a value of the property, and does + * some checks to see if the submitted information is correct. + * + * @param resourceIri the IRI of the resource. + * @param submittedExternalResourceClassIri the submitted external IRI of the resource class. + * @param submittedExternalPropertyIri the submitted external IRI of the property. + * @param valueIri the IRI of the value. + * @param submittedExternalValueType the submitted external IRI of the value type. + * @return a [[ResourcePropertyValue]]. + */ + def getResourcePropertyValue( + resourceIri: IRI, + submittedExternalResourceClassIri: SmartIri, + submittedExternalPropertyIri: SmartIri, + valueIri: IRI, + submittedExternalValueType: SmartIri + ): Future[ResourcePropertyValue] = for { submittedInternalPropertyIri: SmartIri <- Future(submittedExternalPropertyIri.toOntologySchema(InternalSchema)) @@ -872,15 +911,18 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = updateValueRequest.requestingUser ) - propertyInfoResponseForSubmittedProperty: ReadOntologyV2 <- (responderManager ? propertyInfoRequestForSubmittedProperty) - .mapTo[ReadOntologyV2] + propertyInfoResponseForSubmittedProperty: ReadOntologyV2 <- + (responderManager ? propertyInfoRequestForSubmittedProperty) + .mapTo[ReadOntologyV2] propertyInfoForSubmittedProperty: ReadPropertyInfoV2 = propertyInfoResponseForSubmittedProperty.properties( - submittedInternalPropertyIri) + submittedInternalPropertyIri + ) // Don't accept link properties. _ = if (propertyInfoForSubmittedProperty.isLinkProp) { throw BadRequestException( - s"Invalid property <${propertyInfoForSubmittedProperty.entityInfoContent.propertyIri.toOntologySchema(ApiV2Complex)}>. Use a link value property to submit a link.") + s"Invalid property <${propertyInfoForSubmittedProperty.entityInfoContent.propertyIri.toOntologySchema(ApiV2Complex)}>. Use a link value property to submit a link." + ) } // Don't accept knora-api:hasStandoffLinkToValue. @@ -911,7 +953,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ = if (resourceInfo.resourceClassIri != submittedExternalResourceClassIri.toOntologySchema(InternalSchema)) { throw BadRequestException( - s"The rdf:type of resource <$resourceIri> is not <$submittedExternalResourceClassIri>") + s"The rdf:type of resource <$resourceIri> is not <$submittedExternalResourceClassIri>" + ) } // Check that the resource has the value that the user wants to update, as an object of the submitted property. @@ -920,36 +963,37 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde .flatMap(_.find(_.valueIri == valueIri)) .getOrElse { throw NotFoundException( - s"Resource <$resourceIri> does not have value <$valueIri> as an object of property <$submittedExternalPropertyIri>") + s"Resource <$resourceIri> does not have value <$valueIri> as an object of property <$submittedExternalPropertyIri>" + ) } // Check that the current value has the submitted value type. _ = if (currentValue.valueContent.valueType != submittedExternalValueType.toOntologySchema(InternalSchema)) { throw BadRequestException( - s"Value <$valueIri> has type <${currentValue.valueContent.valueType.toOntologySchema(ApiV2Complex)}>, but the submitted type was <$submittedExternalValueType>") + s"Value <$valueIri> has type <${currentValue.valueContent.valueType.toOntologySchema(ApiV2Complex)}>, but the submitted type was <$submittedExternalValueType>" + ) } // If a custom value creation date was submitted, make sure it's later than the date of the current version. _ = if (updateValueRequest.updateValue.valueCreationDate.exists(!_.isAfter(currentValue.valueCreationDate))) { throw BadRequestException("A custom value creation date must be later than the date of the current version") } - } yield - ResourcePropertyValue( - resource = resourceInfo, - submittedInternalPropertyIri = submittedInternalPropertyIri, - adjustedInternalPropertyInfo = adjustedInternalPropertyInfo, - value = currentValue - ) - } + } yield ResourcePropertyValue( + resource = resourceInfo, + submittedInternalPropertyIri = submittedInternalPropertyIri, + adjustedInternalPropertyInfo = adjustedInternalPropertyInfo, + value = currentValue + ) /** - * Updates the permissions attached to a value. - * - * @param updateValuePermissionsV2 the update request. - * @return an [[UpdateValueResponseV2]]. - */ + * Updates the permissions attached to a value. + * + * @param updateValuePermissionsV2 the update request. + * @return an [[UpdateValueResponseV2]]. + */ def makeTaskFutureToUpdateValuePermissions( - updateValuePermissionsV2: UpdateValuePermissionsV2): Future[UpdateValueResponseV2] = { + updateValuePermissionsV2: UpdateValuePermissionsV2 + ): Future[UpdateValueResponseV2] = for { // Do the initial checks, and get information about the resource, the property, and the value. resourcePropertyValue: ResourcePropertyValue <- getResourcePropertyValue( @@ -976,11 +1020,14 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // different from the current ones. currentPermissionsParsed: Map[EntityPermission, Set[IRI]] = PermissionUtilADM.parsePermissions( - currentValue.permissions) + currentValue.permissions + ) newPermissionsParsed: Map[EntityPermission, Set[IRI]] = PermissionUtilADM.parsePermissions( - updateValuePermissionsV2.permissions, { permissionLiteral: String => + updateValuePermissionsV2.permissions, + { permissionLiteral: String => throw AssertionException(s"Invalid permission literal: $permissionLiteral") - }) + } + ) _ = if (newPermissionsParsed == currentPermissionsParsed) { throw BadRequestException(s"The submitted permissions are the same as the current ones") @@ -996,8 +1043,10 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Do the update. dataNamedGraph: IRI = stringFormatter.projectDataNamedGraphV2(resourceInfo.projectADM) - newValueIri: IRI <- checkOrCreateEntityIri(updateValuePermissionsV2.newValueVersionIri, - stringFormatter.makeRandomValueIri(resourceInfo.resourceIri)) + newValueIri: IRI <- checkOrCreateEntityIri( + updateValuePermissionsV2.newValueVersionIri, + stringFormatter.makeRandomValueIri(resourceInfo.resourceIri) + ) currentTime: Instant = updateValuePermissionsV2.valueCreationDate.getOrElse(Instant.now) @@ -1034,23 +1083,22 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde featureFactoryConfig = updateValueRequest.featureFactoryConfig, requestingUser = updateValueRequest.requestingUser ) - } yield - UpdateValueResponseV2( - valueIri = verifiedValue.newValueIri, - valueType = unverifiedValue.valueContent.valueType, - valueUUID = currentValue.valueHasUUID, - projectADM = resourceInfo.projectADM - ) - } + } yield UpdateValueResponseV2( + valueIri = verifiedValue.newValueIri, + valueType = unverifiedValue.valueContent.valueType, + valueUUID = currentValue.valueHasUUID, + projectADM = resourceInfo.projectADM + ) /** - * Updates the contents of a value. - * - * @param updateValueContentV2 the update request. - * @return an [[UpdateValueResponseV2]]. - */ + * Updates the contents of a value. + * + * @param updateValueContentV2 the update request. + * @return an [[UpdateValueResponseV2]]. + */ def makeTaskFutureToUpdateValueContent( - updateValueContentV2: UpdateValueContentV2): Future[UpdateValueResponseV2] = { + updateValueContentV2: UpdateValueContentV2 + ): Future[UpdateValueResponseV2] = { for { // Do the initial checks, and get information about the resource, the property, and the value. resourcePropertyValue: ResourcePropertyValue <- getResourcePropertyValue( @@ -1085,17 +1133,21 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // on the value, they need ChangeRightsPermission, otherwise they need ModifyPermission. currentPermissionsParsed: Map[EntityPermission, Set[IRI]] = PermissionUtilADM.parsePermissions( - currentValue.permissions) + currentValue.permissions + ) newPermissionsParsed: Map[EntityPermission, Set[IRI]] = PermissionUtilADM.parsePermissions( - newValueVersionPermissionLiteral, { permissionLiteral: String => + newValueVersionPermissionLiteral, + { permissionLiteral: String => throw AssertionException(s"Invalid permission literal: $permissionLiteral") - }) + } + ) - permissionNeeded = if (newPermissionsParsed != currentPermissionsParsed) { - ChangeRightsPermission - } else { - ModifyPermission - } + permissionNeeded = + if (newPermissionsParsed != currentPermissionsParsed) { + ChangeRightsPermission + } else { + ModifyPermission + } _ = ResourceUtilV2.checkValuePermission( resourceInfo = resourceInfo, @@ -1106,7 +1158,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Convert the submitted value content to the internal schema. submittedInternalValueContent: ValueContentV2 = updateValueContentV2.valueContent.toOntologySchema( - InternalSchema) + InternalSchema + ) // Check that the object of the adjusted property (the value to be created, or the target of the link to be created) will have // the correct type for the adjusted property's knora-base:objectClassConstraint. @@ -1140,8 +1193,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde .getOrElse(submittedInternalPropertyIri, Seq.empty[ReadValueV2]) .filter(_.valueIri != updateValueContentV2.valueIri) - _ = if (currentValuesForProp.exists(currentVal => - unescapedSubmittedInternalValueContent.wouldDuplicateOtherValue(currentVal.valueContent))) { + _ = if ( + currentValuesForProp.exists(currentVal => + unescapedSubmittedInternalValueContent.wouldDuplicateOtherValue(currentVal.valueContent) + ) + ) { throw DuplicateValueException() } @@ -1212,13 +1268,12 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde featureFactoryConfig = updateValueRequest.featureFactoryConfig, requestingUser = updateValueRequest.requestingUser ) - } yield - UpdateValueResponseV2( - valueIri = verifiedValue.newValueIri, - valueType = unverifiedValue.valueContent.valueType, - valueUUID = unverifiedValue.newValueUUID, - projectADM = resourceInfo.projectADM - ) + } yield UpdateValueResponseV2( + valueIri = verifiedValue.newValueIri, + valueType = unverifiedValue.valueContent.valueType, + valueUUID = unverifiedValue.newValueUUID, + projectADM = resourceInfo.projectADM + ) } if (updateValueRequest.requestingUser.isAnonymousUser) { @@ -1254,41 +1309,47 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done. - * - * @param dataNamedGraph the IRI of the named graph to be updated. - * @param resourceInfo information about the resource containing the value. - * @param propertyIri the IRI of the property that points to the value. - * @param currentValue a [[ReadValueV2]] representing the existing value version. - * @param newValueVersion a [[ValueContentV2]] representing the new value version, in the internal schema. - * @param valueCreator the IRI of the new value's owner. - * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. - * @param valueCreationDate a custom value creation date. - * @param newValueVersionIri an optional IRI to be used for the new value version. - * @param requestingUser the user making the request. - * @return an [[UnverifiedValueV2]]. - */ - private def updateOrdinaryValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - currentValue: ReadValueV2, - newValueVersion: ValueContentV2, - valueCreator: IRI, - valuePermissions: String, - valueCreationDate: Option[Instant], - newValueVersionIri: Option[SmartIri], - requestingUser: UserADM): Future[UnverifiedValueV2] = { + * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done. + * + * @param dataNamedGraph the IRI of the named graph to be updated. + * @param resourceInfo information about the resource containing the value. + * @param propertyIri the IRI of the property that points to the value. + * @param currentValue a [[ReadValueV2]] representing the existing value version. + * @param newValueVersion a [[ValueContentV2]] representing the new value version, in the internal schema. + * @param valueCreator the IRI of the new value's owner. + * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate. + * @param valueCreationDate a custom value creation date. + * @param newValueVersionIri an optional IRI to be used for the new value version. + * @param requestingUser the user making the request. + * @return an [[UnverifiedValueV2]]. + */ + private def updateOrdinaryValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + propertyIri: SmartIri, + currentValue: ReadValueV2, + newValueVersion: ValueContentV2, + valueCreator: IRI, + valuePermissions: String, + valueCreationDate: Option[Instant], + newValueVersionIri: Option[SmartIri], + requestingUser: UserADM + ): Future[UnverifiedValueV2] = for { - newValueIri: IRI <- checkOrCreateEntityIri(newValueVersionIri, - stringFormatter.makeRandomValueIri(resourceInfo.resourceIri)) + newValueIri: IRI <- checkOrCreateEntityIri( + newValueVersionIri, + stringFormatter.makeRandomValueIri(resourceInfo.resourceIri) + ) // If we're updating a text value, update direct links and LinkValues for any resource references in Standoff. standoffLinkUpdates: Seq[SparqlTemplateLinkUpdate] <- (currentValue.valueContent, newValueVersion) match { case (currentTextValue: TextValueContentV2, newTextValue: TextValueContentV2) => // Identify the resource references that have been added or removed in the new version of // the value. - val addedResourceRefs = newTextValue.standoffLinkTagTargetResourceIris -- currentTextValue.standoffLinkTagTargetResourceIris - val removedResourceRefs = currentTextValue.standoffLinkTagTargetResourceIris -- newTextValue.standoffLinkTagTargetResourceIris + val addedResourceRefs = + newTextValue.standoffLinkTagTargetResourceIris -- currentTextValue.standoffLinkTagTargetResourceIris + val removedResourceRefs = + currentTextValue.standoffLinkTagTargetResourceIris -- newTextValue.standoffLinkTagTargetResourceIris // Construct a SparqlTemplateLinkUpdate for each reference that was added. val standoffLinkUpdatesForAddedResourceRefFutures: Seq[Future[SparqlTemplateLinkUpdate]] = @@ -1364,42 +1425,41 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Do the update. _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV2( - newValueIri = newValueIri, - newValueUUID = currentValue.valueHasUUID, - valueContent = newValueVersion.unescape, - permissions = valuePermissions, - creationDate = currentTime - ) - } + } yield UnverifiedValueV2( + newValueIri = newValueIri, + newValueUUID = currentValue.valueHasUUID, + valueContent = newValueVersion.unescape, + permissions = valuePermissions, + creationDate = currentTime + ) /** - * Changes a link, assuming that pre-update checks have already been done. - * - * @param dataNamedGraph the IRI of the named graph to be updated. - * @param resourceInfo information about the resource containing the link. - * @param linkPropertyIri the IRI of the link property. - * @param currentLinkValue a [[ReadLinkValueV2]] representing the `knora-base:LinkValue` for the existing link. - * @param newLinkValue a [[LinkValueContentV2]] indicating the new target resource. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param valueCreationDate a custom value creation date. - * @param newValueVersionIri an optional IRI to be used for the new value version. - * @param requestingUser the user making the request. - * @return an [[UnverifiedValueV2]]. - */ - private def updateLinkValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - linkPropertyIri: SmartIri, - currentLinkValue: ReadLinkValueV2, - newLinkValue: LinkValueContentV2, - valueCreator: IRI, - valuePermissions: String, - valueCreationDate: Option[Instant], - newValueVersionIri: Option[SmartIri], - requestingUser: UserADM): Future[UnverifiedValueV2] = { - + * Changes a link, assuming that pre-update checks have already been done. + * + * @param dataNamedGraph the IRI of the named graph to be updated. + * @param resourceInfo information about the resource containing the link. + * @param linkPropertyIri the IRI of the link property. + * @param currentLinkValue a [[ReadLinkValueV2]] representing the `knora-base:LinkValue` for the existing link. + * @param newLinkValue a [[LinkValueContentV2]] indicating the new target resource. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param valueCreationDate a custom value creation date. + * @param newValueVersionIri an optional IRI to be used for the new value version. + * @param requestingUser the user making the request. + * @return an [[UnverifiedValueV2]]. + */ + private def updateLinkValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + linkPropertyIri: SmartIri, + currentLinkValue: ReadLinkValueV2, + newLinkValue: LinkValueContentV2, + valueCreator: IRI, + valuePermissions: String, + valueCreationDate: Option[Instant], + newValueVersionIri: Option[SmartIri], + requestingUser: UserADM + ): Future[UnverifiedValueV2] = // Are we changing the link target? if (currentLinkValue.valueContent.referredResourceIri != newLinkValue.referredResourceIri) { for { @@ -1446,7 +1506,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = requestingUser.id, stringFormatter = stringFormatter ) - .toString()) + .toString() + ) /* _ = println("================ Update link ================") @@ -1455,14 +1516,13 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde */ _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV2( - newValueIri = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri, - newValueUUID = newLinkValueUUID, - valueContent = newLinkValue.unescape, - permissions = valuePermissions, - creationDate = currentTime - ) + } yield UnverifiedValueV2( + newValueIri = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri, + newValueUUID = newLinkValueUUID, + valueContent = newLinkValue.unescape, + permissions = valuePermissions, + creationDate = currentTime + ) } else { for { // We're not changing the link target, just the metadata on the LinkValue. @@ -1492,28 +1552,27 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde .toString() _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] - } yield - UnverifiedValueV2( - newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, - newValueUUID = currentLinkValue.valueHasUUID, - valueContent = newLinkValue.unescape, - permissions = valuePermissions, - creationDate = currentTime - ) + } yield UnverifiedValueV2( + newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, + newValueUUID = currentLinkValue.valueHasUUID, + valueContent = newLinkValue.unescape, + permissions = valuePermissions, + creationDate = currentTime + ) } - } /** - * Marks a value as deleted. - * - * @param deleteValueRequest the request to mark the value as deleted. - */ + * Marks a value as deleted. + * + * @param deleteValueRequest the request to mark the value as deleted. + */ private def deleteValueV2(deleteValueRequest: DeleteValueRequestV2): Future[SuccessResponseV2] = { def makeTaskFuture: Future[SuccessResponseV2] = { for { // Convert the submitted property IRI to the internal schema. submittedInternalPropertyIri: SmartIri <- Future( - deleteValueRequest.propertyIri.toOntologySchema(InternalSchema)) + deleteValueRequest.propertyIri.toOntologySchema(InternalSchema) + ) // Get ontology information about the submitted property. @@ -1523,15 +1582,18 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = deleteValueRequest.requestingUser ) - propertyInfoResponseForSubmittedProperty: ReadOntologyV2 <- (responderManager ? propertyInfoRequestForSubmittedProperty) - .mapTo[ReadOntologyV2] + propertyInfoResponseForSubmittedProperty: ReadOntologyV2 <- + (responderManager ? propertyInfoRequestForSubmittedProperty) + .mapTo[ReadOntologyV2] propertyInfoForSubmittedProperty: ReadPropertyInfoV2 = propertyInfoResponseForSubmittedProperty.properties( - submittedInternalPropertyIri) + submittedInternalPropertyIri + ) // Don't accept link properties. _ = if (propertyInfoForSubmittedProperty.isLinkProp) { throw BadRequestException( - s"Invalid property <${propertyInfoForSubmittedProperty.entityInfoContent.propertyIri.toOntologySchema(ApiV2Complex)}>. Use a link value property to submit a link.") + s"Invalid property <${propertyInfoForSubmittedProperty.entityInfoContent.propertyIri.toOntologySchema(ApiV2Complex)}>. Use a link value property to submit a link." + ) } // Don't accept knora-api:hasStandoffLinkToValue. @@ -1566,7 +1628,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ = if (resourceInfo.resourceClassIri != deleteValueRequest.resourceClassIri.toOntologySchema(InternalSchema)) { throw BadRequestException( - s"Resource <${deleteValueRequest.resourceIri}> does not belong to class <${deleteValueRequest.resourceClassIri}>") + s"Resource <${deleteValueRequest.resourceIri}> does not belong to class <${deleteValueRequest.resourceClassIri}>" + ) } // Check that the resource has the value that the user wants to delete, as an object of the submitted property. @@ -1581,14 +1644,18 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde case Some(value) => value case None => throw NotFoundException( - s"Resource <${deleteValueRequest.resourceIri}> does not have value <${deleteValueRequest.valueIri}> as an object of property <${deleteValueRequest.propertyIri}>") + s"Resource <${deleteValueRequest.resourceIri}> does not have value <${deleteValueRequest.valueIri}> as an object of property <${deleteValueRequest.propertyIri}>" + ) } // Check that the value is of the type that the client submitted. - _ = if (currentValue.valueContent.valueType != deleteValueRequest.valueTypeIri.toOntologySchema(InternalSchema)) { + _ = if ( + currentValue.valueContent.valueType != deleteValueRequest.valueTypeIri.toOntologySchema(InternalSchema) + ) { throw BadRequestException( - s"Value <${deleteValueRequest.valueIri}> in resource <${deleteValueRequest.resourceIri}> is not of type <${deleteValueRequest.valueTypeIri}>") + s"Value <${deleteValueRequest.valueIri}> in resource <${deleteValueRequest.resourceIri}> is not of type <${deleteValueRequest.valueTypeIri}>" + ) } // Check the user's permissions on the value. @@ -1613,8 +1680,9 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde cardinalityInfo: Cardinality.KnoraCardinalityInfo = classInfo.allCardinalities.getOrElse( submittedInternalPropertyIri, throw InconsistentRepositoryDataException( - s"Resource <${deleteValueRequest.resourceIri}> belongs to class <${resourceInfo.resourceClassIri.toOntologySchema( - ApiV2Complex)}>, which has no cardinality for property <${deleteValueRequest.propertyIri}>") + s"Resource <${deleteValueRequest.resourceIri}> belongs to class <${resourceInfo.resourceClassIri + .toOntologySchema(ApiV2Complex)}>, which has no cardinality for property <${deleteValueRequest.propertyIri}>" + ) ) // Check that the resource class's cardinality for the submitted property allows this value to be deleted. @@ -1622,9 +1690,12 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde currentValuesForProp: Seq[ReadValueV2] = resourceInfo.values .getOrElse(submittedInternalPropertyIri, Seq.empty[ReadValueV2]) - _ = if ((cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome) && currentValuesForProp.size == 1) { - throw OntologyConstraintException(s"Resource class <${resourceInfo.resourceClassIri.toOntologySchema( - ApiV2Complex)}> has a cardinality of ${cardinalityInfo.cardinality} on property <${deleteValueRequest.propertyIri}>, and this does not allow a value to be deleted for that property from resource <${deleteValueRequest.resourceIri}>") + _ = if ( + (cardinalityInfo.cardinality == Cardinality.MustHaveOne || cardinalityInfo.cardinality == Cardinality.MustHaveSome) && currentValuesForProp.size == 1 + ) { + throw OntologyConstraintException( + s"Resource class <${resourceInfo.resourceClassIri.toOntologySchema(ApiV2Complex)}> has a cardinality of ${cardinalityInfo.cardinality} on property <${deleteValueRequest.propertyIri}>, and this does not allow a value to be deleted for that property from resource <${deleteValueRequest.resourceIri}>" + ) } // If a custom delete date was submitted, make sure it's later than the date of the current version. @@ -1658,12 +1729,17 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde sparqlSelectResponse <- (storeManager ? SparqlSelectRequest(sparqlQuery)).mapTo[SparqlSelectResult] rows = sparqlSelectResponse.results.bindings - _ = if (rows.isEmpty || !stringFormatter.optionStringToBoolean( - rows.head.rowMap.get("isDeleted"), - throw InconsistentRepositoryDataException( - s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}"))) { + _ = if ( + rows.isEmpty || !stringFormatter.optionStringToBoolean( + rows.head.rowMap.get("isDeleted"), + throw InconsistentRepositoryDataException( + s"Invalid boolean for isDeleted: ${rows.head.rowMap.get("isDeleted")}" + ) + ) + ) { throw UpdateNotPerformedException( - s"The request to mark value <${deleteValueRequest.valueIri}> (or a new version of that value) as deleted did not succeed. Please report this as a possible bug.") + s"The request to mark value <${deleteValueRequest.valueIri}> (or a new version of that value) as deleted did not succeed. Please report this as a possible bug." + ) } } yield SuccessResponseV2(s"Value <$deletedValueIri> marked as deleted") } @@ -1688,25 +1764,27 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Deletes a value (either an ordinary value or a link), using an existing transaction, assuming that - * pre-update checks have already been done. - * - * @param dataNamedGraph the named graph in which the value is to be deleted. - * @param resourceInfo information about the the resource in which to create the value. - * @param propertyIri the IRI of the property that points from the resource to the value. - * @param currentValue the value to be deleted. - * @param deleteComment an optional comment explaining why the value is being deleted. - * @param deleteDate an optional timestamp indicating when the value was deleted. - * @param requestingUser the user making the request. - * @return the IRI of the value that was marked as deleted. - */ - private def deleteValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - deleteComment: Option[String], - deleteDate: Option[Instant], - currentValue: ReadValueV2, - requestingUser: UserADM): Future[IRI] = { + * Deletes a value (either an ordinary value or a link), using an existing transaction, assuming that + * pre-update checks have already been done. + * + * @param dataNamedGraph the named graph in which the value is to be deleted. + * @param resourceInfo information about the the resource in which to create the value. + * @param propertyIri the IRI of the property that points from the resource to the value. + * @param currentValue the value to be deleted. + * @param deleteComment an optional comment explaining why the value is being deleted. + * @param deleteDate an optional timestamp indicating when the value was deleted. + * @param requestingUser the user making the request. + * @return the IRI of the value that was marked as deleted. + */ + private def deleteValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + propertyIri: SmartIri, + deleteComment: Option[String], + deleteDate: Option[Instant], + currentValue: ReadValueV2, + requestingUser: UserADM + ): Future[IRI] = currentValue.valueContent match { case _: LinkValueContentV2 => deleteLinkValueV2AfterChecks( @@ -1730,27 +1808,28 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = requestingUser ) } - } /** - * Deletes a link after checks. - * - * @param dataNamedGraph the named graph in which the value is to be deleted. - * @param resourceInfo information about the the resource in which to create the value. - * @param propertyIri the IRI of the property that points from the resource to the value. - * @param currentValue the value to be deleted. - * @param deleteComment an optional comment explaining why the value is being deleted. - * @param deleteDate an optional timestamp indicating when the value was deleted. - * @param requestingUser the user making the request. - * @return the IRI of the value that was marked as deleted. - */ - private def deleteLinkValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - currentValue: ReadValueV2, - deleteComment: Option[String], - deleteDate: Option[Instant], - requestingUser: UserADM): Future[IRI] = { + * Deletes a link after checks. + * + * @param dataNamedGraph the named graph in which the value is to be deleted. + * @param resourceInfo information about the the resource in which to create the value. + * @param propertyIri the IRI of the property that points from the resource to the value. + * @param currentValue the value to be deleted. + * @param deleteComment an optional comment explaining why the value is being deleted. + * @param deleteDate an optional timestamp indicating when the value was deleted. + * @param requestingUser the user making the request. + * @return the IRI of the value that was marked as deleted. + */ + private def deleteLinkValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + propertyIri: SmartIri, + currentValue: ReadValueV2, + deleteComment: Option[String], + deleteDate: Option[Instant], + requestingUser: UserADM + ): Future[IRI] = { // Make a new version of of the LinkValue with a reference count of 0, and mark the new // version as deleted. Give the new version the same permissions as the previous version. @@ -1791,24 +1870,26 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Deletes an ordinary value after checks. - * - * @param dataNamedGraph the named graph in which the value is to be deleted. - * @param resourceInfo information about the the resource in which to create the value. - * @param propertyIri the IRI of the property that points from the resource to the value. - * @param currentValue the value to be deleted. - * @param deleteComment an optional comment explaining why the value is being deleted. - * @param deleteDate an optional timestamp indicating when the value was deleted. - * @param requestingUser the user making the request. - * @return the IRI of the value that was marked as deleted. - */ - private def deleteOrdinaryValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - currentValue: ReadValueV2, - deleteComment: Option[String], - deleteDate: Option[Instant], - requestingUser: UserADM): Future[IRI] = { + * Deletes an ordinary value after checks. + * + * @param dataNamedGraph the named graph in which the value is to be deleted. + * @param resourceInfo information about the the resource in which to create the value. + * @param propertyIri the IRI of the property that points from the resource to the value. + * @param currentValue the value to be deleted. + * @param deleteComment an optional comment explaining why the value is being deleted. + * @param deleteDate an optional timestamp indicating when the value was deleted. + * @param requestingUser the user making the request. + * @return the IRI of the value that was marked as deleted. + */ + private def deleteOrdinaryValueV2AfterChecks( + dataNamedGraph: IRI, + resourceInfo: ReadResourceV2, + propertyIri: SmartIri, + currentValue: ReadValueV2, + deleteComment: Option[String], + deleteDate: Option[Instant], + requestingUser: UserADM + ): Future[IRI] = { // Mark the existing version of the value as deleted. // If it's a TextValue, make SparqlTemplateLinkUpdates for updating LinkValues representing @@ -1858,19 +1939,21 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * When a property IRI is submitted for an update, makes an adjusted version of the submitted property: - * if it's a link value property, substitutes the corresponding link property, whose objects we will need to query. - * - * @param submittedPropertyIri the submitted property IRI, in the API v2 complex schema. - * @param maybeSubmittedValueType the submitted value type, if provided, in the API v2 complex schema. - * @param propertyInfoForSubmittedProperty ontology information about the submitted property, in the internal schema. - * @param requestingUser the requesting user. - * @return ontology information about the adjusted property. - */ - private def getAdjustedInternalPropertyInfo(submittedPropertyIri: SmartIri, - maybeSubmittedValueType: Option[SmartIri], - propertyInfoForSubmittedProperty: ReadPropertyInfoV2, - requestingUser: UserADM): Future[ReadPropertyInfoV2] = { + * When a property IRI is submitted for an update, makes an adjusted version of the submitted property: + * if it's a link value property, substitutes the corresponding link property, whose objects we will need to query. + * + * @param submittedPropertyIri the submitted property IRI, in the API v2 complex schema. + * @param maybeSubmittedValueType the submitted value type, if provided, in the API v2 complex schema. + * @param propertyInfoForSubmittedProperty ontology information about the submitted property, in the internal schema. + * @param requestingUser the requesting user. + * @return ontology information about the adjusted property. + */ + private def getAdjustedInternalPropertyInfo( + submittedPropertyIri: SmartIri, + maybeSubmittedValueType: Option[SmartIri], + propertyInfoForSubmittedProperty: ReadPropertyInfoV2, + requestingUser: UserADM + ): Future[ReadPropertyInfoV2] = { val submittedInternalPropertyIri: SmartIri = submittedPropertyIri.toOntologySchema(InternalSchema) if (propertyInfoForSubmittedProperty.isLinkValueProp) { @@ -1879,7 +1962,9 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde if (submittedValueType.toString != OntologyConstants.KnoraApiV2Complex.LinkValue) { FastFuture.failed( BadRequestException( - s"A value of type <$submittedValueType> cannot be an object of property <$submittedPropertyIri>")) + s"A value of type <$submittedValueType> cannot be an object of property <$submittedPropertyIri>" + ) + ) } case None => () @@ -1899,23 +1984,26 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } yield linkPropertyInfoResponse.properties(internalLinkPropertyIri) } else if (propertyInfoForSubmittedProperty.isLinkProp) { throw BadRequestException( - s"Invalid property for creating a link value (submit a link value property instead): $submittedPropertyIri") + s"Invalid property for creating a link value (submit a link value property instead): $submittedPropertyIri" + ) } else { FastFuture.successful(propertyInfoForSubmittedProperty) } } /** - * Given a set of resource IRIs, checks that they point to Knora resources. - * If not, throws an exception. - * - * @param targetResourceIris the IRIs to be checked. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ - private def checkResourceIris(targetResourceIris: Set[IRI], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Unit] = { + * Given a set of resource IRIs, checks that they point to Knora resources. + * If not, throws an exception. + * + * @param targetResourceIris the IRIs to be checked. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ + private def checkResourceIris( + targetResourceIris: Set[IRI], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Unit] = if (targetResourceIris.isEmpty) { FastFuture.successful(()) } else { @@ -1934,43 +2022,49 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ <- (responderManager ? resourcePreviewRequest).mapTo[ReadResourcesSequenceV2] } yield () } - } /** - * Returns a resource's metadata and its values, if any, for the specified property. If the property is a link property, the result - * will contain any objects of the corresponding link value property (link values), as well as metadata for any resources that the link property points to. - * If the property's object type is `knora-base:TextValue`, the result will contain any objects of the property (text values), as well metadata - * for any resources that are objects of `knora-base:hasStandoffLinkTo`. - * - * @param resourceIri the resource IRI. - * @param propertyInfo the property definition (in the internal schema). If the caller wants to query a link, this must be the link property, - * not the link value property. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[ReadResourceV2]] containing only the resource's metadata and its values for the specified property. - */ - private def getResourceWithPropertyValues(resourceIri: IRI, - propertyInfo: ReadPropertyInfoV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ReadResourceV2] = { + * Returns a resource's metadata and its values, if any, for the specified property. If the property is a link property, the result + * will contain any objects of the corresponding link value property (link values), as well as metadata for any resources that the link property points to. + * If the property's object type is `knora-base:TextValue`, the result will contain any objects of the property (text values), as well metadata + * for any resources that are objects of `knora-base:hasStandoffLinkTo`. + * + * @param resourceIri the resource IRI. + * @param propertyInfo the property definition (in the internal schema). If the caller wants to query a link, this must be the link property, + * not the link value property. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[ReadResourceV2]] containing only the resource's metadata and its values for the specified property. + */ + private def getResourceWithPropertyValues( + resourceIri: IRI, + propertyInfo: ReadPropertyInfoV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ReadResourceV2] = for { // Get the property's object class constraint. objectClassConstraint: SmartIri <- Future( propertyInfo.entityInfoContent.requireIriObject( OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, throw InconsistentRepositoryDataException( - s"Property ${propertyInfo.entityInfoContent.propertyIri} has no knora-base:objectClassConstraint") - )) + s"Property ${propertyInfo.entityInfoContent.propertyIri} has no knora-base:objectClassConstraint" + ) + ) + ) // If the property points to a text value, also query the resource's standoff links. - maybeStandoffLinkToPropertyIri: Option[SmartIri] = if (objectClassConstraint.toString == OntologyConstants.KnoraBase.TextValue) { - Some(OntologyConstants.KnoraBase.HasStandoffLinkTo.toSmartIri) - } else { - None - } + maybeStandoffLinkToPropertyIri: Option[SmartIri] = + if (objectClassConstraint.toString == OntologyConstants.KnoraBase.TextValue) { + Some(OntologyConstants.KnoraBase.HasStandoffLinkTo.toSmartIri) + } else { + None + } // Convert the property IRIs to be queried to the API v2 complex schema for Gravsearch. - propertyIrisForGravsearchQuery: Seq[SmartIri] = (Seq(propertyInfo.entityInfoContent.propertyIri) ++ maybeStandoffLinkToPropertyIri) + propertyIrisForGravsearchQuery: Seq[SmartIri] = (Seq( + propertyInfo.entityInfoContent.propertyIri + ) ++ maybeStandoffLinkToPropertyIri) .map(_.toOntologySchema(ApiV2Complex)) // Make a Gravsearch query from a template. @@ -1992,23 +2086,24 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser = requestingUser )).mapTo[ReadResourcesSequenceV2] } yield searchResponse.toResource(resourceIri) - } /** - * Verifies that a value was written correctly to the triplestore. - * - * @param resourceIri the IRI of the resource that the value belongs to. - * @param propertyIri the internal IRI of the property that points to the value. If the value is a link value, - * this is the link value property. - * @param unverifiedValue the value that should have been written to the triplestore. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ - private def verifyValue(resourceIri: IRI, - propertyIri: SmartIri, - unverifiedValue: UnverifiedValueV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[VerifiedValueV2] = { + * Verifies that a value was written correctly to the triplestore. + * + * @param resourceIri the IRI of the resource that the value belongs to. + * @param propertyIri the internal IRI of the property that points to the value. If the value is a link value, + * this is the link value property. + * @param unverifiedValue the value that should have been written to the triplestore. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ + private def verifyValue( + resourceIri: IRI, + propertyIri: SmartIri, + unverifiedValue: UnverifiedValueV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[VerifiedValueV2] = { val verifiedValueFuture: Future[VerifiedValueV2] = for { resourcesRequest <- Future { ResourcesGetRequestV2( @@ -2030,9 +2125,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde .find(_.valueIri == unverifiedValue.newValueIri) .getOrElse(throw UpdateNotPerformedException()) - _ = if (!(unverifiedValue.valueContent.wouldDuplicateCurrentVersion(valueInTriplestore.valueContent) && - valueInTriplestore.permissions == unverifiedValue.permissions && - valueInTriplestore.attachedToUser == requestingUser.id)) { + _ = if ( + !(unverifiedValue.valueContent.wouldDuplicateCurrentVersion(valueInTriplestore.valueContent) && + valueInTriplestore.permissions == unverifiedValue.permissions && + valueInTriplestore.attachedToUser == requestingUser.id) + ) { /* import org.knora.webapi.util.MessageUtil println("==============================") @@ -2044,36 +2141,36 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde println(MessageUtil.toSource(valueInTriplestore.valueContent)) */ throw AssertionException( - s"The value saved as ${unverifiedValue.newValueIri} is not the same as the one that was submitted") + s"The value saved as ${unverifiedValue.newValueIri} is not the same as the one that was submitted" + ) } - } yield - VerifiedValueV2( - newValueIri = unverifiedValue.newValueIri, - value = unverifiedValue.valueContent, - permissions = unverifiedValue.permissions - ) + } yield VerifiedValueV2( + newValueIri = unverifiedValue.newValueIri, + value = unverifiedValue.valueContent, + permissions = unverifiedValue.permissions + ) - verifiedValueFuture.recover { - case _: NotFoundException => - throw UpdateNotPerformedException( - s"Resource <$resourceIri> was not found. Please report this as a possible bug.") + verifiedValueFuture.recover { case _: NotFoundException => + throw UpdateNotPerformedException(s"Resource <$resourceIri> was not found. Please report this as a possible bug.") } } /** - * Checks that a link value points to a resource with the correct type for the link property's object class constraint. - * - * @param linkPropertyIri the IRI of the link property. - * @param objectClassConstraint the object class constraint of the link property. - * @param linkValueContent the link value. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ - private def checkLinkPropertyObjectClassConstraint(linkPropertyIri: SmartIri, - objectClassConstraint: SmartIri, - linkValueContent: LinkValueContentV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Unit] = { + * Checks that a link value points to a resource with the correct type for the link property's object class constraint. + * + * @param linkPropertyIri the IRI of the link property. + * @param objectClassConstraint the object class constraint of the link property. + * @param linkValueContent the link value. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ + private def checkLinkPropertyObjectClassConstraint( + linkPropertyIri: SmartIri, + objectClassConstraint: SmartIri, + linkValueContent: LinkValueContentV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Unit] = for { // Get a preview of the target resource, because we only need to find out its class and whether the user has permission to view it. resourcePreviewRequest <- FastFuture.successful( @@ -2103,23 +2200,25 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // If it isn't, throw an exception. _ = if (!subClassResponse.isSubClass) { throw OntologyConstraintException( - s"Resource <${linkValueContent.referredResourceIri}> cannot be the target of property <$linkPropertyIri>, because it is not a member of class <$objectClassConstraint>") + s"Resource <${linkValueContent.referredResourceIri}> cannot be the target of property <$linkPropertyIri>, because it is not a member of class <$objectClassConstraint>" + ) } } yield () - } /** - * Checks that a non-link value has the correct type for a property's object class constraint. - * - * @param propertyIri the IRI of the property that should point to the value. - * @param objectClassConstraint the property's object class constraint. - * @param valueContent the value. - * @param requestingUser the user making the request. - */ - private def checkNonLinkPropertyObjectClassConstraint(propertyIri: SmartIri, - objectClassConstraint: SmartIri, - valueContent: ValueContentV2, - requestingUser: UserADM): Future[Unit] = { + * Checks that a non-link value has the correct type for a property's object class constraint. + * + * @param propertyIri the IRI of the property that should point to the value. + * @param objectClassConstraint the property's object class constraint. + * @param valueContent the value. + * @param requestingUser the user making the request. + */ + private def checkNonLinkPropertyObjectClassConstraint( + propertyIri: SmartIri, + objectClassConstraint: SmartIri, + valueContent: ValueContentV2, + requestingUser: UserADM + ): Future[Unit] = // Is the value type the same as the property's object class constraint? if (objectClassConstraint == valueContent.valueType) { // Yes. Nothing more to do here. @@ -2132,40 +2231,45 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde subClassIri = valueContent.valueType, superClassIri = objectClassConstraint, requestingUser = requestingUser - )) + ) + ) subClassResponse <- (responderManager ? subClassRequest).mapTo[CheckSubClassResponseV2] // If it isn't, throw an exception. _ = if (!subClassResponse.isSubClass) { throw OntologyConstraintException( - s"A value of type <${valueContent.valueType}> cannot be the target of property <$propertyIri>, because it is not a member of class <$objectClassConstraint>") + s"A value of type <${valueContent.valueType}> cannot be the target of property <$propertyIri>, because it is not a member of class <$objectClassConstraint>" + ) } } yield () } - } /** - * Checks that a value to be updated has the correct type for the `knora-base:objectClassConstraint` of - * the property that is supposed to point to it. - * - * @param propertyInfo the property whose object class constraint is to be checked. If the value is a link value, this is the link property. - * @param valueContent the value to be updated. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - */ - private def checkPropertyObjectClassConstraint(propertyInfo: ReadPropertyInfoV2, - valueContent: ValueContentV2, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[Unit] = { + * Checks that a value to be updated has the correct type for the `knora-base:objectClassConstraint` of + * the property that is supposed to point to it. + * + * @param propertyInfo the property whose object class constraint is to be checked. If the value is a link value, this is the link property. + * @param valueContent the value to be updated. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + */ + private def checkPropertyObjectClassConstraint( + propertyInfo: ReadPropertyInfoV2, + valueContent: ValueContentV2, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[Unit] = for { objectClassConstraint: SmartIri <- Future( propertyInfo.entityInfoContent.requireIriObject( OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, throw InconsistentRepositoryDataException( - s"Property ${propertyInfo.entityInfoContent.propertyIri} has no knora-base:objectClassConstraint") - )) + s"Property ${propertyInfo.entityInfoContent.propertyIri} has no knora-base:objectClassConstraint" + ) + ) + ) result: Unit <- valueContent match { case linkValueContent: LinkValueContentV2 => @@ -2174,7 +2278,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Check that the property whose object class constraint is to be checked is actually a link property. if (!propertyInfo.isLinkProp) { throw BadRequestException( - s"Property <${propertyInfo.entityInfoContent.propertyIri.toOntologySchema(ApiV2Complex)}> is not a link property") + s"Property <${propertyInfo.entityInfoContent.propertyIri.toOntologySchema(ApiV2Complex)}> is not a link property" + ) } // Check that the user has permission to view the target resource, and that the target resource has the correct type. @@ -2197,20 +2302,21 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } } yield result - } /** - * Given a [[ReadResourceV2]], finds a link that uses the specified property and points to the specified target - * resource. - * - * @param sourceResourceInfo a [[ReadResourceV2]] describing the source of the link. - * @param linkPropertyIri the IRI of the link property. - * @param targetResourceIri the IRI of the target resource. - * @return a [[ReadLinkValueV2]] describing the link value, if found. - */ - private def findLinkValue(sourceResourceInfo: ReadResourceV2, - linkPropertyIri: SmartIri, - targetResourceIri: IRI): Option[ReadLinkValueV2] = { + * Given a [[ReadResourceV2]], finds a link that uses the specified property and points to the specified target + * resource. + * + * @param sourceResourceInfo a [[ReadResourceV2]] describing the source of the link. + * @param linkPropertyIri the IRI of the link property. + * @param targetResourceIri the IRI of the target resource. + * @return a [[ReadLinkValueV2]] describing the link value, if found. + */ + private def findLinkValue( + sourceResourceInfo: ReadResourceV2, + linkPropertyIri: SmartIri, + targetResourceIri: IRI + ): Option[ReadLinkValueV2] = { val linkValueProperty = linkPropertyIri.fromLinkPropToLinkValueProp sourceResourceInfo.values.get(linkValueProperty).flatMap { linkValueInfos: Seq[ReadValueV2] => @@ -2222,35 +2328,37 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to create a `LinkValue` or to - * increment the reference count of an existing `LinkValue`. This happens in two cases: - * - * - When the user creates a link. In this case, neither the link nor the `LinkValue` exist yet. The - * [[SparqlTemplateLinkUpdate]] will specify that the link should be created, and that the `LinkValue` should be - * created with a reference count of 1. - * - When a text value is updated so that its standoff markup refers to a resource that it did not previously - * refer to. Here there are two possibilities: - * - If there is currently a `knora-base:hasStandoffLinkTo` link between the source and target resources, with a - * corresponding `LinkValue`, a new version of the `LinkValue` will be made, with an incremented reference count. - * - If that link and `LinkValue` don't yet exist, they will be created, and the `LinkValue` will be given - * a reference count of 1. - * - * @param sourceResourceInfo information about the source resource. - * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. - * @param targetResourceIri the IRI of the target resource. - * @param customNewLinkValueIri the optional custom IRI supplied for the link value. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. - */ - private def incrementLinkValue(sourceResourceInfo: ReadResourceV2, - linkPropertyIri: SmartIri, - targetResourceIri: IRI, - customNewLinkValueIri: Option[SmartIri] = None, - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[SparqlTemplateLinkUpdate] = { + * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to create a `LinkValue` or to + * increment the reference count of an existing `LinkValue`. This happens in two cases: + * + * - When the user creates a link. In this case, neither the link nor the `LinkValue` exist yet. The + * [[SparqlTemplateLinkUpdate]] will specify that the link should be created, and that the `LinkValue` should be + * created with a reference count of 1. + * - When a text value is updated so that its standoff markup refers to a resource that it did not previously + * refer to. Here there are two possibilities: + * - If there is currently a `knora-base:hasStandoffLinkTo` link between the source and target resources, with a + * corresponding `LinkValue`, a new version of the `LinkValue` will be made, with an incremented reference count. + * - If that link and `LinkValue` don't yet exist, they will be created, and the `LinkValue` will be given + * a reference count of 1. + * + * @param sourceResourceInfo information about the source resource. + * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. + * @param targetResourceIri the IRI of the target resource. + * @param customNewLinkValueIri the optional custom IRI supplied for the link value. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. + */ + private def incrementLinkValue( + sourceResourceInfo: ReadResourceV2, + linkPropertyIri: SmartIri, + targetResourceIri: IRI, + customNewLinkValueIri: Option[SmartIri] = None, + valueCreator: IRI, + valuePermissions: String, + requestingUser: UserADM + ): Future[SparqlTemplateLinkUpdate] = { // Check whether a LinkValue already exists for this link. val maybeLinkValueInfo: Option[ReadLinkValueV2] = findLinkValue( sourceResourceInfo = sourceResourceInfo, @@ -2260,8 +2368,10 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde for { // Make an IRI for the new LinkValue. - newLinkValueIri: IRI <- checkOrCreateEntityIri(customNewLinkValueIri, - stringFormatter.makeRandomValueIri(sourceResourceInfo.resourceIri)) + newLinkValueIri: IRI <- checkOrCreateEntityIri( + customNewLinkValueIri, + stringFormatter.makeRandomValueIri(sourceResourceInfo.resourceIri) + ) linkUpdate = maybeLinkValueInfo match { case Some(linkValueInfo) => @@ -2304,31 +2414,33 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to decrement the reference count - * of a `LinkValue`. This happens in two cases: - * - * - When the user deletes (or changes) a user-created link. In this case, the current reference count will be 1. - * The existing link will be removed. A new version of the `LinkValue` be made with a reference count of 0, and - * will be marked as deleted. - * - When a resource reference is removed from standoff markup on a text value, so that the text value no longer - * contains any references to that target resource. In this case, a new version of the `LinkValue` will be - * made, with a decremented reference count. If the new reference count is 0, the link will be removed and the - * `LinkValue` will be marked as deleted. - * - * @param sourceResourceInfo information about the source resource. - * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. - * @param targetResourceIri the IRI of the target resource. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. - */ - private def decrementLinkValue(sourceResourceInfo: ReadResourceV2, - linkPropertyIri: SmartIri, - targetResourceIri: IRI, - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[SparqlTemplateLinkUpdate] = { + * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to decrement the reference count + * of a `LinkValue`. This happens in two cases: + * + * - When the user deletes (or changes) a user-created link. In this case, the current reference count will be 1. + * The existing link will be removed. A new version of the `LinkValue` be made with a reference count of 0, and + * will be marked as deleted. + * - When a resource reference is removed from standoff markup on a text value, so that the text value no longer + * contains any references to that target resource. In this case, a new version of the `LinkValue` will be + * made, with a decremented reference count. If the new reference count is 0, the link will be removed and the + * `LinkValue` will be marked as deleted. + * + * @param sourceResourceInfo information about the source resource. + * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. + * @param targetResourceIri the IRI of the target resource. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. + */ + private def decrementLinkValue( + sourceResourceInfo: ReadResourceV2, + linkPropertyIri: SmartIri, + targetResourceIri: IRI, + valueCreator: IRI, + valuePermissions: String, + requestingUser: UserADM + ): Future[SparqlTemplateLinkUpdate] = { // Check whether a LinkValue already exists for this link. val maybeLinkValueInfo = findLinkValue( @@ -2352,49 +2464,51 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde for { // Generate an IRI for the new LinkValue. newLinkValueIri: IRI <- makeUnusedValueIri(sourceResourceInfo.resourceIri) - } yield - SparqlTemplateLinkUpdate( - linkPropertyIri = linkPropertyIri, - directLinkExists = true, - insertDirectLink = false, - deleteDirectLink = deleteDirectLink, - linkValueExists = true, - linkTargetExists = true, - newLinkValueIri = newLinkValueIri, - linkTargetIri = targetResourceIri, - currentReferenceCount = linkValueInfo.valueHasRefCount, - newReferenceCount = newReferenceCount, - newLinkValueCreator = valueCreator, - newLinkValuePermissions = valuePermissions - ) + } yield SparqlTemplateLinkUpdate( + linkPropertyIri = linkPropertyIri, + directLinkExists = true, + insertDirectLink = false, + deleteDirectLink = deleteDirectLink, + linkValueExists = true, + linkTargetExists = true, + newLinkValueIri = newLinkValueIri, + linkTargetIri = targetResourceIri, + currentReferenceCount = linkValueInfo.valueHasRefCount, + newReferenceCount = newReferenceCount, + newLinkValueCreator = valueCreator, + newLinkValuePermissions = valuePermissions + ) case None => // We didn't find the LinkValue. This shouldn't happen. throw InconsistentRepositoryDataException( - s"There should be a knora-base:LinkValue describing a direct link from resource <${sourceResourceInfo.resourceIri}> to resource <$targetResourceIri> using property <$linkPropertyIri>, but it seems to be missing") + s"There should be a knora-base:LinkValue describing a direct link from resource <${sourceResourceInfo.resourceIri}> to resource <$targetResourceIri> using property <$linkPropertyIri>, but it seems to be missing" + ) } } /** - * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to change the metadata - * on a `LinkValue`. - * - * @param sourceResourceInfo information about the source resource. - * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. - * @param targetResourceIri the IRI of the target resource. - * @param customNewLinkValueIri the optional custom IRI supplied for the link value. - * @param valueCreator the IRI of the new link value's owner. - * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. - * @param requestingUser the user making the request. - * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. - */ - private def changeLinkValueMetadata(sourceResourceInfo: ReadResourceV2, - linkPropertyIri: SmartIri, - targetResourceIri: IRI, - customNewLinkValueIri: Option[SmartIri] = None, - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[SparqlTemplateLinkUpdate] = { + * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to change the metadata + * on a `LinkValue`. + * + * @param sourceResourceInfo information about the source resource. + * @param linkPropertyIri the IRI of the property that links the source resource to the target resource. + * @param targetResourceIri the IRI of the target resource. + * @param customNewLinkValueIri the optional custom IRI supplied for the link value. + * @param valueCreator the IRI of the new link value's owner. + * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate. + * @param requestingUser the user making the request. + * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template. + */ + private def changeLinkValueMetadata( + sourceResourceInfo: ReadResourceV2, + linkPropertyIri: SmartIri, + targetResourceIri: IRI, + customNewLinkValueIri: Option[SmartIri] = None, + valueCreator: IRI, + valuePermissions: String, + requestingUser: UserADM + ): Future[SparqlTemplateLinkUpdate] = { // Check whether a LinkValue already exists for this link. val maybeLinkValueInfo: Option[ReadLinkValueV2] = findLinkValue( @@ -2412,34 +2526,35 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // If no custom IRI was provided, generate an IRI for the new LinkValue. newLinkValueIri: IRI <- checkOrCreateEntityIri( customNewLinkValueIri, - stringFormatter.makeRandomValueIri(sourceResourceInfo.resourceIri)) - - } yield - SparqlTemplateLinkUpdate( - linkPropertyIri = linkPropertyIri, - directLinkExists = true, - insertDirectLink = false, - deleteDirectLink = false, - linkValueExists = true, - linkTargetExists = true, - newLinkValueIri = newLinkValueIri, - linkTargetIri = targetResourceIri, - currentReferenceCount = linkValueInfo.valueHasRefCount, - newReferenceCount = linkValueInfo.valueHasRefCount, - newLinkValueCreator = valueCreator, - newLinkValuePermissions = valuePermissions + stringFormatter.makeRandomValueIri(sourceResourceInfo.resourceIri) ) + } yield SparqlTemplateLinkUpdate( + linkPropertyIri = linkPropertyIri, + directLinkExists = true, + insertDirectLink = false, + deleteDirectLink = false, + linkValueExists = true, + linkTargetExists = true, + newLinkValueIri = newLinkValueIri, + linkTargetIri = targetResourceIri, + currentReferenceCount = linkValueInfo.valueHasRefCount, + newReferenceCount = linkValueInfo.valueHasRefCount, + newLinkValueCreator = valueCreator, + newLinkValuePermissions = valuePermissions + ) + case None => // We didn't find the LinkValue. This shouldn't happen. throw InconsistentRepositoryDataException( - s"There should be a knora-base:LinkValue describing a direct link from resource <${sourceResourceInfo.resourceIri}> to resource <$targetResourceIri> using property <$linkPropertyIri>, but it seems to be missing") + s"There should be a knora-base:LinkValue describing a direct link from resource <${sourceResourceInfo.resourceIri}> to resource <$targetResourceIri> using property <$linkPropertyIri>, but it seems to be missing" + ) } } /** - * The permissions that are granted by every `knora-base:LinkValue` describing a standoff link. - */ + * The permissions that are granted by every `knora-base:LinkValue` describing a standoff link. + */ lazy val standoffLinkValuePermissions: String = { val permissions: Set[PermissionADM] = Set( PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.SystemUser), @@ -2450,34 +2565,38 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } /** - * A convenience method for generating an unused random value IRI. - * - * @param resourceIri the IRI of the containing resource. - * @return the new value IRI. - */ - private def makeUnusedValueIri(resourceIri: IRI): Future[IRI] = { + * A convenience method for generating an unused random value IRI. + * + * @param resourceIri the IRI of the containing resource. + * @return the new value IRI. + */ + private def makeUnusedValueIri(resourceIri: IRI): Future[IRI] = stringFormatter.makeUnusedIri(stringFormatter.makeRandomValueIri(resourceIri), storeManager, loggingAdapter) - } /** - * Make a new value UUID considering optional custom value UUID and custom value IRI. - * If a custom UUID is given, this method checks that it matches the ending of a given IRI, if there was any. - * If no custom UUID is given for a value, it checks if a custom value IRI is given or not. If yes, it extracts the - * UUID from the given IRI. If no custom value IRI was given, it generates a random UUID. - * - * @param maybeCustomIri the optional value IRI. - * @param maybeCustomUUID the optional value UUID. - * @return the new value UUID. - */ - private def makeNewValueUUID(maybeCustomIri: Option[SmartIri], maybeCustomUUID: Option[UUID]): UUID = { + * Make a new value UUID considering optional custom value UUID and custom value IRI. + * If a custom UUID is given, this method checks that it matches the ending of a given IRI, if there was any. + * If no custom UUID is given for a value, it checks if a custom value IRI is given or not. If yes, it extracts the + * UUID from the given IRI. If no custom value IRI was given, it generates a random UUID. + * + * @param maybeCustomIri the optional value IRI. + * @param maybeCustomUUID the optional value UUID. + * @return the new value UUID. + */ + private def makeNewValueUUID(maybeCustomIri: Option[SmartIri], maybeCustomUUID: Option[UUID]): UUID = // Is there any custom value UUID given? maybeCustomUUID match { case Some(customValueUUID) => // Yes. Check that if a custom IRI is given, it ends with the same UUID - if (maybeCustomIri.nonEmpty && stringFormatter.base64DecodeUuid(maybeCustomIri.get.toString.split("/").last) != customValueUUID) { + if ( + maybeCustomIri.nonEmpty && stringFormatter.base64DecodeUuid( + maybeCustomIri.get.toString.split("/").last + ) != customValueUUID + ) { throw BadRequestException( s" Given custom IRI ${maybeCustomIri.get} should contain the given custom UUID ${stringFormatter - .base64EncodeUuid(customValueUUID)}.") + .base64EncodeUuid(customValueUUID)}." + ) } customValueUUID case None => @@ -2490,5 +2609,4 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde case None => UUID.randomUUID } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala index 0195d5aaff..7a614c60a6 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cache.scala @@ -77,44 +77,48 @@ object Cache extends LazyLogging { val ONTOLOGY_CACHE_LOCK_IRI = "http://rdfh.ch/ontologies" /** - * The in-memory cache of ontologies. - * - * @param ontologies a map of ontology IRIs to ontologies. - * @param subClassOfRelations a map of subclasses to their base classes. - * @param superClassOfRelations a map of base classes to their subclasses. - * @param subPropertyOfRelations a map of subproperties to their base proeprties. - * @param guiAttributeDefinitions a map of salsah-gui:Guielement individuals to their GUI attribute definitions. - * @param standoffProperties a set of standoff properties. - */ - case class OntologyCacheData(ontologies: Map[SmartIri, ReadOntologyV2], - subClassOfRelations: Map[SmartIri, Seq[SmartIri]], - superClassOfRelations: Map[SmartIri, Set[SmartIri]], - subPropertyOfRelations: Map[SmartIri, Set[SmartIri]], - guiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], - standoffProperties: Set[SmartIri]) { + * The in-memory cache of ontologies. + * + * @param ontologies a map of ontology IRIs to ontologies. + * @param subClassOfRelations a map of subclasses to their base classes. + * @param superClassOfRelations a map of base classes to their subclasses. + * @param subPropertyOfRelations a map of subproperties to their base proeprties. + * @param guiAttributeDefinitions a map of salsah-gui:Guielement individuals to their GUI attribute definitions. + * @param standoffProperties a set of standoff properties. + */ + case class OntologyCacheData( + ontologies: Map[SmartIri, ReadOntologyV2], + subClassOfRelations: Map[SmartIri, Seq[SmartIri]], + superClassOfRelations: Map[SmartIri, Set[SmartIri]], + subPropertyOfRelations: Map[SmartIri, Set[SmartIri]], + guiAttributeDefinitions: Map[SmartIri, Set[SalsahGuiAttributeDefinition]], + standoffProperties: Set[SmartIri] + ) { lazy val allPropertyDefs: Map[SmartIri, PropertyInfoContentV2] = ontologies.values - .flatMap(_.properties.map { - case (propertyIri, readPropertyInfo) => propertyIri -> readPropertyInfo.entityInfoContent + .flatMap(_.properties.map { case (propertyIri, readPropertyInfo) => + propertyIri -> readPropertyInfo.entityInfoContent }) .toMap } /** - * Loads and caches all ontology information. - * - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return a [[SuccessResponseV2]]. - */ - def loadOntologies(settings: KnoraSettingsImpl, - storeManager: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM)(implicit ec: ExecutionContext, - stringFormat: StringFormatter, - timeout: Timeout): Future[SuccessResponseV2] = { + * Loads and caches all ontology information. + * + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return a [[SuccessResponseV2]]. + */ + def loadOntologies( + settings: KnoraSettingsImpl, + storeManager: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + )(implicit ec: ExecutionContext, stringFormat: StringFormatter, timeout: Timeout): Future[SuccessResponseV2] = { val loadOntologiesFuture: Future[SuccessResponseV2] = for { _ <- Future { - if (!(requestingUser.id == KnoraSystemInstances.Users.SystemUser.id || requestingUser.permissions.isSystemAdmin)) { + if ( + !(requestingUser.id == KnoraSystemInstances.Users.SystemUser.id || requestingUser.permissions.isSystemAdmin) + ) { throw ForbiddenException(s"Only a system administrator can reload ontologies") } } @@ -123,22 +127,28 @@ object Cache extends LazyLogging { allOntologyMetadataSparql <- FastFuture.successful( org.knora.webapi.messages.twirl.queries.sparql.v2.txt .getAllOntologyMetadata(triplestore = settings.triplestoreType) - .toString()) + .toString() + ) allOntologyMetadataResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(allOntologyMetadataSparql)) .mapTo[SparqlSelectResult] allOntologyMetadata: Map[SmartIri, OntologyMetadataV2] = OntologyHelpers.buildOntologyMetadata( - allOntologyMetadataResponse) + allOntologyMetadataResponse + ) knoraBaseOntologyMetadata: OntologyMetadataV2 = allOntologyMetadata.getOrElse( OntologyConstants.KnoraBase.KnoraBaseOntologyIri.toSmartIri, - throw InconsistentRepositoryDataException(s"No knora-base ontology found")) + throw InconsistentRepositoryDataException(s"No knora-base ontology found") + ) knoraBaseOntologyVersion: String = knoraBaseOntologyMetadata.ontologyVersion.getOrElse( throw InconsistentRepositoryDataException( - "The knora-base ontology in the repository is not up to date. See the Knora documentation on repository updates.")) + "The knora-base ontology in the repository is not up to date. See the Knora documentation on repository updates." + ) + ) _ = if (knoraBaseOntologyVersion != KnoraBaseVersion) { throw InconsistentRepositoryDataException( - s"The knora-base ontology in the repository has version '$knoraBaseOntologyVersion', but this version of Knora requires '$KnoraBaseVersion'. See the Knora documentation on repository updates.") + s"The knora-base ontology in the repository has version '$knoraBaseOntologyVersion', but this version of Knora requires '$KnoraBaseVersion'. See the Knora documentation on repository updates." + ) } // Get the contents of each named graph containing an ontology. @@ -163,27 +173,29 @@ object Cache extends LazyLogging { _ = makeOntologyCache(allOntologyMetadata, ontologyGraphs) } yield SuccessResponseV2("Ontologies loaded.") - loadOntologiesFuture.recover { - case exception: Throwable => - exception match { - case inconsistentRepositoryDataException: InconsistentRepositoryDataException => - log.error(inconsistentRepositoryDataException.message) - SuccessResponseV2( - s"An error occurred when loading ontologies: ${inconsistentRepositoryDataException.message}") + loadOntologiesFuture.recover { case exception: Throwable => + exception match { + case inconsistentRepositoryDataException: InconsistentRepositoryDataException => + log.error(inconsistentRepositoryDataException.message) + SuccessResponseV2( + s"An error occurred when loading ontologies: ${inconsistentRepositoryDataException.message}" + ) - case other => throw other - } + case other => throw other + } } } /** - * Given ontology metdata and ontology graphs read from the triplestore, constructs the ontology cache. - * - * @param allOntologyMetadata a map of ontology IRIs to ontology metadata. - * @param ontologyGraphs a list of ontology graphs. - */ - def makeOntologyCache(allOntologyMetadata: Map[SmartIri, OntologyMetadataV2], - ontologyGraphs: Iterable[OntologyGraph])(implicit stringFormatter: StringFormatter): Unit = { + * Given ontology metdata and ontology graphs read from the triplestore, constructs the ontology cache. + * + * @param allOntologyMetadata a map of ontology IRIs to ontology metadata. + * @param ontologyGraphs a list of ontology graphs. + */ + def makeOntologyCache( + allOntologyMetadata: Map[SmartIri, OntologyMetadataV2], + ontologyGraphs: Iterable[OntologyGraph] + )(implicit stringFormatter: StringFormatter): Unit = { // Get the IRIs of all the entities in each ontology. // A map of ontology IRIs to class IRIs in each ontology. @@ -242,8 +254,8 @@ object Cache extends LazyLogging { // Determine relations between entities. // A map of class IRIs to their immediate base classes. - val directSubClassOfRelations: Map[SmartIri, Set[SmartIri]] = allClassDefs.map { - case (classIri, classDef) => classIri -> classDef.subClassOf + val directSubClassOfRelations: Map[SmartIri, Set[SmartIri]] = allClassDefs.map { case (classIri, classDef) => + classIri -> classDef.subClassOf } // A map of property IRIs to their immediate base properties. @@ -283,15 +295,18 @@ object Cache extends LazyLogging { // A set of all subproperties of knora-base:hasLinkTo. val allLinkProps: Set[SmartIri] = allPropertyIris.filter(prop => - allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri)) + allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkTo.toSmartIri) + ) // A set of all subproperties of knora-base:hasLinkToValue. val allLinkValueProps: Set[SmartIri] = allPropertyIris.filter(prop => - allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri)) + allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasLinkToValue.toSmartIri) + ) // A set of all subproperties of knora-base:hasFileValue. val allFileValueProps: Set[SmartIri] = allPropertyIris.filter(prop => - allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasFileValue.toSmartIri)) + allSubPropertyOfRelations(prop).contains(OntologyConstants.KnoraBase.HasFileValue.toSmartIri) + ) // A map of the cardinalities defined directly on each resource class. Each class IRI points to a map of // property IRIs to KnoraCardinalityInfo objects. @@ -350,34 +365,34 @@ object Cache extends LazyLogging { case (ontologyIri, ontologyMetadata) => ontologyIri -> ReadOntologyV2( ontologyMetadata = ontologyMetadata, - classes = readClassInfos.filter { - case (classIri, _) => classIri.getOntologyFromEntity == ontologyIri + classes = readClassInfos.filter { case (classIri, _) => + classIri.getOntologyFromEntity == ontologyIri }, - properties = readPropertyInfos.filter { - case (propertyIri, _) => propertyIri.getOntologyFromEntity == ontologyIri + properties = readPropertyInfos.filter { case (propertyIri, _) => + propertyIri.getOntologyFromEntity == ontologyIri }, - individuals = readIndividualInfos.filter { - case (individualIri, _) => individualIri.getOntologyFromEntity == ontologyIri + individuals = readIndividualInfos.filter { case (individualIri, _) => + individualIri.getOntologyFromEntity == ontologyIri }, isWholeOntology = true ) } // A set of the IRIs of all properties used in cardinalities in standoff classes. - val propertiesUsedInStandoffCardinalities: Set[SmartIri] = readClassInfos.flatMap { - case (_, readClassInfo) => - if (readClassInfo.isStandoffClass) { - readClassInfo.allCardinalities.keySet - } else { - Set.empty[SmartIri] - } + val propertiesUsedInStandoffCardinalities: Set[SmartIri] = readClassInfos.flatMap { case (_, readClassInfo) => + if (readClassInfo.isStandoffClass) { + readClassInfo.allCardinalities.keySet + } else { + Set.empty[SmartIri] + } }.toSet // A set of the IRIs of all properties whose subject class constraint is a standoff class. val propertiesWithStandoffTagSubjects: Set[SmartIri] = readPropertyInfos.flatMap { case (propertyIri, readPropertyInfo) => readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { + OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri + ) match { case Some(subjectClassConstraint: SmartIri) => readClassInfos.get(subjectClassConstraint) match { case Some(subjectReadClassInfo: ReadClassInfoV2) => @@ -396,84 +411,84 @@ object Cache extends LazyLogging { // Construct the ontology cache data. val ontologyCacheData: OntologyCacheData = OntologyCacheData( - ontologies = new ErrorHandlingMap[SmartIri, ReadOntologyV2](readOntologies, { key => - s"Ontology not found: $key" - }), - subClassOfRelations = new ErrorHandlingMap[SmartIri, Seq[SmartIri]](allSubClassOfRelations, { key => - s"Class not found: $key" - }), - superClassOfRelations = new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSuperClassOfRelations, { key => - s"Class not found: $key" - }), - subPropertyOfRelations = new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSubPropertyOfRelations, { key => - s"Property not found: $key" - }), - guiAttributeDefinitions = - new ErrorHandlingMap[SmartIri, Set[SalsahGuiAttributeDefinition]](allGuiAttributeDefinitions, { key => - s"salsah-gui:Guielement not found: $key" - }), + ontologies = new ErrorHandlingMap[SmartIri, ReadOntologyV2](readOntologies, key => s"Ontology not found: $key"), + subClassOfRelations = + new ErrorHandlingMap[SmartIri, Seq[SmartIri]](allSubClassOfRelations, key => s"Class not found: $key"), + superClassOfRelations = + new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSuperClassOfRelations, key => s"Class not found: $key"), + subPropertyOfRelations = + new ErrorHandlingMap[SmartIri, Set[SmartIri]](allSubPropertyOfRelations, key => s"Property not found: $key"), + guiAttributeDefinitions = new ErrorHandlingMap[SmartIri, Set[SalsahGuiAttributeDefinition]]( + allGuiAttributeDefinitions, + key => s"salsah-gui:Guielement not found: $key" + ), standoffProperties = propertiesUsedInStandoffCardinalities ++ propertiesWithStandoffTagSubjects ) // Check property subject and object class constraints. - readPropertyInfos.foreach { - case (propertyIri, readPropertyInfo) => - val allSuperPropertyIris: Set[SmartIri] = allSubPropertyOfRelations.getOrElse(propertyIri, Set.empty[SmartIri]) - - readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri) match { - case Some(subjectClassConstraint) => - // Each property's subject class constraint, if provided, must be a subclass of the subject class constraints of all its base properties. - checkPropertyConstraint( - cacheData = ontologyCacheData, - internalPropertyIri = propertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, - constraintValueToBeChecked = subjectClassConstraint, - allSuperPropertyIris = allSuperPropertyIris, - errorSchema = InternalSchema, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) + readPropertyInfos.foreach { case (propertyIri, readPropertyInfo) => + val allSuperPropertyIris: Set[SmartIri] = allSubPropertyOfRelations.getOrElse(propertyIri, Set.empty[SmartIri]) + + readPropertyInfo.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri + ) match { + case Some(subjectClassConstraint) => + // Each property's subject class constraint, if provided, must be a subclass of the subject class constraints of all its base properties. + checkPropertyConstraint( + cacheData = ontologyCacheData, + internalPropertyIri = propertyIri, + constraintPredicateIri = OntologyConstants.KnoraBase.SubjectClassConstraint.toSmartIri, + constraintValueToBeChecked = subjectClassConstraint, + allSuperPropertyIris = allSuperPropertyIris, + errorSchema = InternalSchema, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) + } + ) - // If the property is defined in a project-specific ontology, its subject class constraint, if provided, must be a Knora resource or standoff class. - if (!propertyIri.isKnoraBuiltInDefinitionIri) { - val baseClassesOfSubjectClassConstraint = allSubClassOfRelations(subjectClassConstraint) + // If the property is defined in a project-specific ontology, its subject class constraint, if provided, must be a Knora resource or standoff class. + if (!propertyIri.isKnoraBuiltInDefinitionIri) { + val baseClassesOfSubjectClassConstraint = allSubClassOfRelations(subjectClassConstraint) - if (!(baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) || - baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri))) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri is defined in a project-specific ontology, but its knora-base:subjectClassConstraint, $subjectClassConstraint, is not a subclass of knora-base:Resource or knora-base:StandoffTag") - } + if ( + !(baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) || + baseClassesOfSubjectClassConstraint.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri)) + ) { + throw InconsistentRepositoryDataException( + s"Property $propertyIri is defined in a project-specific ontology, but its knora-base:subjectClassConstraint, $subjectClassConstraint, is not a subclass of knora-base:Resource or knora-base:StandoffTag" + ) } + } - case None => () - } - - readPropertyInfo.entityInfoContent.getPredicateIriObject( - OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri) match { - case Some(objectClassConstraint) => - // Each property's object class constraint, if provided, must be a subclass of the object class constraints of all its base properties. - checkPropertyConstraint( - cacheData = ontologyCacheData, - internalPropertyIri = propertyIri, - constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, - constraintValueToBeChecked = objectClassConstraint, - allSuperPropertyIris = allSuperPropertyIris, - errorSchema = InternalSchema, - errorFun = { msg: String => - throw InconsistentRepositoryDataException(msg) - } - ) + case None => () + } - case None => - // A resource property must have an object class constraint, unless it's knora-base:resourceProperty. - if (readPropertyInfo.isResourceProp && propertyIri != OntologyConstants.KnoraBase.ResourceProperty.toSmartIri) { - throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint") + readPropertyInfo.entityInfoContent.getPredicateIriObject( + OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri + ) match { + case Some(objectClassConstraint) => + // Each property's object class constraint, if provided, must be a subclass of the object class constraints of all its base properties. + checkPropertyConstraint( + cacheData = ontologyCacheData, + internalPropertyIri = propertyIri, + constraintPredicateIri = OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, + constraintValueToBeChecked = objectClassConstraint, + allSuperPropertyIris = allSuperPropertyIris, + errorSchema = InternalSchema, + errorFun = { msg: String => + throw InconsistentRepositoryDataException(msg) } - } + ) + + case None => + // A resource property must have an object class constraint, unless it's knora-base:resourceProperty. + if ( + readPropertyInfo.isResourceProp && propertyIri != OntologyConstants.KnoraBase.ResourceProperty.toSmartIri + ) { + throw InconsistentRepositoryDataException(s"Property $propertyIri has no knora-base:objectClassConstraint") + } + } } // Check references between ontologies. @@ -484,23 +499,25 @@ object Cache extends LazyLogging { } /** - * Checks that a property's `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint` is compatible with (i.e. a subclass of) - * the ones in all its base properties. - * - * @param internalPropertyIri the internal IRI of the property to be checked. - * @param constraintPredicateIri the internal IRI of the constraint, i.e. `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint`. - * @param constraintValueToBeChecked the constraint value to be checked. - * @param allSuperPropertyIris the IRIs of all the base properties of the property, including indirect base properties and the property itself. - * @param errorSchema the ontology schema to be used for error messages. - * @param errorFun a function that throws an exception. It will be called with an error message argument if the property constraint is invalid. - */ - def checkPropertyConstraint(cacheData: OntologyCacheData, - internalPropertyIri: SmartIri, - constraintPredicateIri: SmartIri, - constraintValueToBeChecked: SmartIri, - allSuperPropertyIris: Set[SmartIri], - errorSchema: OntologySchema, - errorFun: String => Nothing): Unit = { + * Checks that a property's `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint` is compatible with (i.e. a subclass of) + * the ones in all its base properties. + * + * @param internalPropertyIri the internal IRI of the property to be checked. + * @param constraintPredicateIri the internal IRI of the constraint, i.e. `knora-base:subjectClassConstraint` or `knora-base:objectClassConstraint`. + * @param constraintValueToBeChecked the constraint value to be checked. + * @param allSuperPropertyIris the IRIs of all the base properties of the property, including indirect base properties and the property itself. + * @param errorSchema the ontology schema to be used for error messages. + * @param errorFun a function that throws an exception. It will be called with an error message argument if the property constraint is invalid. + */ + def checkPropertyConstraint( + cacheData: OntologyCacheData, + internalPropertyIri: SmartIri, + constraintPredicateIri: SmartIri, + constraintValueToBeChecked: SmartIri, + allSuperPropertyIris: Set[SmartIri], + errorSchema: OntologySchema, + errorFun: String => Nothing + ): Unit = { // The property constraint value must be a Knora class, or one of a limited set of classes defined in OWL. val superClassesOfConstraintValueToBeChecked: Set[SmartIri] = if (OntologyConstants.Owl.ClassesThatCanBeKnoraClassConstraints.contains(constraintValueToBeChecked.toString)) { @@ -512,7 +529,8 @@ object Cache extends LazyLogging { errorFun( s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri .toOntologySchema(errorSchema)} of " + - s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}") + s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}" + ) ) .toSet } @@ -526,7 +544,8 @@ object Cache extends LazyLogging { .getOrElse( superPropertyIri, errorFun( - s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} is a subproperty of $superPropertyIri, which is undefined") + s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} is a subproperty of $superPropertyIri, which is undefined" + ) ) } @@ -535,8 +554,13 @@ object Cache extends LazyLogging { val superPropertyConstraintValues: Map[SmartIri, SmartIri] = superPropertyInfos.flatMap { superPropertyInfo => superPropertyInfo.entityInfoContent.predicates .get(constraintPredicateIri) - .map(_.requireIriObject(throw InconsistentRepositoryDataException( - s"Property ${superPropertyInfo.entityInfoContent.propertyIri} has an invalid object for $constraintPredicateIri"))) + .map( + _.requireIriObject( + throw InconsistentRepositoryDataException( + s"Property ${superPropertyInfo.entityInfoContent.propertyIri} has an invalid object for $constraintPredicateIri" + ) + ) + ) .map { superPropertyConstraintValue => superPropertyInfo.entityInfoContent.propertyIri -> superPropertyConstraintValue } @@ -544,33 +568,35 @@ object Cache extends LazyLogging { // Check that the constraint value to be checked is a subclass of the constraint value in every superproperty. - superPropertyConstraintValues.foreach { - case (superPropertyIri, superPropertyConstraintValue) => - if (!superClassesOfConstraintValueToBeChecked.contains(superPropertyConstraintValue)) { - errorFun( - s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri + superPropertyConstraintValues.foreach { case (superPropertyIri, superPropertyConstraintValue) => + if (!superClassesOfConstraintValueToBeChecked.contains(superPropertyConstraintValue)) { + errorFun( + s"Property ${internalPropertyIri.toOntologySchema(errorSchema)} cannot have a ${constraintPredicateIri + .toOntologySchema(errorSchema)} of " + + s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}, because that is not a subclass of " + + s"${superPropertyConstraintValue.toOntologySchema(errorSchema)}, which is the ${constraintPredicateIri .toOntologySchema(errorSchema)} of " + - s"${constraintValueToBeChecked.toOntologySchema(errorSchema)}, because that is not a subclass of " + - s"${superPropertyConstraintValue.toOntologySchema(errorSchema)}, which is the ${constraintPredicateIri - .toOntologySchema(errorSchema)} of " + - s"a base property, ${superPropertyIri.toOntologySchema(errorSchema)}") - } + s"a base property, ${superPropertyIri.toOntologySchema(errorSchema)}" + ) + } } } /** - * Checks a reference between an ontology entity and another ontology entity to see if the target - * is in a non-shared ontology in another project. - * - * @param ontologyCacheData the ontology cache data. - * @param sourceEntityIri the entity whose definition contains the reference. - * @param targetEntityIri the entity that's the target of the reference. - * @param errorFun a function that throws an exception with the specified message if the reference is invalid. - */ - private def checkOntologyReferenceInEntity(ontologyCacheData: OntologyCacheData, - sourceEntityIri: SmartIri, - targetEntityIri: SmartIri, - errorFun: String => Nothing): Unit = { + * Checks a reference between an ontology entity and another ontology entity to see if the target + * is in a non-shared ontology in another project. + * + * @param ontologyCacheData the ontology cache data. + * @param sourceEntityIri the entity whose definition contains the reference. + * @param targetEntityIri the entity that's the target of the reference. + * @param errorFun a function that throws an exception with the specified message if the reference is invalid. + */ + private def checkOntologyReferenceInEntity( + ontologyCacheData: OntologyCacheData, + sourceEntityIri: SmartIri, + targetEntityIri: SmartIri, + errorFun: String => Nothing + ): Unit = if (targetEntityIri.isKnoraDefinitionIri) { val sourceOntologyIri = sourceEntityIri.getOntologyFromEntity val sourceOntologyMetadata = ontologyCacheData.ontologies(sourceOntologyIri).ontologyMetadata @@ -581,23 +607,24 @@ object Cache extends LazyLogging { if (sourceOntologyMetadata.projectIri != targetOntologyMetadata.projectIri) { if (!(targetOntologyIri.isKnoraBuiltInDefinitionIri || targetOntologyIri.isKnoraSharedDefinitionIri)) { errorFun( - s"Entity $sourceEntityIri refers to entity $targetEntityIri, which is in a non-shared ontology that belongs to another project") + s"Entity $sourceEntityIri refers to entity $targetEntityIri, which is in a non-shared ontology that belongs to another project" + ) } } } - } /** - * Checks a property definition to ensure that it doesn't refer to any other non-shared ontologies. - * - * @param ontologyCacheData the ontology cache data. - * @param propertyDef the property definition. - * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. - */ + * Checks a property definition to ensure that it doesn't refer to any other non-shared ontologies. + * + * @param ontologyCacheData the ontology cache data. + * @param propertyDef the property definition. + * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. + */ def checkOntologyReferencesInPropertyDef( - ontologyCacheData: OntologyCacheData, - propertyDef: PropertyInfoContentV2, - errorFun: String => Nothing)(implicit stringFormatter: StringFormatter): Unit = { + ontologyCacheData: OntologyCacheData, + propertyDef: PropertyInfoContentV2, + errorFun: String => Nothing + )(implicit stringFormatter: StringFormatter): Unit = { // Ensure that the property isn't a subproperty of any property in a non-shared ontology in another project. for (subPropertyOf <- propertyDef.subPropertyOf) { @@ -637,15 +664,17 @@ object Cache extends LazyLogging { } /** - * Checks a class definition to ensure that it doesn't refer to any non-shared ontologies in other projects. - * - * @param ontologyCacheData the ontology cache data. - * @param classDef the class definition. - * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. - */ - def checkOntologyReferencesInClassDef(ontologyCacheData: OntologyCacheData, - classDef: ClassInfoContentV2, - errorFun: String => Nothing): Unit = { + * Checks a class definition to ensure that it doesn't refer to any non-shared ontologies in other projects. + * + * @param ontologyCacheData the ontology cache data. + * @param classDef the class definition. + * @param errorFun a function that throws an exception with the specified message if the property definition is invalid. + */ + def checkOntologyReferencesInClassDef( + ontologyCacheData: OntologyCacheData, + classDef: ClassInfoContentV2, + errorFun: String => Nothing + ): Unit = { for (subClassOf <- classDef.subClassOf) { checkOntologyReferenceInEntity( ontologyCacheData = ontologyCacheData, @@ -666,12 +695,13 @@ object Cache extends LazyLogging { } /** - * Checks references between ontologies to ensure that they do not refer to non-shared ontologies in other projects. - * - * @param ontologyCacheData the ontology cache data. - */ - private def checkReferencesBetweenOntologies(ontologyCacheData: OntologyCacheData)( - implicit stringFormatter: StringFormatter): Unit = { + * Checks references between ontologies to ensure that they do not refer to non-shared ontologies in other projects. + * + * @param ontologyCacheData the ontology cache data. + */ + private def checkReferencesBetweenOntologies( + ontologyCacheData: OntologyCacheData + )(implicit stringFormatter: StringFormatter): Unit = for (ontology <- ontologyCacheData.ontologies.values) { for (propertyInfo <- ontology.properties.values) { checkOntologyReferencesInPropertyDef( @@ -693,41 +723,39 @@ object Cache extends LazyLogging { ) } } - } /** - * Updates the ontology cache. - * - * @param cacheData the updated data to be cached. - */ - def storeCacheData(cacheData: OntologyCacheData): Unit = { + * Updates the ontology cache. + * + * @param cacheData the updated data to be cached. + */ + def storeCacheData(cacheData: OntologyCacheData): Unit = CacheUtil.put(cacheName = OntologyCacheName, key = OntologyCacheKey, value = cacheData) - } /** - * Gets the ontology data from the cache. - * - * @return an [[OntologyCacheData]] - */ - def getCacheData(implicit ec: ExecutionContext): Future[OntologyCacheData] = { + * Gets the ontology data from the cache. + * + * @return an [[OntologyCacheData]] + */ + def getCacheData(implicit ec: ExecutionContext): Future[OntologyCacheData] = Future { CacheUtil.get[OntologyCacheData](cacheName = OntologyCacheName, key = OntologyCacheKey) match { case Some(data) => data case None => throw ApplicationCacheException( - s"The Knora API server has not loaded any ontologies, perhaps because of an invalid ontology") + s"The Knora API server has not loaded any ontologies, perhaps because of an invalid ontology" + ) } } - } /** - * Given the IRI of a base class, updates inherited cardinalities in subclasses. - * - * @param baseClassIri the internal IRI of the base class. - * @param cacheData the ontology cache. - * - * @return the updated ontology cache. - */ + * Given the IRI of a base class, updates inherited cardinalities in subclasses. + * + * @param baseClassIri the internal IRI of the base class. + * @param cacheData the ontology cache. + * + * @return the updated ontology cache. + */ def updateSubClasses(baseClassIri: SmartIri, cacheData: OntologyCacheData): OntologyCacheData = { // Get the class definitions of all the subclasses of the base class. diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala index a066401af1..d39d476cdf 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/Cardinalities.scala @@ -67,42 +67,42 @@ object Cardinalities { )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[CanDoResponseV2] = { for { cacheData: Cache.OntologyCacheData <- Cache.getCacheData - ontology = cacheData.ontologies(internalOntologyIri) + ontology = cacheData.ontologies(internalOntologyIri) submittedClassDefinition: ClassInfoContentV2 = deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, - featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. rdfType: SmartIri = submittedClassDefinition.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that cardinalities were submitted. _ = if (submittedClassDefinition.directCardinalities.isEmpty) { - throw BadRequestException("No cardinalities specified") - } + throw BadRequestException("No cardinalities specified") + } // Check that only one cardinality was submitted. _ = if (submittedClassDefinition.directCardinalities.size > 1) { - throw BadRequestException("Only one cardinality is allowed to be submitted.") - } + throw BadRequestException("Only one cardinality is allowed to be submitted.") + } // Check that the class exists currentClassDefinition: ClassInfoContentV2 <- @@ -122,16 +122,16 @@ object Cardinalities { deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema).directCardinalities _ = cardinalitiesToDelete.foreach(p => - isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri) - ) + isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri) + ) // Check if property is used in resources submittedPropertyToDelete: SmartIri = cardinalitiesToDelete.head._1 - propertyIsUsed: Boolean <- isPropertyUsedInResources(settings, storeManager, submittedPropertyToDelete) + propertyIsUsed: Boolean <- isPropertyUsedInResources(settings, storeManager, submittedPropertyToDelete) _ = if (propertyIsUsed) { - throw BadRequestException("Property is used in data. The cardinality cannot be deleted.") - } + throw BadRequestException("Property is used in data. The cardinality cannot be deleted.") + } // Make an update class definition in which the cardinality to delete is removed @@ -164,12 +164,12 @@ object Cardinalities { // Check that the class definition doesn't refer to any non-shared ontologies in other projects. _ = Cache.checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = newInternalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) } yield CanDoResponseV2(true) } @@ -194,41 +194,41 @@ object Cardinalities { )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[ReadOntologyV2] = for { cacheData: Cache.OntologyCacheData <- Cache.getCacheData - ontology = cacheData.ontologies(internalOntologyIri) + ontology = cacheData.ontologies(internalOntologyIri) submittedClassDefinition: ClassInfoContentV2 = deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema) // Check that the ontology exists and has not been updated by another user since the client last read it. _ <- OntologyHelpers.checkOntologyLastModificationDateBeforeUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, - featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) // Check that the class's rdf:type is owl:Class. rdfType: SmartIri = submittedClassDefinition.requireIriObject( - OntologyConstants.Rdf.Type.toSmartIri, - throw BadRequestException(s"No rdf:type specified") - ) + OntologyConstants.Rdf.Type.toSmartIri, + throw BadRequestException(s"No rdf:type specified") + ) _ = if (rdfType != OntologyConstants.Owl.Class.toSmartIri) { - throw BadRequestException(s"Invalid rdf:type for property: $rdfType") - } + throw BadRequestException(s"Invalid rdf:type for property: $rdfType") + } // Check that cardinalities were submitted. _ = if (submittedClassDefinition.directCardinalities.isEmpty) { - throw BadRequestException("No cardinalities specified") - } + throw BadRequestException("No cardinalities specified") + } // Check that only one cardinality was submitted. _ = if (submittedClassDefinition.directCardinalities.size > 1) { - throw BadRequestException("Only one cardinality is allowed to be submitted.") - } + throw BadRequestException("Only one cardinality is allowed to be submitted.") + } // Check that the class exists currentClassDefinition: ClassInfoContentV2 <- @@ -245,16 +245,16 @@ object Cardinalities { deleteCardinalitiesFromClassRequest.classInfoContent.toOntologySchema(InternalSchema).directCardinalities _ = cardinalitiesToDelete.foreach(p => - isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri) - ) + isCardinalityDefinedOnClass(cacheData, p._1, p._2, internalClassIri, internalOntologyIri) + ) // Check if property is used in resources submittedPropertyToDelete: SmartIri = cardinalitiesToDelete.head._1 - propertyIsUsed: Boolean <- isPropertyUsedInResources(settings, storeManager, submittedPropertyToDelete) + propertyIsUsed: Boolean <- isPropertyUsedInResources(settings, storeManager, submittedPropertyToDelete) _ = if (propertyIsUsed) { - throw BadRequestException("Property is used in data. The cardinality cannot be deleted.") - } + throw BadRequestException("Property is used in data. The cardinality cannot be deleted.") + } // Make an update class definition in which the cardinality to delete is removed @@ -287,12 +287,12 @@ object Cardinalities { // Check that the class definition doesn't refer to any non-shared ontologies in other projects. _ = Cache.checkOntologyReferencesInClassDef( - ontologyCacheData = cacheData, - classDef = newInternalClassDefWithLinkValueProps, - errorFun = { msg: String => - throw BadRequestException(msg) - } - ) + ontologyCacheData = cacheData, + classDef = newInternalClassDefWithLinkValueProps, + errorFun = { msg: String => + throw BadRequestException(msg) + } + ) // Prepare to update the ontology cache. (No need to deal with SPARQL-escaping here, because there // isn't any text to escape in cardinalities.) @@ -305,93 +305,93 @@ object Cardinalities { } readClassInfo = ReadClassInfoV2( - entityInfoContent = newInternalClassDefWithLinkValueProps, - allBaseClasses = allBaseClassIris, - isResourceClass = true, - canBeInstantiated = true, - inheritedCardinalities = inheritedCardinalities, - knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) - ), - linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkProp(propertyIri, cacheData) - ), - linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isLinkValueProp(propertyIri, cacheData) - ), - fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => - OntologyHelpers.isFileValueProp(propertyIri, cacheData) - ) - ) + entityInfoContent = newInternalClassDefWithLinkValueProps, + allBaseClasses = allBaseClassIris, + isResourceClass = true, + canBeInstantiated = true, + inheritedCardinalities = inheritedCardinalities, + knoraResourceProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isKnoraResourceProperty(propertyIri, cacheData) + ), + linkProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkProp(propertyIri, cacheData) + ), + linkValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isLinkValueProp(propertyIri, cacheData) + ), + fileValueProperties = propertyIrisOfAllCardinalitiesForClass.filter(propertyIri => + OntologyHelpers.isFileValueProp(propertyIri, cacheData) + ) + ) // Add the cardinalities to the class definition in the triplestore. currentTime: Instant = Instant.now updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .replaceClassCardinalities( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = internalOntologyIri, - ontologyIri = internalOntologyIri, - classIri = internalClassIri, - newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, - lastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, - currentTime = currentTime - ) - .toString() + .replaceClassCardinalities( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = internalOntologyIri, + ontologyIri = internalOntologyIri, + classIri = internalClassIri, + newCardinalities = newInternalClassDefWithLinkValueProps.directCardinalities, + lastModificationDate = deleteCardinalitiesFromClassRequest.lastModificationDate, + currentTime = currentTime + ) + .toString() _ <- (storeManager ? SparqlUpdateRequest(updateSparql)).mapTo[SparqlUpdateResponse] // Check that the ontology's last modification date was updated. _ <- OntologyHelpers.checkOntologyLastModificationDateAfterUpdate( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - expectedLastModificationDate = currentTime, - featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + expectedLastModificationDate = currentTime, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) // Check that the data that was saved corresponds to the data that was submitted. loadedClassDef <- OntologyHelpers.loadClassDefinition( - settings, - storeManager, - classIri = internalClassIri, - featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig - ) + settings, + storeManager, + classIri = internalClassIri, + featureFactoryConfig = deleteCardinalitiesFromClassRequest.featureFactoryConfig + ) _ = if (loadedClassDef != newInternalClassDefWithLinkValueProps) { - throw InconsistentRepositoryDataException( - s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" - ) - } + throw InconsistentRepositoryDataException( + s"Attempted to save class definition $newInternalClassDefWithLinkValueProps, but $loadedClassDef was saved" + ) + } // Update subclasses and write the cache. updatedOntology = ontology.copy( - ontologyMetadata = ontology.ontologyMetadata.copy( - lastModificationDate = Some(currentTime) - ), - classes = ontology.classes + (internalClassIri -> readClassInfo) - ) + ontologyMetadata = ontology.ontologyMetadata.copy( + lastModificationDate = Some(currentTime) + ), + classes = ontology.classes + (internalClassIri -> readClassInfo) + ) _ = Cache.storeCacheData( - Cache.updateSubClasses( - baseClassIri = internalClassIri, - cacheData = cacheData.copy( - ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) - ) - ) + Cache.updateSubClasses( + baseClassIri = internalClassIri, + cacheData = cacheData.copy( + ontologies = cacheData.ontologies + (internalOntologyIri -> updatedOntology) ) + ) + ) // Read the data back from the cache. response: ReadOntologyV2 <- OntologyHelpers.getClassDefinitionsFromOntologyV2( - classIris = Set(internalClassIri), - allLanguages = true, - requestingUser = deleteCardinalitiesFromClassRequest.requestingUser - ) + classIris = Set(internalClassIri), + allLanguages = true, + requestingUser = deleteCardinalitiesFromClassRequest.requestingUser + ) } yield response @@ -413,15 +413,15 @@ object Cardinalities { )(implicit ec: ExecutionContext, timeout: Timeout): Future[Boolean] = for { request <- Future( - org.knora.webapi.queries.sparql.v2.txt - .isPropertyUsed( - triplestore = settings.triplestoreType, - internalPropertyIri = internalPropertyIri.toString, - ignoreKnoraConstraints = true, - ignoreRdfSubjectAndObject = true - ) - .toString() - ) + org.knora.webapi.queries.sparql.v2.txt + .isPropertyUsed( + triplestore = settings.triplestoreType, + internalPropertyIri = internalPropertyIri.toString, + ignoreKnoraConstraints = true, + ignoreRdfSubjectAndObject = true + ) + .toString() + ) response: SparqlAskResponse <- (storeManager ? SparqlAskRequest(request)).mapTo[SparqlAskResponse] } yield response.result @@ -443,13 +443,13 @@ object Cardinalities { )(implicit ec: ExecutionContext): Future[ClassInfoContentV2] = for { currentOntologyState: ReadOntologyV2 <- Future(cacheData.ontologies(internalOntologyIri)) currentClassDefinition = currentOntologyState.classes - .getOrElse( - internalClassIri, - throw BadRequestException( - s"Class ${submittedClassInfoContentV2.classIri} does not exist" - ) - ) - .entityInfoContent + .getOrElse( + internalClassIri, + throw BadRequestException( + s"Class ${submittedClassInfoContentV2.classIri} does not exist" + ) + ) + .entityInfoContent } yield currentClassDefinition /** diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala index 4774a01eba..6815fecd28 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ontology/OntologyHelpers.scala @@ -139,132 +139,131 @@ object OntologyHelpers { ): Future[Option[OntologyMetadataV2]] = { for { _ <- Future { - if (!internalOntologyIri.getOntologySchema.contains(InternalSchema)) { - throw AssertionException(s"Expected an internal ontology IRI: $internalOntologyIri") - } - } + if (!internalOntologyIri.getOntologySchema.contains(InternalSchema)) { + throw AssertionException(s"Expected an internal ontology IRI: $internalOntologyIri") + } + } getOntologyInfoSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getOntologyInfo( - triplestore = settings.triplestoreType, - ontologyIri = internalOntologyIri - ) - .toString() + .getOntologyInfo( + triplestore = settings.triplestoreType, + ontologyIri = internalOntologyIri + ) + .toString() getOntologyInfoResponse <- (storeManager ? SparqlConstructRequest( - sparql = getOntologyInfoSparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlConstructResponse] - - metadata: Option[OntologyMetadataV2] = if (getOntologyInfoResponse.statements.isEmpty) { - None - } else { - getOntologyInfoResponse.statements.get( - internalOntologyIri.toString - ) match { - case Some(statements: Seq[(IRI, String)]) => - val statementMap: Map[IRI, Seq[String]] = statements.groupBy { - case (pred, _) => pred - }.map { case (pred, predStatements) => - pred -> predStatements.map { case (_, obj) => - obj - } - } - - val projectIris: Seq[String] = statementMap.getOrElse( - OntologyConstants.KnoraBase.AttachedToProject, - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has no knora-base:attachedToProject" - ) - ) - val labels: Seq[String] = statementMap.getOrElse( - OntologyConstants.Rdfs.Label, - Seq.empty[String] - ) - val comments: Seq[String] = statementMap.getOrElse( - OntologyConstants.Rdfs.Comment, - Seq.empty[String] - ) - val lastModDates: Seq[String] = - statementMap.getOrElse( - OntologyConstants.KnoraBase.LastModificationDate, - Seq.empty[String] - ) - - val projectIri = if (projectIris.size > 1) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has more than one knora-base:attachedToProject" - ) - } else { - projectIris.head.toSmartIri - } - - if (!internalOntologyIri.isKnoraBuiltInDefinitionIri) { - if ( - projectIri.toString == OntologyConstants.KnoraAdmin.SystemProject - ) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri cannot be in project ${OntologyConstants.KnoraAdmin.SystemProject}" - ) - } - - if ( - internalOntologyIri.isKnoraSharedDefinitionIri && projectIri.toString != OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject - ) { - throw InconsistentRepositoryDataException( - s"Shared ontology $internalOntologyIri must be in project ${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}" - ) - } - } - - val label: String = if (labels.size > 1) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has more than one rdfs:label" - ) - } else if (labels.isEmpty) { - internalOntologyIri.getOntologyName - } else { - labels.head - } - - val comment: Option[String] = if (comments.size > 1) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has more than one rdfs:comment" - ) - } else comments.headOption - - val lastModificationDate: Option[Instant] = - if (lastModDates.size > 1) { - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has more than one ${OntologyConstants.KnoraBase.LastModificationDate}" - ) - } else if (lastModDates.isEmpty) { - None - } else { - val dateStr = lastModDates.head - Some( - stringFormatter.xsdDateTimeStampToInstant( - dateStr, - throw InconsistentRepositoryDataException( - s"Invalid ${OntologyConstants.KnoraBase.LastModificationDate}: $dateStr" - ) - ) - ) - } - - Some( - OntologyMetadataV2( - ontologyIri = internalOntologyIri, - projectIri = Some(projectIri), - label = Some(label), - comment = comment, - lastModificationDate = lastModificationDate - ) - ) - - case None => None - } - } + sparql = getOntologyInfoSparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlConstructResponse] + + metadata: Option[OntologyMetadataV2] = + if (getOntologyInfoResponse.statements.isEmpty) { + None + } else { + getOntologyInfoResponse.statements.get( + internalOntologyIri.toString + ) match { + case Some(statements: Seq[(IRI, String)]) => + val statementMap: Map[IRI, Seq[String]] = statements.groupBy { case (pred, _) => + pred + }.map { case (pred, predStatements) => + pred -> predStatements.map { case (_, obj) => + obj + } + } + + val projectIris: Seq[String] = statementMap.getOrElse( + OntologyConstants.KnoraBase.AttachedToProject, + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has no knora-base:attachedToProject" + ) + ) + val labels: Seq[String] = statementMap.getOrElse( + OntologyConstants.Rdfs.Label, + Seq.empty[String] + ) + val comments: Seq[String] = statementMap.getOrElse( + OntologyConstants.Rdfs.Comment, + Seq.empty[String] + ) + val lastModDates: Seq[String] = + statementMap.getOrElse( + OntologyConstants.KnoraBase.LastModificationDate, + Seq.empty[String] + ) + + val projectIri = if (projectIris.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one knora-base:attachedToProject" + ) + } else { + projectIris.head.toSmartIri + } + + if (!internalOntologyIri.isKnoraBuiltInDefinitionIri) { + if (projectIri.toString == OntologyConstants.KnoraAdmin.SystemProject) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri cannot be in project ${OntologyConstants.KnoraAdmin.SystemProject}" + ) + } + + if ( + internalOntologyIri.isKnoraSharedDefinitionIri && projectIri.toString != OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject + ) { + throw InconsistentRepositoryDataException( + s"Shared ontology $internalOntologyIri must be in project ${OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject}" + ) + } + } + + val label: String = if (labels.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one rdfs:label" + ) + } else if (labels.isEmpty) { + internalOntologyIri.getOntologyName + } else { + labels.head + } + + val comment: Option[String] = if (comments.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one rdfs:comment" + ) + } else comments.headOption + + val lastModificationDate: Option[Instant] = + if (lastModDates.size > 1) { + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has more than one ${OntologyConstants.KnoraBase.LastModificationDate}" + ) + } else if (lastModDates.isEmpty) { + None + } else { + val dateStr = lastModDates.head + Some( + stringFormatter.xsdDateTimeStampToInstant( + dateStr, + throw InconsistentRepositoryDataException( + s"Invalid ${OntologyConstants.KnoraBase.LastModificationDate}: $dateStr" + ) + ) + ) + } + + Some( + OntologyMetadataV2( + ontologyIri = internalOntologyIri, + projectIri = Some(projectIri), + label = Some(label), + comment = comment, + lastModificationDate = lastModificationDate + ) + ) + + case None => None + } + } } yield metadata } @@ -349,9 +348,9 @@ object OntologyHelpers { // Identify the Knora resource properties, link properties, link value properties, and file value properties in the cardinalities. val knoraResourcePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allKnoraResourceProps) - val linkPropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkProps) - val linkValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkValueProps) - val fileValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allFileValueProps) + val linkPropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkProps) + val linkValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allLinkValueProps) + val fileValuePropsInClass = allPropertyIrisForCardinalitiesInClass.filter(allFileValueProps) // Make sure there is a link value property for each link property. @@ -377,8 +376,8 @@ object OntologyHelpers { // Make sure that the cardinality for each link property is the same as the cardinality for the corresponding link value property. for (linkProp <- linkPropsInClass) { - val linkValueProp: SmartIri = linkProp.fromLinkPropToLinkValueProp - val linkPropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkProp) + val linkValueProp: SmartIri = linkProp.fromLinkPropToLinkValueProp + val linkPropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkProp) val linkValuePropCardinality: KnoraCardinalityInfo = allOwlCardinalitiesForClass(linkValueProp) if (!linkPropCardinality.equalsWithoutGuiOrder(linkValuePropCardinality)) { @@ -393,8 +392,8 @@ object OntologyHelpers { val directCardinalityPropertyIris = directCardinalities.keySet val allBaseClasses: Seq[SmartIri] = allSubClassOfRelations(classIri) - val isKnoraResourceClass = allBaseClasses.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) - val isStandoffClass = allBaseClasses.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri) + val isKnoraResourceClass = allBaseClasses.contains(OntologyConstants.KnoraBase.Resource.toSmartIri) + val isStandoffClass = allBaseClasses.contains(OntologyConstants.KnoraBase.StandoffTag.toSmartIri) val isValueClass = !(isKnoraResourceClass || isStandoffClass) && allBaseClasses.contains( OntologyConstants.KnoraBase.Value.toSmartIri ) @@ -705,7 +704,7 @@ object OntologyHelpers { val isResourceProp = allKnoraResourceProps.contains(propertyIri) val isValueProp = allSubPropertyOfRelations(propertyIri).contains(OntologyConstants.KnoraBase.HasValue.toSmartIri) - val isLinkProp = allLinkProps.contains(propertyIri) + val isLinkProp = allLinkProps.contains(propertyIri) val isLinkValueProp = allLinkValueProps.contains(propertyIri) val isFileValueProp = allFileValueProps.contains(propertyIri) @@ -834,7 +833,7 @@ object OntologyHelpers { errorFun: String => Nothing )(implicit stringFormatter: StringFormatter): Unit = { val propertyIri = propertyInfoContent.propertyIri - val predicates = propertyInfoContent.predicates + val predicates = propertyInfoContent.predicates // Find out which salsah-gui:Guielement the property uses, if any. val maybeGuiElementPred: Option[PredicateInfoV2] = @@ -890,8 +889,8 @@ object OntologyHelpers { } // Check that all required GUI attributes are provided. - val requiredAttributeNames = guiAttributeDefs.filter(_.isRequired).map(_.attributeName) - val providedAttributeNames = guiAttributes.map(_.attributeName) + val requiredAttributeNames = guiAttributeDefs.filter(_.isRequired).map(_.attributeName) + val providedAttributeNames = guiAttributes.map(_.attributeName) val missingAttributeNames: Set[String] = requiredAttributeNames -- providedAttributeNames if (missingAttributeNames.nonEmpty) { @@ -1083,22 +1082,22 @@ object OntologyHelpers { ): Future[Set[IRI]] = for { isOntologyUsedSparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .isOntologyUsed( - triplestore = settings.triplestoreType, - ontologyNamedGraphIri = ontology.ontologyMetadata.ontologyIri, - classIris = ontology.classes.keySet, - propertyIris = ontology.properties.keySet - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .isOntologyUsed( + triplestore = settings.triplestoreType, + ontologyNamedGraphIri = ontology.ontologyMetadata.ontologyIri, + classIris = ontology.classes.keySet, + propertyIris = ontology.properties.keySet + ) + .toString() + ) isOntologyUsedResponse: SparqlSelectResult <- (storeManager ? SparqlSelectRequest(isOntologyUsedSparql)) - .mapTo[SparqlSelectResult] + .mapTo[SparqlSelectResult] subjects = isOntologyUsedResponse.results.bindings.map { row => - row.rowMap("s") - }.toSet + row.rowMap("s") + }.toSet } yield subjects /** @@ -1118,9 +1117,9 @@ object OntologyHelpers { _ <- checkExternalOntologyIriForUpdate(externalOntologyIri) _ <- checkExternalEntityIriForUpdate(externalEntityIri = externalEntityIri) _ <- checkPermissionsForOntologyUpdate( - internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema), - requestingUser = requestingUser - ) + internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema), + requestingUser = requestingUser + ) } yield () /** @@ -1138,18 +1137,18 @@ object OntologyHelpers { )(implicit ex: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[PropertyInfoContentV2] = for { sparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getPropertyDefinition( - triplestore = settings.triplestoreType, - propertyIri = propertyIri - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getPropertyDefinition( + triplestore = settings.triplestoreType, + propertyIri = propertyIri + ) + .toString() + ) constructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] + sparql = sparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] } yield constructResponseToPropertyDefinition( propertyIri = propertyIri, constructResponse = constructResponse @@ -1332,109 +1331,109 @@ object OntologyHelpers { nonKnoraEntities = (classIris ++ propertyIris).filter(!_.isKnoraEntityIri) _ = if (nonKnoraEntities.nonEmpty) { - throw BadRequestException( - s"Some requested entities are not Knora entities: ${nonKnoraEntities.mkString(", ")}" - ) - } + throw BadRequestException( + s"Some requested entities are not Knora entities: ${nonKnoraEntities.mkString(", ")}" + ) + } // See if any of the requested entities are unavailable in the requested schema. classesUnavailableInSchema: Set[SmartIri] = classIris.foldLeft(Set.empty[SmartIri]) { case (acc, classIri) => - // Is this class IRI hard-coded in the requested schema? - if ( - KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd - .contains(classIri) || - KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd - .contains(classIri) - ) { - // Yes, so it's available. - acc - } else { - // No. Is it among the classes removed from the internal ontology in the requested schema? - classIri.getOntologySchema.get match { - case apiV2Schema: ApiV2Schema => - val internalClassIri = - classIri.toOntologySchema(InternalSchema) - val knoraBaseClassesToRemove = OntologyTransformationRules - .getTransformationRules( - classIri.getOntologyFromEntity, - apiV2Schema - ) - .internalClassesToRemove - - if (knoraBaseClassesToRemove.contains(internalClassIri)) { - // Yes. Include it in the set of unavailable classes. - acc + classIri - } else { - // No. It's available. - acc - } - - case InternalSchema => acc - } - } - } + // Is this class IRI hard-coded in the requested schema? + if ( + KnoraBaseToApiV2SimpleTransformationRules.externalClassesToAdd + .contains(classIri) || + KnoraBaseToApiV2ComplexTransformationRules.externalClassesToAdd + .contains(classIri) + ) { + // Yes, so it's available. + acc + } else { + // No. Is it among the classes removed from the internal ontology in the requested schema? + classIri.getOntologySchema.get match { + case apiV2Schema: ApiV2Schema => + val internalClassIri = + classIri.toOntologySchema(InternalSchema) + val knoraBaseClassesToRemove = OntologyTransformationRules + .getTransformationRules( + classIri.getOntologyFromEntity, + apiV2Schema + ) + .internalClassesToRemove + + if (knoraBaseClassesToRemove.contains(internalClassIri)) { + // Yes. Include it in the set of unavailable classes. + acc + classIri + } else { + // No. It's available. + acc + } + + case InternalSchema => acc + } + } + } propertiesUnavailableInSchema: Set[SmartIri] = propertyIris.foldLeft(Set.empty[SmartIri]) { - case (acc, propertyIri) => - // Is this property IRI hard-coded in the requested schema? - if ( - KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd - .contains(propertyIri) || - KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd - .contains(propertyIri) - ) { - // Yes, so it's available. - acc - } else { - // No. See if it's available in the requested schema. - propertyIri.getOntologySchema.get match { - case apiV2Schema: ApiV2Schema => - val internalPropertyIri = - propertyIri.toOntologySchema(InternalSchema) - - // If it's a link value property and it's requested in the simple schema, it's unavailable. - if ( - apiV2Schema == ApiV2Simple && OntologyHelpers - .isLinkValueProp(internalPropertyIri, cacheData) - ) { - acc + propertyIri - } else { - // Is it among the properties removed from the internal ontology in the requested schema? - - val knoraBasePropertiesToRemove = - OntologyTransformationRules - .getTransformationRules( - propertyIri.getOntologyFromEntity, - apiV2Schema - ) - .internalPropertiesToRemove - - if ( - knoraBasePropertiesToRemove.contains( - internalPropertyIri - ) - ) { - // Yes. Include it in the set of unavailable properties. - acc + propertyIri - } else { - // No. It's available. - acc - } - } - - case InternalSchema => acc - } - } - } + case (acc, propertyIri) => + // Is this property IRI hard-coded in the requested schema? + if ( + KnoraBaseToApiV2SimpleTransformationRules.externalPropertiesToAdd + .contains(propertyIri) || + KnoraBaseToApiV2ComplexTransformationRules.externalPropertiesToAdd + .contains(propertyIri) + ) { + // Yes, so it's available. + acc + } else { + // No. See if it's available in the requested schema. + propertyIri.getOntologySchema.get match { + case apiV2Schema: ApiV2Schema => + val internalPropertyIri = + propertyIri.toOntologySchema(InternalSchema) + + // If it's a link value property and it's requested in the simple schema, it's unavailable. + if ( + apiV2Schema == ApiV2Simple && OntologyHelpers + .isLinkValueProp(internalPropertyIri, cacheData) + ) { + acc + propertyIri + } else { + // Is it among the properties removed from the internal ontology in the requested schema? + + val knoraBasePropertiesToRemove = + OntologyTransformationRules + .getTransformationRules( + propertyIri.getOntologyFromEntity, + apiV2Schema + ) + .internalPropertiesToRemove + + if ( + knoraBasePropertiesToRemove.contains( + internalPropertyIri + ) + ) { + // Yes. Include it in the set of unavailable properties. + acc + propertyIri + } else { + // No. It's available. + acc + } + } + + case InternalSchema => acc + } + } + } entitiesUnavailableInSchema = classesUnavailableInSchema ++ propertiesUnavailableInSchema _ = if (entitiesUnavailableInSchema.nonEmpty) { - throw NotFoundException( - s"Some requested entities were not found: ${entitiesUnavailableInSchema.mkString(", ")}" - ) - } + throw NotFoundException( + s"Some requested entities were not found: ${entitiesUnavailableInSchema.mkString(", ")}" + ) + } // See if any of the requested entities are hard-coded for knora-api. @@ -1461,15 +1460,15 @@ object OntologyHelpers { .map(externalIri => externalIri.toOntologySchema(InternalSchema) -> externalIri) .toMap - classIrisForCache = internalToExternalClassIris.keySet + classIrisForCache = internalToExternalClassIris.keySet propertyIrisForCache = internalToExternalPropertyIris.keySet // Get the entities that are available in the ontology cache. classOntologiesForCache: Iterable[ReadOntologyV2] = cacheData.ontologies.view - .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) - .toMap - .values + .filterKeys(classIrisForCache.map(_.getOntologyFromEntity)) + .toMap + .values propertyOntologiesForCache: Iterable[ReadOntologyV2] = cacheData.ontologies.view .filterKeys(propertyIrisForCache.map(_.getOntologyFromEntity)) @@ -1477,16 +1476,16 @@ object OntologyHelpers { .values classesAvailableFromCache: Map[SmartIri, ReadClassInfoV2] = classOntologiesForCache.flatMap { ontology => - ontology.classes.view - .filterKeys(classIrisForCache) - .toMap - }.toMap + ontology.classes.view + .filterKeys(classIrisForCache) + .toMap + }.toMap propertiesAvailableFromCache: Map[SmartIri, ReadPropertyInfoV2] = propertyOntologiesForCache.flatMap { ontology => - ontology.properties.view - .filterKeys(propertyIrisForCache) - .toMap - }.toMap + ontology.properties.view + .filterKeys(propertyIrisForCache) + .toMap + }.toMap allClassesAvailable: Map[SmartIri, ReadClassInfoV2] = classesAvailableFromCache ++ hardCodedExternalClassesAvailable @@ -1496,34 +1495,34 @@ object OntologyHelpers { // See if any entities are missing. allExternalClassIrisAvailable: Set[SmartIri] = allClassesAvailable.keySet.map { classIri => - if (classIri.getOntologySchema.contains(InternalSchema)) { - internalToExternalClassIris(classIri) - } else { - classIri - } - } + if (classIri.getOntologySchema.contains(InternalSchema)) { + internalToExternalClassIris(classIri) + } else { + classIri + } + } allExternalPropertyIrisAvailable = allPropertiesAvailable.keySet.map { propertyIri => - if (propertyIri.getOntologySchema.contains(InternalSchema)) { - internalToExternalPropertyIris(propertyIri) - } else { - propertyIri - } - } - - missingClasses = classIris -- allExternalClassIrisAvailable + if (propertyIri.getOntologySchema.contains(InternalSchema)) { + internalToExternalPropertyIris(propertyIri) + } else { + propertyIri + } + } + + missingClasses = classIris -- allExternalClassIrisAvailable missingProperties = propertyIris -- allExternalPropertyIrisAvailable missingEntities = missingClasses ++ missingProperties _ = if (missingEntities.nonEmpty) { - throw NotFoundException(s"Some requested entities were not found: ${missingEntities.mkString(", ")}") - } + throw NotFoundException(s"Some requested entities were not found: ${missingEntities.mkString(", ")}") + } response = EntityInfoGetResponseV2( - classInfoMap = new ErrorHandlingMap(allClassesAvailable, key => s"Resource class $key not found"), - propertyInfoMap = new ErrorHandlingMap(allPropertiesAvailable, key => s"Property $key not found") - ) + classInfoMap = new ErrorHandlingMap(allClassesAvailable, key => s"Resource class $key not found"), + propertyInfoMap = new ErrorHandlingMap(allPropertiesAvailable, key => s"Property $key not found") + ) } yield response } @@ -1545,21 +1544,22 @@ object OntologyHelpers { ontologyIris = classIris.map(_.getOntologyFromEntity) _ = if (ontologyIris.size != 1) { - throw BadRequestException(s"Only one ontology may be queried per request") - } + throw BadRequestException(s"Only one ontology may be queried per request") + } classInfoResponse: EntityInfoGetResponseV2 <- getEntityInfoResponseV2(classIris = classIris, requestingUser = requestingUser) internalOntologyIri = ontologyIris.head.toOntologySchema(InternalSchema) // Are we returning data in the user's preferred language, or in all available languages? - userLang = if (!allLanguages) { - // Just the user's preferred language. - Some(requestingUser.lang) - } else { - // All available languages. - None - } + userLang = + if (!allLanguages) { + // Just the user's preferred language. + Some(requestingUser.lang) + } else { + // All available languages. + None + } } yield ReadOntologyV2( ontologyMetadata = cacheData.ontologies(internalOntologyIri).ontologyMetadata, classes = classInfoResponse.classInfoMap, @@ -1581,18 +1581,18 @@ object OntologyHelpers { )(implicit ex: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[ClassInfoContentV2] = for { sparql <- Future( - org.knora.webapi.messages.twirl.queries.sparql.v2.txt - .getClassDefinition( - triplestore = settings.triplestoreType, - classIri = classIri - ) - .toString() - ) + org.knora.webapi.messages.twirl.queries.sparql.v2.txt + .getClassDefinition( + triplestore = settings.triplestoreType, + classIri = classIri + ) + .toString() + ) constructResponse <- (storeManager ? SparqlExtendedConstructRequest( - sparql = sparql, - featureFactoryConfig = featureFactoryConfig - )).mapTo[SparqlExtendedConstructResponse] + sparql = sparql, + featureFactoryConfig = featureFactoryConfig + )).mapTo[SparqlExtendedConstructResponse] } yield constructResponseToClassDefinition( classIri = classIri, constructResponse = constructResponse @@ -1830,31 +1830,31 @@ object OntologyHelpers { )(implicit ec: ExecutionContext, stringFormatter: StringFormatter, timeout: Timeout): Future[Unit] = for { existingOntologyMetadata: Option[OntologyMetadataV2] <- loadOntologyMetadata( - settings, - storeManager, - internalOntologyIri = internalOntologyIri, - featureFactoryConfig = featureFactoryConfig - ) + settings, + storeManager, + internalOntologyIri = internalOntologyIri, + featureFactoryConfig = featureFactoryConfig + ) _ = existingOntologyMetadata match { - case Some(metadata) => - metadata.lastModificationDate match { - case Some(lastModificationDate) => - if (lastModificationDate != expectedLastModificationDate) { - errorFun - } - - case None => - throw InconsistentRepositoryDataException( - s"Ontology $internalOntologyIri has no ${OntologyConstants.KnoraBase.LastModificationDate}" - ) + case Some(metadata) => + metadata.lastModificationDate match { + case Some(lastModificationDate) => + if (lastModificationDate != expectedLastModificationDate) { + errorFun } case None => - throw NotFoundException( - s"Ontology $internalOntologyIri (corresponding to ${internalOntologyIri.toOntologySchema(ApiV2Complex)}) not found" + throw InconsistentRepositoryDataException( + s"Ontology $internalOntologyIri has no ${OntologyConstants.KnoraBase.LastModificationDate}" ) } + + case None => + throw NotFoundException( + s"Ontology $internalOntologyIri (corresponding to ${internalOntologyIri.toOntologySchema(ApiV2Complex)}) not found" + ) + } } yield () /** @@ -1905,11 +1905,11 @@ object OntologyHelpers { .get _ = if ( - !requestingUser.permissions.isProjectAdmin(projectIri.toString) && !requestingUser.permissions.isSystemAdmin - ) { - // not a project or system admin - throw ForbiddenException("Ontologies can be modified only by a project or system admin.") - } + !requestingUser.permissions.isProjectAdmin(projectIri.toString) && !requestingUser.permissions.isSystemAdmin + ) { + // not a project or system admin + throw ForbiddenException("Ontologies can be modified only by a project or system admin.") + } } yield projectIri diff --git a/webapi/src/main/scala/org/knora/webapi/routing/AroundDirectives.scala b/webapi/src/main/scala/org/knora/webapi/routing/AroundDirectives.scala index 38bfd1e7fa..3f40d3941f 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/AroundDirectives.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/AroundDirectives.scala @@ -24,19 +24,20 @@ import akka.http.scaladsl.server.Directives._ import org.knora.webapi.instrumentation.InstrumentationSupport /** - * Akka HTTP directives which can be wrapped around a [[akka.http.scaladsl.server.Route]]]. - */ + * Akka HTTP directives which can be wrapped around a [[akka.http.scaladsl.server.Route]]]. + */ trait AroundDirectives extends InstrumentationSupport { /** - * When wrapped around a [[akka.http.scaladsl.server.Route]], logs the time it took for the route to run. - */ + * When wrapped around a [[akka.http.scaladsl.server.Route]], logs the time it took for the route to run. + */ def logDuration: Directive0 = extractRequestContext.flatMap { ctx => val start = System.currentTimeMillis() mapResponse { resp => val took = System.currentTimeMillis() - start metricsLogger.info( - s"[${resp.status.intValue()}] ${ctx.request.method.name} " + s"${ctx.request.uri} took: ${took}ms") + s"[${resp.status.intValue()}] ${ctx.request.method.name} " + s"${ctx.request.uri} took: ${took}ms" + ) resp } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala b/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala index 290d333f0a..037f48f1b7 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/Authenticator.scala @@ -48,10 +48,10 @@ import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success, Try} /** - * This trait is used in routes that need authentication support. It provides methods that use the [[RequestContext]] - * to extract credentials, authenticate provided credentials, and look up cached credentials through the use of the - * session id. All private methods used in this trait can be found in the companion object. - */ + * This trait is used in routes that need authentication support. It provides methods that use the [[RequestContext]] + * to extract credentials, authenticate provided credentials, and look up cached credentials through the use of the + * session id. All private methods used in this trait can be found in the companion object. + */ trait Authenticator { // Import companion object @@ -63,41 +63,51 @@ trait Authenticator { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Checks if the credentials provided in [[RequestContext]] are valid, and if so returns a message and cookie header - * with the generated session id for the client to save. - * - * @param requestContext a [[RequestContext]] containing the http request - * @param featureFactoryConfig the feature factory configuration. - * @param system the current [[ActorSystem]] - * @return a [[HttpResponse]] containing either a failure message or a message with a cookie header containing - * the generated session id. - */ - def doLoginV1(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[HttpResponse] = { + * Checks if the credentials provided in [[RequestContext]] are valid, and if so returns a message and cookie header + * with the generated session id for the client to save. + * + * @param requestContext a [[RequestContext]] containing the http request + * @param featureFactoryConfig the feature factory configuration. + * @param system the current [[ActorSystem]] + * @return a [[HttpResponse]] containing either a failure message or a message with a cookie header containing + * the generated session id. + */ + def doLoginV1(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + executionContext: ExecutionContext + ): Future[HttpResponse] = { val settings = KnoraSettings(system) val credentials: Option[KnoraCredentialsV2] = extractCredentialsV2(requestContext) for { - userADM <- getUserADMThroughCredentialsV2(credentials = credentials, featureFactoryConfig = featureFactoryConfig) // will return or throw + userADM <- getUserADMThroughCredentialsV2( + credentials = credentials, + featureFactoryConfig = featureFactoryConfig + ) // will return or throw userProfile = userADM.asUserProfileV1 cookieDomain = Some(settings.cookieDomain) - sessionToken = JWTHelper.createToken(userProfile.userData.user_id.get, - settings.jwtSecretKey, - settings.jwtLongevity) + sessionToken = JWTHelper.createToken( + userProfile.userData.user_id.get, + settings.jwtSecretKey, + settings.jwtLongevity + ) httpResponse = HttpResponse( headers = List( headers.`Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - sessionToken, - domain = cookieDomain, - path = Some("/"), - httpOnly = true))), // set path to "/" to make the cookie valid for the whole domain (and not just a segment like v1 etc.) + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + sessionToken, + domain = cookieDomain, + path = Some("/"), + httpOnly = true + ) + ) + ), // set path to "/" to make the cookie valid for the whole domain (and not just a segment like v1 etc.) status = StatusCodes.OK, entity = HttpEntity( ContentTypes.`application/json`, @@ -113,18 +123,19 @@ trait Authenticator { } /** - * Checks if the provided credentials are valid, and if so returns a JWT token for the client to save. - * - * @param credentials the user supplied [[KnoraPasswordCredentialsV2]] containing the user's login information. - * @param featureFactoryConfig the feature factory configuration. - * @param system the current [[ActorSystem]] - * @return a [[HttpResponse]] containing either a failure message or a message with a cookie header containing - * the generated session id. - */ - def doLoginV2(credentials: KnoraPasswordCredentialsV2, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[HttpResponse] = { + * Checks if the provided credentials are valid, and if so returns a JWT token for the client to save. + * + * @param credentials the user supplied [[KnoraPasswordCredentialsV2]] containing the user's login information. + * @param featureFactoryConfig the feature factory configuration. + * @param system the current [[ActorSystem]] + * @return a [[HttpResponse]] containing either a failure message or a message with a cookie header containing + * the generated session id. + */ + def doLoginV2(credentials: KnoraPasswordCredentialsV2, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + executionContext: ExecutionContext + ): Future[HttpResponse] = { log.debug(s"doLoginV2 - credentials: $credentials") @@ -148,11 +159,15 @@ trait Authenticator { httpResponse = HttpResponse( headers = List( headers.`Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - token, - domain = cookieDomain, - path = Some("/"), - httpOnly = true))), // set path to "/" to make the cookie valid for the whole domain (and not just a segment like v1 etc.) + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + token, + domain = cookieDomain, + path = Some("/"), + httpOnly = true + ) + ) + ), // set path to "/" to make the cookie valid for the whole domain (and not just a segment like v1 etc.) status = StatusCodes.OK, entity = HttpEntity( ContentTypes.`application/json`, @@ -165,8 +180,9 @@ trait Authenticator { } yield httpResponse } - def presentLoginFormV2(requestContext: RequestContext)(implicit system: ActorSystem, - executionContext: ExecutionContext): Future[HttpResponse] = { + def presentLoginFormV2( + requestContext: RequestContext + )(implicit system: ActorSystem, executionContext: ExecutionContext): Future[HttpResponse] = { val settings = KnoraSettings(system) @@ -174,30 +190,30 @@ trait Authenticator { val form = s""" - |
- |
- | - | - |
- | - |
- |

- | © 2015–2019 Knora.org - |

- |
+ |
+ |
+ | + | + |
+ | + |
+ |

+ | © 2015–2019 Knora.org + |

+ |
""".stripMargin val httpResponse = HttpResponse( @@ -216,18 +232,19 @@ trait Authenticator { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Checks if the credentials provided in [[RequestContext]] are valid, and if so returns a message. No session is - * generated. - * - * @param requestContext a [[RequestContext]] containing the http request - * @param featureFactoryConfig the feature factory configuration. - * @param system the current [[ActorSystem]] - * @return a [[RequestContext]] - */ - def doAuthenticateV1(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[HttpResponse] = { + * Checks if the credentials provided in [[RequestContext]] are valid, and if so returns a message. No session is + * generated. + * + * @param requestContext a [[RequestContext]] containing the http request + * @param featureFactoryConfig the feature factory configuration. + * @param system the current [[ActorSystem]] + * @return a [[RequestContext]] + */ + def doAuthenticateV1(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + executionContext: ExecutionContext + ): Future[HttpResponse] = { val credentials: Option[KnoraCredentialsV2] = extractCredentialsV2(requestContext) @@ -255,16 +272,17 @@ trait Authenticator { } /** - * Checks if the credentials provided in [[RequestContext]] are valid. - * - * @param requestContext a [[RequestContext]] containing the http request - * @param system the current [[ActorSystem]] - * @return a [[HttpResponse]] - */ - def doAuthenticateV2(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[HttpResponse] = { + * Checks if the credentials provided in [[RequestContext]] are valid. + * + * @param requestContext a [[RequestContext]] containing the http request + * @param system the current [[ActorSystem]] + * @return a [[HttpResponse]] + */ + def doAuthenticateV2(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + executionContext: ExecutionContext + ): Future[HttpResponse] = { val credentials: Option[KnoraCredentialsV2] = extractCredentialsV2(requestContext) @@ -292,12 +310,12 @@ trait Authenticator { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Used to logout the user, i.e. returns a header deleting the cookie and puts the token on the 'invalidated' list. - * - * @param requestContext a [[RequestContext]] containing the http request - * @param system the current [[ActorSystem]] - * @return a [[HttpResponse]] - */ + * Used to logout the user, i.e. returns a header deleting the cookie and puts the token on the 'invalidated' list. + * + * @param requestContext a [[RequestContext]] containing the http request + * @param system the current [[ActorSystem]] + * @return a [[HttpResponse]] + */ def doLogoutV2(requestContext: RequestContext)(implicit system: ActorSystem): HttpResponse = { val credentials = extractCredentialsV2(requestContext) @@ -312,12 +330,16 @@ trait Authenticator { HttpResponse( headers = List( headers.`Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - "", - domain = cookieDomain, - path = Some("/"), - httpOnly = true, - expires = Some(DateTime(1970, 1, 1, 0, 0, 0))))), + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + "", + domain = cookieDomain, + path = Some("/"), + httpOnly = true, + expires = Some(DateTime(1970, 1, 1, 0, 0, 0)) + ) + ) + ), status = StatusCodes.OK, entity = HttpEntity( ContentTypes.`application/json`, @@ -335,12 +357,16 @@ trait Authenticator { HttpResponse( headers = List( headers.`Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - "", - domain = cookieDomain, - path = Some("/"), - httpOnly = true, - expires = Some(DateTime(1970, 1, 1, 0, 0, 0))))), + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + "", + domain = cookieDomain, + path = Some("/"), + httpOnly = true, + expires = Some(DateTime(1970, 1, 1, 0, 0, 0)) + ) + ) + ), status = StatusCodes.OK, entity = HttpEntity( ContentTypes.`application/json`, @@ -372,20 +398,21 @@ trait Authenticator { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Returns a UserProfile of the supplied type that match the credentials found in the [[RequestContext]]. - * The credentials can be email/password as parameters or auth headers, or session token in a cookie header. If no - * credentials are found, then a default UserProfile is returned. If the credentials are not correct, then the - * corresponding error is returned. - * - * @param requestContext a [[RequestContext]] containing the http request - * @param system the current [[ActorSystem]] - * @return a [[UserProfileV1]] - */ + * Returns a UserProfile of the supplied type that match the credentials found in the [[RequestContext]]. + * The credentials can be email/password as parameters or auth headers, or session token in a cookie header. If no + * credentials are found, then a default UserProfile is returned. If the credentials are not correct, then the + * corresponding error is returned. + * + * @param requestContext a [[RequestContext]] containing the http request + * @param system the current [[ActorSystem]] + * @return a [[UserProfileV1]] + */ @deprecated("Please use: getUserADM()", "Knora v1.7.0") - def getUserProfileV1(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[UserProfileV1] = { + def getUserProfileV1(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + executionContext: ExecutionContext + ): Future[UserProfileV1] = { val settings = KnoraSettings(system) @@ -394,16 +421,20 @@ trait Authenticator { if (settings.skipAuthentication) { // return anonymous if skipAuthentication log.debug( - "getUserProfileV1 - Authentication skipping active, returning default UserProfileV1 with 'anonymousUser' inside 'permissionData' set to true!") + "getUserProfileV1 - Authentication skipping active, returning default UserProfileV1 with 'anonymousUser' inside 'permissionData' set to true!" + ) FastFuture.successful(UserProfileV1()) } else if (credentials.isEmpty) { log.debug( - "getUserProfileV1 - No credentials found, returning default UserProfileV1 with 'anonymousUser' inside 'permissionData' set to true!") + "getUserProfileV1 - No credentials found, returning default UserProfileV1 with 'anonymousUser' inside 'permissionData' set to true!" + ) FastFuture.successful(UserProfileV1()) } else { for { - userADM <- getUserADMThroughCredentialsV2(credentials = credentials, - featureFactoryConfig = featureFactoryConfig) + userADM <- getUserADMThroughCredentialsV2( + credentials = credentials, + featureFactoryConfig = featureFactoryConfig + ) userProfile: UserProfileV1 = userADM.asUserProfileV1 _ = log.debug("Authenticator - getUserProfileV1 - userProfile: {}", userProfile) @@ -413,20 +444,21 @@ trait Authenticator { } /** - * Returns a User that match the credentials found in the [[RequestContext]]. - * The credentials can be email/password as parameters or auth headers, or session token in a cookie header. If no - * credentials are found, then a default UserProfile is returned. If the credentials are not correct, then the - * corresponding error is returned. - * - * @param requestContext a [[RequestContext]] containing the http request - * @param featureFactoryConfig the feature factory configuration. - * @param system the current [[ActorSystem]] - * @return a [[UserProfileV1]] - */ - def getUserADM(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[UserADM] = { + * Returns a User that match the credentials found in the [[RequestContext]]. + * The credentials can be email/password as parameters or auth headers, or session token in a cookie header. If no + * credentials are found, then a default UserProfile is returned. If the credentials are not correct, then the + * corresponding error is returned. + * + * @param requestContext a [[RequestContext]] containing the http request + * @param featureFactoryConfig the feature factory configuration. + * @param system the current [[ActorSystem]] + * @return a [[UserProfileV1]] + */ + def getUserADM(requestContext: RequestContext, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + executionContext: ExecutionContext + ): Future[UserADM] = { val settings = KnoraSettings(system) @@ -442,8 +474,10 @@ trait Authenticator { } else { for { - user: UserADM <- getUserADMThroughCredentialsV2(credentials = credentials, - featureFactoryConfig = featureFactoryConfig) + user: UserADM <- getUserADMThroughCredentialsV2( + credentials = credentials, + featureFactoryConfig = featureFactoryConfig + ) _ = log.debug("Authenticator - getUserADM - user: {}", user) /* we return the complete UserADM */ @@ -453,10 +487,10 @@ trait Authenticator { } /** - * This companion object holds all private methods used in the trait. This division is needed so that we can test - * the private methods directly with scalatest as described in [[https://groups.google.com/forum/#!topic/scalatest-users/FeaO\_\_f1dN4]] - * and [[http://doc.scalatest.org/2.2.6/index.html#org.scalatest.PrivateMethodTester]] - */ + * This companion object holds all private methods used in the trait. This division is needed so that we can test + * the private methods directly with scalatest as described in [[https://groups.google.com/forum/#!topic/scalatest-users/FeaO\_\_f1dN4]] + * and [[http://doc.scalatest.org/2.2.6/index.html#org.scalatest.PrivateMethodTester]] + */ object Authenticator { val BAD_CRED_PASSWORD_MISMATCH = "bad credentials: user found, but password did not match" @@ -476,24 +510,23 @@ object Authenticator { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Tries to authenticate the supplied credentials (email/password or token). In the case of email/password, - * authentication is performed checking if the supplied email/password combination is valid by retrieving the - * user's profile. In the case of the token, the token itself is validated. If both are supplied, then both need - * to be valid. - * - * @param credentials the user supplied and extracted credentials. - * @param featureFactoryConfig the feature factory configuration. - * @param system the current [[ActorSystem]] - * @return true if the credentials are valid. If the credentials are invalid, then the corresponding exception - * will be thrown. - * @throws BadCredentialsException when no credentials are supplied; when user is not active; - * when the password does not match; when the supplied token is not valid. - */ - def authenticateCredentialsV2(credentials: Option[KnoraCredentialsV2], featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[Boolean] = { - + * Tries to authenticate the supplied credentials (email/password or token). In the case of email/password, + * authentication is performed checking if the supplied email/password combination is valid by retrieving the + * user's profile. In the case of the token, the token itself is validated. If both are supplied, then both need + * to be valid. + * + * @param credentials the user supplied and extracted credentials. + * @param featureFactoryConfig the feature factory configuration. + * @param system the current [[ActorSystem]] + * @return true if the credentials are valid. If the credentials are invalid, then the corresponding exception + * will be thrown. + * @throws BadCredentialsException when no credentials are supplied; when user is not active; + * when the password does not match; when the supplied token is not valid. + */ + def authenticateCredentialsV2( + credentials: Option[KnoraCredentialsV2], + featureFactoryConfig: FeatureFactoryConfig + )(implicit system: ActorSystem, responderManager: ActorRef, executionContext: ExecutionContext): Future[Boolean] = for { settings <- FastFuture.successful(KnoraSettings(system)) @@ -538,18 +571,17 @@ object Authenticator { } } yield result - } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HELPER METHODS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Tries to extract the credentials from the requestContext (parameters, auth headers, token) - * - * @param requestContext a [[RequestContext]] containing the http request - * @return [[KnoraCredentialsV2]]. - */ + * Tries to extract the credentials from the requestContext (parameters, auth headers, token) + * + * @param requestContext a [[RequestContext]] containing the http request + * @return [[KnoraCredentialsV2]]. + */ private def extractCredentialsV2(requestContext: RequestContext): Option[KnoraCredentialsV2] = { // log.debug("extractCredentialsV2 start ...") @@ -571,11 +603,11 @@ object Authenticator { } /** - * Tries to extract credentials supplied as URL parameters. - * - * @param requestContext the HTTP request context. - * @return [[KnoraCredentialsV2]]. - */ + * Tries to extract credentials supplied as URL parameters. + * + * @param requestContext the HTTP request context. + * @return [[KnoraCredentialsV2]]. + */ private def extractCredentialsFromParametersV2(requestContext: RequestContext): Option[KnoraCredentialsV2] = { // extract email/password from parameters @@ -629,20 +661,20 @@ object Authenticator { } /** - * Tries to extract the credentials (email/password, token) from the authorization header and the session token - * from the cookie header. - * - * The authorization header looks something like this: 'Authorization: Basic xyz, Bearer xyz.xyz.xyz' - * if both the email/password and token are sent. - * - * If more then one set of credentials is found, then they are selected as follows: - * 1. email/password - * 2. authorization token - * 3. session token - * - * @param requestContext the HTTP request context. - * @return an optional [[KnoraCredentialsV2]]. - */ + * Tries to extract the credentials (email/password, token) from the authorization header and the session token + * from the cookie header. + * + * The authorization header looks something like this: 'Authorization: Basic xyz, Bearer xyz.xyz.xyz' + * if both the email/password and token are sent. + * + * If more then one set of credentials is found, then they are selected as follows: + * 1. email/password + * 2. authorization token + * 3. session token + * + * @param requestContext the HTTP request context. + * @return an optional [[KnoraCredentialsV2]]. + */ private def extractCredentialsFromHeaderV2(requestContext: RequestContext): Option[KnoraCredentialsV2] = { // Session token from cookie header @@ -713,20 +745,19 @@ object Authenticator { } /** - * Tries to retrieve a [[UserADM]] based on the supplied credentials. If both email/password and session - * token are supplied, then the user profile for the session token is returned. This method should only be used - * with authenticated credentials. - * - * @param credentials the user supplied credentials. - * @param featureFactoryConfig the feature factory configuration. - * @return a [[UserADM]] - * @throws AuthenticationException when the IRI can not be found inside the token, which is probably a bug. - */ - private def getUserADMThroughCredentialsV2(credentials: Option[KnoraCredentialsV2], - featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - executionContext: ExecutionContext): Future[UserADM] = { + * Tries to retrieve a [[UserADM]] based on the supplied credentials. If both email/password and session + * token are supplied, then the user profile for the session token is returned. This method should only be used + * with authenticated credentials. + * + * @param credentials the user supplied credentials. + * @param featureFactoryConfig the feature factory configuration. + * @return a [[UserADM]] + * @throws AuthenticationException when the IRI can not be found inside the token, which is probably a bug. + */ + private def getUserADMThroughCredentialsV2( + credentials: Option[KnoraCredentialsV2], + featureFactoryConfig: FeatureFactoryConfig + )(implicit system: ActorSystem, responderManager: ActorRef, executionContext: ExecutionContext): Future[UserADM] = { val settings = KnoraSettings(system) @@ -783,21 +814,22 @@ object Authenticator { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * Tries to get a [[UserADM]]. - * - * @param identifier the IRI, email, or username of the user to be queried - * @param featureFactoryConfig the feature factory configuration. - * @param system the current akka actor system - * @param timeout the timeout of the query - * @param executionContext the current execution context - * @return a [[UserADM]] - * @throws BadCredentialsException when either the supplied email is empty or no user with such an email could be found. - */ - private def getUserByIdentifier(identifier: UserIdentifierADM, featureFactoryConfig: FeatureFactoryConfig)( - implicit system: ActorSystem, - responderManager: ActorRef, - timeout: Timeout, - executionContext: ExecutionContext): Future[UserADM] = { + * Tries to get a [[UserADM]]. + * + * @param identifier the IRI, email, or username of the user to be queried + * @param featureFactoryConfig the feature factory configuration. + * @param system the current akka actor system + * @param timeout the timeout of the query + * @param executionContext the current execution context + * @return a [[UserADM]] + * @throws BadCredentialsException when either the supplied email is empty or no user with such an email could be found. + */ + private def getUserByIdentifier(identifier: UserIdentifierADM, featureFactoryConfig: FeatureFactoryConfig)(implicit + system: ActorSystem, + responderManager: ActorRef, + timeout: Timeout, + executionContext: ExecutionContext + ): Future[UserADM] = { val userADMFuture = for { maybeUserADM <- (responderManager ? UserGetADM( @@ -822,8 +854,8 @@ object Authenticator { } /** - * Provides functions for creating, decoding, and validating JWT tokens. - */ + * Provides functions for creating, decoding, and validating JWT tokens. + */ object JWTHelper { import Authenticator.AUTHENTICATION_INVALIDATION_CACHE_NAME @@ -835,22 +867,24 @@ object JWTHelper { val log = Logger(LoggerFactory.getLogger(this.getClass)) /** - * Creates a JWT. - * - * @param userIri the user IRI that will be encoded into the token. - * @param secret the secret key used for encoding. - * @param longevity the token's longevity. - * @param content any other content to be included in the token. - * @return a [[String]] containing the JWT. - */ - def createToken(userIri: IRI, - secret: String, - longevity: FiniteDuration, - content: Map[String, JsValue] = Map.empty): String = { + * Creates a JWT. + * + * @param userIri the user IRI that will be encoded into the token. + * @param secret the secret key used for encoding. + * @param longevity the token's longevity. + * @param content any other content to be included in the token. + * @return a [[String]] containing the JWT. + */ + def createToken( + userIri: IRI, + secret: String, + longevity: FiniteDuration, + content: Map[String, JsValue] = Map.empty + ): String = { val stringFormatter = StringFormatter.getGeneralInstance // now in seconds - val now: Long = System.currentTimeMillis() / 1000l + val now: Long = System.currentTimeMillis() / 1000L // calculate expiration time (seconds) val nowPlusLongevity: Long = now + longevity.toSeconds @@ -876,15 +910,15 @@ object JWTHelper { } /** - * Validates a JWT, taking the invalidation cache into account. The invalidation cache holds invalidated - * tokens, which would otherwise validate. This method also makes sure that the required headers and claims are - * present. - * - * @param token the JWT. - * @param secret the secret used to encode the token. - * @return a [[Boolean]]. - */ - def validateToken(token: String, secret: String): Boolean = { + * Validates a JWT, taking the invalidation cache into account. The invalidation cache holds invalidated + * tokens, which would otherwise validate. This method also makes sure that the required headers and claims are + * present. + * + * @param token the JWT. + * @param secret the secret used to encode the token. + * @return a [[Boolean]]. + */ + def validateToken(token: String, secret: String): Boolean = if (CacheUtil.get[UserADM](AUTHENTICATION_INVALIDATION_CACHE_NAME, token).nonEmpty) { // token invalidated so no need to decode log.debug("validateToken - token found in invalidation cache, so not valid") @@ -892,32 +926,30 @@ object JWTHelper { } else { decodeToken(token, secret).isDefined } - } /** - * Extracts the encoded user IRI. This method also makes sure that the required headers and claims are present. - * - * @param token the JWT. - * @param secret the secret used to encode the token. - * @return an optional [[IRI]]. - */ - def extractUserIriFromToken(token: String, secret: String): Option[IRI] = { + * Extracts the encoded user IRI. This method also makes sure that the required headers and claims are present. + * + * @param token the JWT. + * @param secret the secret used to encode the token. + * @return an optional [[IRI]]. + */ + def extractUserIriFromToken(token: String, secret: String): Option[IRI] = decodeToken(token, secret) match { case Some((_: JwtHeader, claim: JwtClaim)) => claim.subject case None => None } - } /** - * Extracts application-specific content from a JWT token. This method also makes sure that the required headers - * and claims are present. - * - * @param token the JWT. - * @param secret the secret used to encode the token. - * @param contentName the name of the content field to be extracted. - * @return the string value of the specified content field. - */ - def extractContentFromToken(token: String, secret: String, contentName: String): Option[String] = { + * Extracts application-specific content from a JWT token. This method also makes sure that the required headers + * and claims are present. + * + * @param token the JWT. + * @param secret the secret used to encode the token. + * @param contentName the name of the content field to be extracted. + * @return the string value of the specified content field. + */ + def extractContentFromToken(token: String, secret: String, contentName: String): Option[String] = decodeToken(token, secret) match { case Some((_: JwtHeader, claim: JwtClaim)) => claim.content.parseJson.asJsObject.fields.get(contentName) match { @@ -927,15 +959,14 @@ object JWTHelper { case None => None } - } /** - * Decodes and validates a JWT token. - * - * @param token the token to be decoded. - * @param secret the secret used to encode the token. - * @return the token's header and claim, or `None` if the token is invalid. - */ + * Decodes and validates a JWT token. + * + * @param token the token to be decoded. + * @param secret the secret used to encode the token. + * @return the token's header and claim, or `None` if the token is invalid. + */ private def decodeToken(token: String, secret: String): Option[(JwtHeader, JwtClaim)] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -953,8 +984,9 @@ object JWTHelper { if (!missingRequiredContent) { Try( - stringFormatter.validateAndEscapeIri(claim.subject.get, - throw BadRequestException("Invalid user IRI in JWT"))) match { + stringFormatter + .validateAndEscapeIri(claim.subject.get, throw BadRequestException("Invalid user IRI in JWT")) + ) match { case Success(_) => Some(header, claim) case Failure(e) => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala index 6e73774816..e366734fe7 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala @@ -34,8 +34,8 @@ import scala.concurrent.duration._ case class HealthCheckResult(name: String, severity: String, status: Boolean, message: String) /** - * Provides health check logic - */ + * Provides health check logic + */ trait HealthCheck { this: HealthRoute => @@ -71,8 +71,7 @@ trait HealthCheck { } yield response - protected def createResponse(result: HealthCheckResult): HttpResponse = { - + protected def createResponse(result: HealthCheckResult): HttpResponse = HttpResponse( status = statusCode(result.status), entity = HttpEntity( @@ -85,44 +84,40 @@ trait HealthCheck { ).compactPrint ) ) - } private def status(s: Boolean) = if (s) "healthy" else "unhealthy" private def statusCode(s: Boolean) = if (s) StatusCodes.OK else StatusCodes.ServiceUnavailable - private def unhealthy(str: String) = { + private def unhealthy(str: String) = HealthCheckResult( name = "AppState", severity = "non fatal", status = false, message = str ) - } - private def healthy() = { + private def healthy() = HealthCheckResult( name = "AppState", severity = "non fatal", status = true, message = "Application is healthy" ) - } } /** - * Provides the '/health' endpoint serving the health status. - */ + * Provides the '/health' endpoint serving the health status. + */ class HealthRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with HealthCheck { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("health") { get { requestContext => requestContext.complete(healthCheck()) } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala index 4f17345216..7896112431 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala @@ -45,19 +45,19 @@ import org.knora.webapi.settings.{KnoraDispatchers, KnoraSettings, KnoraSettings import scala.concurrent.{ExecutionContext, Future} /** - * Data needed to be passed to each route. - * - * @param system the actor system. - * @param appActor the main application actor. - */ + * Data needed to be passed to each route. + * + * @param system the actor system. + * @param appActor the main application actor. + */ case class KnoraRouteData(system: ActorSystem, appActor: ActorRef) /** - * An abstract class providing functionality that is commonly used by Knora routes and by - * feature factories that construct Knora routes. - * - * @param routeData a [[KnoraRouteData]] providing access to the application. - */ + * An abstract class providing functionality that is commonly used by Knora routes and by + * feature factories that construct Knora routes. + * + * @param routeData a [[KnoraRouteData]] providing access to the application. + */ abstract class KnoraRouteFactory(routeData: KnoraRouteData) { implicit protected val system: ActorSystem = routeData.system implicit protected val settings: KnoraSettingsImpl = KnoraSettings(system) @@ -74,47 +74,47 @@ abstract class KnoraRouteFactory(routeData: KnoraRouteData) { protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl /** - * Constructs a route. This can be done: - * - * - by statically returning a routing function (if this is an ordinary route that - * doesn't use a feature factory, or if this is a route feature returned by - * a feature factory) - * - * - by asking a feature factory for a routing function (if this is a façade route) - * - * - by making a choice based on a feature toggle (if this is a feature factory) - * - * @param featureFactoryConfig the per-request feature factory configuration. - * @return a route configured with the features enabled by the feature factory configuration. - */ + * Constructs a route. This can be done: + * + * - by statically returning a routing function (if this is an ordinary route that + * doesn't use a feature factory, or if this is a route feature returned by + * a feature factory) + * + * - by asking a feature factory for a routing function (if this is a façade route) + * + * - by making a choice based on a feature toggle (if this is a feature factory) + * + * @param featureFactoryConfig the per-request feature factory configuration. + * @return a route configured with the features enabled by the feature factory configuration. + */ def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route } /** - * An abstract class providing functionality that is commonly used in implementing Knora routes. - * - * @param routeData a [[KnoraRouteData]] providing access to the application. - */ + * An abstract class providing functionality that is commonly used in implementing Knora routes. + * + * @param routeData a [[KnoraRouteData]] providing access to the application. + */ abstract class KnoraRoute(routeData: KnoraRouteData) extends KnoraRouteFactory(routeData) { /** - * A [[KnoraSettingsFeatureFactoryConfig]] to use as the parent [[FeatureFactoryConfig]]. - */ + * A [[KnoraSettingsFeatureFactoryConfig]] to use as the parent [[FeatureFactoryConfig]]. + */ private val knoraSettingsFeatureFactoryConfig: KnoraSettingsFeatureFactoryConfig = new KnoraSettingsFeatureFactoryConfig(settings) /** - * Returns a routing function that uses per-request feature factory configuration. - */ + * Returns a routing function that uses per-request feature factory configuration. + */ def knoraApiPath: Route = runRoute /** - * A routing function that calls `makeRoute`, passing it the per-request feature factory configuration, - * and runs the resulting routing function. - * - * @param requestContext the HTTP request context. - * @return the result of running the route. - */ + * A routing function that calls `makeRoute`, passing it the per-request feature factory configuration, + * and runs the resulting routing function. + * + * @param requestContext the HTTP request context. + * @return the result of running the route. + */ private def runRoute(requestContext: RequestContext): Future[RouteResult] = { // Construct the per-request feature factory configuration. val featureFactoryConfig: FeatureFactoryConfig = new RequestContextFeatureFactoryConfig( @@ -130,19 +130,22 @@ abstract class KnoraRoute(routeData: KnoraRouteData) extends KnoraRouteFactory(r } /** - * Gets a [[ProjectADM]] corresponding to the specified project IRI. - * - * @param projectIri the project IRI. - * @param featureFactoryConfig the feature factory configuration. - * @param requestingUser the user making the request. - * @return the corresponding [[ProjectADM]]. - */ - protected def getProjectADM(projectIri: IRI, - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM): Future[ProjectADM] = { + * Gets a [[ProjectADM]] corresponding to the specified project IRI. + * + * @param projectIri the project IRI. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the corresponding [[ProjectADM]]. + */ + protected def getProjectADM( + projectIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM + ): Future[ProjectADM] = { val checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( projectIri, - throw BadRequestException(s"Invalid project IRI: $projectIri")) + throw BadRequestException(s"Invalid project IRI: $projectIri") + ) if (stringFormatter.isKnoraBuiltInProjectIriStr(checkedProjectIri)) { throw BadRequestException(s"Metadata cannot be updated for a built-in project") diff --git a/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala index 8ef45a78b4..ab8b2557c2 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala @@ -28,8 +28,8 @@ import scala.concurrent.duration._ import scala.util.{Failure, Success} /** - * Provides AppState actor access logic - */ + * Provides AppState actor access logic + */ trait AppStateAccess { this: RejectingRoute => @@ -45,19 +45,18 @@ trait AppStateAccess { } /** - * A route used for rejecting requests to certain paths depending on the state of the app or the configuration. - * - * If the current state of the application is anything other then [[AppStates.Running]], then return [[StatusCodes.ServiceUnavailable]]. - * If the current state of the application is [[AppStates.Running]], then reject requests to paths as defined - * in 'application.conf'. - */ + * A route used for rejecting requests to certain paths depending on the state of the app or the configuration. + * + * If the current state of the application is anything other then [[AppStates.Running]], then return [[StatusCodes.ServiceUnavailable]]. + * If the current state of the application is [[AppStates.Running]], then reject requests to paths as defined + * in 'application.conf'. + */ class RejectingRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with AppStateAccess { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path(Remaining) { wholePath => // check to see if route is on the rejection list val rejectSeq: Seq[Option[Boolean]] = settings.routesToReject.map { pathToReject: String => @@ -95,5 +94,4 @@ class RejectingRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi complete(StatusCodes.ServiceUnavailable, ex.getMessage) } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala index ee8fecfc0a..c345c9c0f0 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala @@ -33,30 +33,31 @@ import org.knora.webapi.settings.KnoraSettingsImpl import scala.concurrent.{ExecutionContext, Future} /** - * Convenience methods for Knora Admin routes. - */ + * Convenience methods for Knora Admin routes. + */ object RouteUtilADM { /** - * Sends a message to a responder and completes the HTTP request by returning the response as JSON. - * - * @param requestMessageF a future containing a [[KnoraRequestADM]] message that should be sent to the responder manager. - * @param requestContext the akka-http [[RequestContext]]. - * @param featureFactoryConfig the per-request feature factory configuration. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[Future]] containing a [[RouteResult]]. - */ + * Sends a message to a responder and completes the HTTP request by returning the response as JSON. + * + * @param requestMessageF a future containing a [[KnoraRequestADM]] message that should be sent to the responder manager. + * @param requestContext the akka-http [[RequestContext]]. + * @param featureFactoryConfig the per-request feature factory configuration. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[Future]] containing a [[RouteResult]]. + */ def runJsonRoute( - requestMessageF: Future[KnoraRequestADM], - requestContext: RequestContext, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { + requestMessageF: Future[KnoraRequestADM], + requestContext: RequestContext, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { val httpResponse: Future[HttpResponse] = for { @@ -83,16 +84,15 @@ object RouteUtilADM { } jsonResponse = knoraResponse.toJsValue.asJsObject - } yield - featureFactoryConfig.addHeaderToHttpResponse( - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity( - ContentTypes.`application/json`, - jsonResponse.compactPrint - ) + } yield featureFactoryConfig.addHeaderToHttpResponse( + HttpResponse( + status = StatusCodes.OK, + entity = HttpEntity( + ContentTypes.`application/json`, + jsonResponse.compactPrint ) ) + ) requestContext.complete(httpResponse) } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV1.scala index fb974b38c7..088c9e9554 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV1.scala @@ -50,28 +50,29 @@ import scala.concurrent.{ExecutionContext, Future} import scala.reflect.ClassTag /** - * Convenience methods for Knora routes. - */ + * Convenience methods for Knora routes. + */ object RouteUtilV1 { /** - * Sends a message to a responder and completes the HTTP request by returning the response as JSON. - * - * @param requestMessage a [[KnoraRequestV1]] message that should be sent to the responder manager. - * @param requestContext the akka-http [[RequestContext]]. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[Future]] containing a [[RouteResult]]. - */ + * Sends a message to a responder and completes the HTTP request by returning the response as JSON. + * + * @param requestMessage a [[KnoraRequestV1]] message that should be sent to the responder manager. + * @param requestContext the akka-http [[RequestContext]]. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[Future]] containing a [[RouteResult]]. + */ def runJsonRoute( - requestMessage: KnoraRequestV1, - requestContext: RequestContext, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { + requestMessage: KnoraRequestV1, + requestContext: RequestContext, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { // Optionally log the request message. TODO: move this to the testing framework. if (settings.dumpMessages) { log.debug(requestMessage.toString) @@ -95,38 +96,39 @@ object RouteUtilV1 { // The request was successful, so add a status of ApiStatusCodesV1.OK to the response. jsonResponseWithStatus = JsObject( - knoraResponse.toJsValue.asJsObject.fields + ("status" -> JsNumber(ApiStatusCodesV1.OK.id))) + knoraResponse.toJsValue.asJsObject.fields + ("status" -> JsNumber(ApiStatusCodesV1.OK.id)) + ) - } yield - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity( - ContentTypes.`application/json`, - jsonResponseWithStatus.compactPrint - ) + } yield HttpResponse( + status = StatusCodes.OK, + entity = HttpEntity( + ContentTypes.`application/json`, + jsonResponseWithStatus.compactPrint ) + ) requestContext.complete(httpResponse) } /** - * Sends a message (resulting from a [[Future]]) to a responder and completes the HTTP request by returning the response as JSON. - * - * @param requestMessageF a [[Future]] containing a [[KnoraRequestV1]] message that should be sent to the responder manager. - * @param requestContext the akka-http [[RequestContext]]. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[Future]] containing a [[RouteResult]]. - */ + * Sends a message (resulting from a [[Future]]) to a responder and completes the HTTP request by returning the response as JSON. + * + * @param requestMessageF a [[Future]] containing a [[KnoraRequestV1]] message that should be sent to the responder manager. + * @param requestContext the akka-http [[RequestContext]]. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[Future]] containing a [[RouteResult]]. + */ def runJsonRouteWithFuture[RequestMessageT <: KnoraRequestV1]( - requestMessageF: Future[RequestMessageT], - requestContext: RequestContext, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { + requestMessageF: Future[RequestMessageT], + requestContext: RequestContext, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = for { requestMessage <- requestMessageF routeResult <- runJsonRoute( @@ -137,29 +139,29 @@ object RouteUtilV1 { log = log ) } yield routeResult - } /** - * Sends a message to a responder and completes the HTTP request by returning the response as HTML. - * - * @tparam RequestMessageT the type of request message to be sent to the responder. - * @tparam ReplyMessageT the type of reply message expected from the responder. - * @param requestMessageF a [[Future]] containing the message that should be sent to the responder manager. - * @param viewHandler a function that can generate HTML from the responder's reply message. - * @param requestContext the [[RequestContext]]. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - */ + * Sends a message to a responder and completes the HTTP request by returning the response as HTML. + * + * @tparam RequestMessageT the type of request message to be sent to the responder. + * @tparam ReplyMessageT the type of reply message expected from the responder. + * @param requestMessageF a [[Future]] containing the message that should be sent to the responder manager. + * @param viewHandler a function that can generate HTML from the responder's reply message. + * @param requestContext the [[RequestContext]]. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + */ def runHtmlRoute[RequestMessageT <: KnoraRequestV1, ReplyMessageT <: KnoraResponseV1: ClassTag]( - requestMessageF: Future[RequestMessageT], - viewHandler: (ReplyMessageT, ActorRef) => String, - requestContext: RequestContext, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { + requestMessageF: Future[RequestMessageT], + viewHandler: (ReplyMessageT, ActorRef) => String, + requestContext: RequestContext, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { val httpResponse: Future[HttpResponse] = for { @@ -186,47 +188,44 @@ object RouteUtilV1 { log.debug(knoraResponse.toString) } - } yield - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity( - ContentTypes.`text/html(UTF-8)`, - viewHandler(knoraResponse, responderManager) - ) + } yield HttpResponse( + status = StatusCodes.OK, + entity = HttpEntity( + ContentTypes.`text/html(UTF-8)`, + viewHandler(knoraResponse, responderManager) ) + ) requestContext.complete(httpResponse) } /** - * - * Converts XML to a [[TextWithStandoffTagsV2]], representing the text and its standoff markup. - * - * @param xml the given XML to be converted to standoff. - * @param mappingIri the mapping to be used to convert the XML to standoff. - * @param acceptStandoffLinksToClientIDs if `true`, allow standoff link tags to use the client's IDs for target - * resources. In a bulk import, this allows standoff links to resources - * that are to be created by the import. - * @param userProfile the user making the request. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[TextWithStandoffTagsV2]]. - */ - def convertXMLtoStandoffTagV1(xml: String, - mappingIri: IRI, - acceptStandoffLinksToClientIDs: Boolean, - userProfile: UserADM, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter)( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TextWithStandoffTagsV2] = { - + * Converts XML to a [[TextWithStandoffTagsV2]], representing the text and its standoff markup. + * + * @param xml the given XML to be converted to standoff. + * @param mappingIri the mapping to be used to convert the XML to standoff. + * @param acceptStandoffLinksToClientIDs if `true`, allow standoff link tags to use the client's IDs for target + * resources. In a bulk import, this allows standoff links to resources + * that are to be created by the import. + * @param userProfile the user making the request. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[TextWithStandoffTagsV2]]. + */ + def convertXMLtoStandoffTagV1( + xml: String, + mappingIri: IRI, + acceptStandoffLinksToClientIDs: Boolean, + userProfile: UserADM, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TextWithStandoffTagsV2] = for { // get the mapping directly from v2 responder directly (to avoid useless back and forth conversions between v2 and v1 message formats) @@ -244,19 +243,18 @@ object RouteUtilV1 { ) } yield textWithStandoffTagV1 - } /** - * MIME types used in Sipi to store image files. - */ + * MIME types used in Sipi to store image files. + */ private val imageMimeTypes: Set[String] = Set( "image/jp2", - "image/jpx", + "image/jpx" ) /** - * MIME types used in Sipi to store text files. - */ + * MIME types used in Sipi to store text files. + */ private val textMimeTypes: Set[String] = Set( "application/xml", "text/xml", @@ -265,8 +263,8 @@ object RouteUtilV1 { ) /** - * MIME types used in Sipi to store document files. - */ + * MIME types used in Sipi to store document files. + */ private val documentMimeTypes: Set[String] = Set( "application/pdf", "application/msword", @@ -282,8 +280,8 @@ object RouteUtilV1 { ) /** - * MIME types used in Sipi to store audio files. - */ + * MIME types used in Sipi to store audio files. + */ private val audioMimeTypes: Set[String] = Set( "audio/mpeg", "audio/mp4", @@ -293,23 +291,25 @@ object RouteUtilV1 { ) /** - * MIME types used in Sipi to store video files. - */ + * MIME types used in Sipi to store video files. + */ private val videoMimeTypes: Set[String] = Set( "video/mp4" ) /** - * Converts file metadata from Sipi into a [[FileValueV1]]. - * - * @param filename the filename. - * @param fileMetadataResponse the file metadata from Sipi. - * @param projectShortcode the project short code that the file value is to be created in. - * @return a [[FileValueV1]] representing the file. - */ - def makeFileValue(filename: String, - fileMetadataResponse: GetFileMetadataResponse, - projectShortcode: String): FileValueV1 = { + * Converts file metadata from Sipi into a [[FileValueV1]]. + * + * @param filename the filename. + * @param fileMetadataResponse the file metadata from Sipi. + * @param projectShortcode the project short code that the file value is to be created in. + * @return a [[FileValueV1]] representing the file. + */ + def makeFileValue( + filename: String, + fileMetadataResponse: GetFileMetadataResponse, + projectShortcode: String + ): FileValueV1 = if (imageMimeTypes.contains(fileMetadataResponse.internalMimeType)) { StillImageFileValueV1( internalFilename = filename, @@ -365,5 +365,4 @@ object RouteUtilV1 { } else { throw BadRequestException(s"MIME type ${fileMetadataResponse.internalMimeType} not supported in Knora API v1") } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV2.scala index bf862950b2..b233401bce 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilV2.scala @@ -39,91 +39,90 @@ import scala.concurrent.{ExecutionContext, Future} import scala.util.control.Exception.catching /** - * Handles message formatting, content negotiation, and simple interactions with responders, on behalf of Knora routes. - */ + * Handles message formatting, content negotiation, and simple interactions with responders, on behalf of Knora routes. + */ object RouteUtilV2 { /** - * The name of the HTTP header in which an ontology schema can be requested. - */ + * The name of the HTTP header in which an ontology schema can be requested. + */ val SCHEMA_HEADER: String = "x-knora-accept-schema" /** - * The name of the URL parameter in which an ontology schema can be requested. - */ + * The name of the URL parameter in which an ontology schema can be requested. + */ val SCHEMA_PARAM: String = "schema" /** - * The name of the complex schema. - */ + * The name of the complex schema. + */ val SIMPLE_SCHEMA_NAME: String = "simple" /** - * The name of the simple schema. - */ + * The name of the simple schema. + */ val COMPLEX_SCHEMA_NAME: String = "complex" /** - * The name of the HTTP header in which results from a project can be requested. - */ + * The name of the HTTP header in which results from a project can be requested. + */ val PROJECT_HEADER: String = "x-knora-accept-project" /** - * The name of the URL parameter that can be used to specify how markup should be returned - * with text values. - */ + * The name of the URL parameter that can be used to specify how markup should be returned + * with text values. + */ val MARKUP_PARAM: String = "markup" /** - * The name of the HTTP header that can be used to specify how markup should be returned with - * text values. - */ + * The name of the HTTP header that can be used to specify how markup should be returned with + * text values. + */ val MARKUP_HEADER: String = "x-knora-accept-markup" /** - * Indicates that standoff markup should be returned as XML with text values. - */ + * Indicates that standoff markup should be returned as XML with text values. + */ val MARKUP_XML: String = "xml" /** - * Indicates that markup should not be returned with text values, because it will be requested - * separately as standoff. - */ + * Indicates that markup should not be returned with text values, because it will be requested + * separately as standoff. + */ val MARKUP_STANDOFF: String = "standoff" /** - * The name of the HTTP header that can be used to request hierarchical or flat JSON-LD. - */ + * The name of the HTTP header that can be used to request hierarchical or flat JSON-LD. + */ val JSON_LD_RENDERING_HEADER: String = "x-knora-json-ld-rendering" /** - * Indicates that flat JSON-LD should be returned, i.e. objects with IRIs should be referenced by IRI - * rather than nested. Blank nodes will still be nested in any case. - */ + * Indicates that flat JSON-LD should be returned, i.e. objects with IRIs should be referenced by IRI + * rather than nested. Blank nodes will still be nested in any case. + */ val JSON_LD_RENDERING_FLAT: String = "flat" /** - * Indicates that hierarchical JSON-LD should be returned, i.e. objects with IRIs should be nested when - * possible, rather than referenced by IRI. - */ + * Indicates that hierarchical JSON-LD should be returned, i.e. objects with IRIs should be nested when + * possible, rather than referenced by IRI. + */ val JSON_LD_RENDERING_HIERARCHICAL: String = "hierarchical" /** - * Gets the ontology schema that is specified in an HTTP request. The schema can be specified - * either in the HTTP header [[SCHEMA_HEADER]] or in the URL parameter [[SCHEMA_PARAM]]. - * If no schema is specified in the request, the default of [[ApiV2Complex]] is returned. - * - * @param requestContext the akka-http [[RequestContext]]. - * @return the specified schema, or [[ApiV2Complex]] if no schema was specified in the request. - */ + * Gets the ontology schema that is specified in an HTTP request. The schema can be specified + * either in the HTTP header [[SCHEMA_HEADER]] or in the URL parameter [[SCHEMA_PARAM]]. + * If no schema is specified in the request, the default of [[ApiV2Complex]] is returned. + * + * @param requestContext the akka-http [[RequestContext]]. + * @return the specified schema, or [[ApiV2Complex]] if no schema was specified in the request. + */ def getOntologySchema(requestContext: RequestContext): ApiV2Schema = { - def nameToSchema(schemaName: String): ApiV2Schema = { + def nameToSchema(schemaName: String): ApiV2Schema = schemaName match { case SIMPLE_SCHEMA_NAME => ApiV2Simple case COMPLEX_SCHEMA_NAME => ApiV2Complex case _ => throw BadRequestException(s"Unrecognised ontology schema name: $schemaName") } - } val params: Map[String, String] = requestContext.request.uri.query().toMap @@ -139,23 +138,22 @@ object RouteUtilV2 { } /** - * Gets the type of standoff rendering that should be used when returning text with standoff. - * The name of the standoff rendering can be specified either in the HTTP header [[MARKUP_HEADER]] - * or in the URL parameter [[MARKUP_PARAM]]. If no rendering is specified in the request, the - * default of [[MarkupAsXml]] is returned. - * - * @param requestContext the akka-http [[RequestContext]]. - * @return the specified standoff rendering, or [[MarkupAsXml]] if no rendering was specified - * in the request. - */ + * Gets the type of standoff rendering that should be used when returning text with standoff. + * The name of the standoff rendering can be specified either in the HTTP header [[MARKUP_HEADER]] + * or in the URL parameter [[MARKUP_PARAM]]. If no rendering is specified in the request, the + * default of [[MarkupAsXml]] is returned. + * + * @param requestContext the akka-http [[RequestContext]]. + * @return the specified standoff rendering, or [[MarkupAsXml]] if no rendering was specified + * in the request. + */ private def getStandoffRendering(requestContext: RequestContext): Option[MarkupRendering] = { - def nameToStandoffRendering(standoffRenderingName: String): MarkupRendering = { + def nameToStandoffRendering(standoffRenderingName: String): MarkupRendering = standoffRenderingName match { case MARKUP_XML => MarkupAsXml case MARKUP_STANDOFF => MarkupAsStandoff case _ => throw BadRequestException(s"Unrecognised standoff rendering: $standoffRenderingName") } - } val params: Map[String, String] = requestContext.request.uri.query().toMap @@ -170,13 +168,12 @@ object RouteUtilV2 { } private def getJsonLDRendering(requestContext: RequestContext): Option[JsonLDRendering] = { - def nameToJsonLDRendering(jsonLDRenderingName: String): JsonLDRendering = { + def nameToJsonLDRendering(jsonLDRenderingName: String): JsonLDRendering = jsonLDRenderingName match { case JSON_LD_RENDERING_FLAT => FlatJsonLD case JSON_LD_RENDERING_HIERARCHICAL => HierarchicalJsonLD case _ => throw BadRequestException(s"Unrecognised JSON-LD rendering: $jsonLDRenderingName") } - } requestContext.request.headers.find(_.lowercaseName == JSON_LD_RENDERING_HEADER).map { header => nameToJsonLDRendering(header.value) @@ -184,54 +181,53 @@ object RouteUtilV2 { } /** - * Gets the schema options submitted in the request. - * - * @param requestContext the request context. - * @return the set of schema options submitted in the request, including default options. - */ - def getSchemaOptions(requestContext: RequestContext): Set[SchemaOption] = { + * Gets the schema options submitted in the request. + * + * @param requestContext the request context. + * @return the set of schema options submitted in the request, including default options. + */ + def getSchemaOptions(requestContext: RequestContext): Set[SchemaOption] = Set( getStandoffRendering(requestContext), getJsonLDRendering(requestContext) ).flatten - } /** - * Gets the project IRI specified in a Knora-specific HTTP header. - * - * @param requestContext the akka-http [[RequestContext]]. - * @return the specified project IRI, or [[None]] if no project header was included in the request. - */ - def getProject(requestContext: RequestContext)(implicit stringFormatter: StringFormatter): Option[SmartIri] = { + * Gets the project IRI specified in a Knora-specific HTTP header. + * + * @param requestContext the akka-http [[RequestContext]]. + * @return the specified project IRI, or [[None]] if no project header was included in the request. + */ + def getProject(requestContext: RequestContext)(implicit stringFormatter: StringFormatter): Option[SmartIri] = requestContext.request.headers.find(_.lowercaseName == PROJECT_HEADER).map { header => val projectIriStr = header.value projectIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid project IRI: $projectIriStr")) } - } /** - * Sends a message to a responder and completes the HTTP request by returning the response as RDF using content negotiation. - * - * @param requestMessage a future containing a [[KnoraRequestV2]] message that should be sent to the responder manager. - * @param requestContext the akka-http [[RequestContext]]. - * @param featureFactoryConfig the per-request feature factory configuration. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param targetSchema the API schema that should be used in the response. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[Future]] containing a [[RouteResult]]. - */ - private def runRdfRoute(requestMessage: KnoraRequestV2, - requestContext: RequestContext, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter, - targetSchema: OntologySchema, - schemaOptions: Set[SchemaOption])(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[RouteResult] = { + * Sends a message to a responder and completes the HTTP request by returning the response as RDF using content negotiation. + * + * @param requestMessage a future containing a [[KnoraRequestV2]] message that should be sent to the responder manager. + * @param requestContext the akka-http [[RequestContext]]. + * @param featureFactoryConfig the per-request feature factory configuration. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param targetSchema the API schema that should be used in the response. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[Future]] containing a [[RouteResult]]. + */ + private def runRdfRoute( + requestMessage: KnoraRequestV2, + requestContext: RequestContext, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter, + targetSchema: OntologySchema, + schemaOptions: Set[SchemaOption] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { // Optionally log the request message. TODO: move this to the testing framework. if (settings.dumpMessages) { log.debug(requestMessage.toString) @@ -270,42 +266,42 @@ object RouteUtilV2 { featureFactoryConfig = featureFactoryConfig, schemaOptions = schemaOptions ) - } yield - featureFactoryConfig.addHeaderToHttpResponse( - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity( - contentType, - formattedResponseContent - ) + } yield featureFactoryConfig.addHeaderToHttpResponse( + HttpResponse( + status = StatusCodes.OK, + entity = HttpEntity( + contentType, + formattedResponseContent ) ) + ) requestContext.complete(httpResponse) } /** - * Sends a message to a responder and completes the HTTP request by returning the response as TEI/XML. - * - * @param requestMessageF a future containing a [[KnoraRequestV2]] message that should be sent to the responder manager. - * @param requestContext the akka-http [[RequestContext]]. - * @param featureFactoryConfig the per-request feature factory configuration. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param targetSchema the API schema that should be used in the response. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[Future]] containing a [[RouteResult]]. - */ + * Sends a message to a responder and completes the HTTP request by returning the response as TEI/XML. + * + * @param requestMessageF a future containing a [[KnoraRequestV2]] message that should be sent to the responder manager. + * @param requestContext the akka-http [[RequestContext]]. + * @param featureFactoryConfig the per-request feature factory configuration. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param targetSchema the API schema that should be used in the response. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[Future]] containing a [[RouteResult]]. + */ def runTEIXMLRoute( - requestMessageF: Future[KnoraRequestV2], - requestContext: RequestContext, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter, - targetSchema: ApiV2Schema)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { + requestMessageF: Future[KnoraRequestV2], + requestContext: RequestContext, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter, + targetSchema: ApiV2Schema + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = { val contentType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`) @@ -322,45 +318,44 @@ object RouteUtilV2 { throw UnexpectedMessageException(s"Responder sent a reply of type ${other.getClass.getCanonicalName}") } - } yield - featureFactoryConfig.addHeaderToHttpResponse( - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity( - contentType, - teiResponse.toXML - ) + } yield featureFactoryConfig.addHeaderToHttpResponse( + HttpResponse( + status = StatusCodes.OK, + entity = HttpEntity( + contentType, + teiResponse.toXML ) ) + ) requestContext.complete(httpResponse) } /** - * Sends a message (resulting from a [[Future]]) to a responder and completes the HTTP request by returning the response as RDF. - * - * @param requestMessageF a [[Future]] containing a [[KnoraRequestV2]] message that should be sent to the responder manager. - * @param requestContext the akka-http [[RequestContext]]. - * @param featureFactoryConfig the per-request feature factory configuration. - * @param settings the application's settings. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param targetSchema the API schema that should be used in the response. - * @param schemaOptions the schema options that should be used when processing the request. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[Future]] containing a [[RouteResult]]. - */ - def runRdfRouteWithFuture(requestMessageF: Future[KnoraRequestV2], - requestContext: RequestContext, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl, - responderManager: ActorRef, - log: LoggingAdapter, - targetSchema: OntologySchema, - schemaOptions: Set[SchemaOption])( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[RouteResult] = { + * Sends a message (resulting from a [[Future]]) to a responder and completes the HTTP request by returning the response as RDF. + * + * @param requestMessageF a [[Future]] containing a [[KnoraRequestV2]] message that should be sent to the responder manager. + * @param requestContext the akka-http [[RequestContext]]. + * @param featureFactoryConfig the per-request feature factory configuration. + * @param settings the application's settings. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param targetSchema the API schema that should be used in the response. + * @param schemaOptions the schema options that should be used when processing the request. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[Future]] containing a [[RouteResult]]. + */ + def runRdfRouteWithFuture( + requestMessageF: Future[KnoraRequestV2], + requestContext: RequestContext, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl, + responderManager: ActorRef, + log: LoggingAdapter, + targetSchema: OntologySchema, + schemaOptions: Set[SchemaOption] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[RouteResult] = for { requestMessage <- requestMessageF routeResult <- runRdfRoute( @@ -375,50 +370,51 @@ object RouteUtilV2 { ) } yield routeResult - } /** - * Parses a request entity to an [[RdfModel]]. - * - * @param entityStr the request entity. - * @param requestContext the request context. - * @return the corresponding [[RdfModel]]. - */ - def requestToRdfModel(entityStr: String, - requestContext: RequestContext, - featureFactoryConfig: FeatureFactoryConfig): RdfModel = { + * Parses a request entity to an [[RdfModel]]. + * + * @param entityStr the request entity. + * @param requestContext the request context. + * @return the corresponding [[RdfModel]]. + */ + def requestToRdfModel( + entityStr: String, + requestContext: RequestContext, + featureFactoryConfig: FeatureFactoryConfig + ): RdfModel = RdfFeatureFactory .getRdfFormatUtil(featureFactoryConfig) .parseToRdfModel( rdfStr = entityStr, rdfFormat = RdfFormat.fromMediaType(getRequestContentType(requestContext)) ) - } /** - * Parses a request entity to a [[JsonLDDocument]]. - * - * @param entityStr the request entity. - * @param requestContext the request context. - * @return the corresponding [[JsonLDDocument]]. - */ - def requestToJsonLD(entityStr: String, - requestContext: RequestContext, - featureFactoryConfig: FeatureFactoryConfig): JsonLDDocument = { + * Parses a request entity to a [[JsonLDDocument]]. + * + * @param entityStr the request entity. + * @param requestContext the request context. + * @return the corresponding [[JsonLDDocument]]. + */ + def requestToJsonLD( + entityStr: String, + requestContext: RequestContext, + featureFactoryConfig: FeatureFactoryConfig + ): JsonLDDocument = RdfFeatureFactory .getRdfFormatUtil(featureFactoryConfig) .parseToJsonLDDocument( rdfStr = entityStr, rdfFormat = RdfFormat.fromMediaType(getRequestContentType(requestContext)) ) - } /** - * Determines the content type of a request according to its `Content-Type` header. - * - * @param requestContext the request context. - * @return a [[MediaType.NonBinary]] representing the submitted content type. - */ + * Determines the content type of a request according to its `Content-Type` header. + * + * @param requestContext the request context. + * @return a [[MediaType.NonBinary]] representing the submitted content type. + */ private def getRequestContentType(requestContext: RequestContext): MediaType.NonBinary = { // Does the request contain a Content-Type header? val maybeContentType: Option[ContentType] = Some(requestContext.request.entity.contentType) @@ -445,11 +441,11 @@ object RouteUtilV2 { } /** - * Chooses an RDF media type for the response, using content negotiation as per [[https://tools.ietf.org/html/rfc7231#section-5.3.2]]. - * - * @param requestContext the request context. - * @return an RDF media type. - */ + * Chooses an RDF media type for the response, using content negotiation as per [[https://tools.ietf.org/html/rfc7231#section-5.3.2]]. + * + * @param requestContext the request context. + * @return an RDF media type. + */ private def chooseRdfMediaTypeForResponse(requestContext: RequestContext): MediaType.NonBinary = { // Get the client's HTTP Accept header, if provided. val maybeAcceptHeader: Option[HttpHeader] = requestContext.request.headers.find(_.lowercaseName == "accept") @@ -463,17 +459,16 @@ object RouteUtilV2 { .flatMap { headerValueItem => val mediaRangeParts: Array[String] = headerValueItem.split(';').map(_.trim) val mediaTypeStr: String = mediaRangeParts.headOption.getOrElse( - throw BadRequestException(s"Invalid Accept header: ${acceptHeader.value}")) + throw BadRequestException(s"Invalid Accept header: ${acceptHeader.value}") + ) // Get the qValue, if provided; it defaults to 1. - val qValue: Float = mediaRangeParts.tail - .flatMap { param => - param.split('=').map(_.trim) match { - case Array("q", qValueStr) => catching(classOf[NumberFormatException]).opt(qValueStr.toFloat) - case _ => None // Ignore other parameters. - } + val qValue: Float = mediaRangeParts.tail.flatMap { param => + param.split('=').map(_.trim) match { + case Array("q", qValueStr) => catching(classOf[NumberFormatException]).opt(qValueStr.toFloat) + case _ => None // Ignore other parameters. } - .headOption + }.headOption .getOrElse(1) val maybeMediaType: Option[MediaType] = RdfMediaTypes.registry.get(mediaTypeStr) match { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala index db80a1bcb0..d153f554b6 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala @@ -29,8 +29,8 @@ import org.knora.webapi.routing.admin._ import org.knora.webapi.routing.admin.lists._ /** - * Provides the '/api-docs' endpoint serving the 'swagger.json' OpenAPI specification - */ + * Provides the '/api-docs' endpoint serving the 'swagger.json' OpenAPI specification + */ class SwaggerApiDocsRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with SwaggerHttpService { // List all routes here @@ -64,10 +64,9 @@ class SwaggerApiDocsRoute(routeData: KnoraRouteData) extends KnoraRoute(routeDat override val securitySchemeDefinitions = Map("basicAuth" -> new BasicAuthDefinition()) /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = routes - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala index aa1f8efed2..1829c8b22b 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala @@ -29,26 +29,27 @@ import spray.json.{JsObject, JsString} import scala.concurrent.duration._ -case class VersionCheckResult(name: String, - webapi: String, - scala: String, - akkaHttp: String, - sipi: String, - fuseki: String) +case class VersionCheckResult( + name: String, + webapi: String, + scala: String, + akkaHttp: String, + sipi: String, + fuseki: String +) /** - * Provides version check logic - */ + * Provides version check logic + */ trait VersionCheck { this: VersionRoute => override implicit val timeout: Timeout = 1.second - protected def versionCheck: HttpResponse = { + protected def versionCheck: HttpResponse = createResponse(getVersion) - } - protected def createResponse(result: VersionCheckResult): HttpResponse = { + protected def createResponse(result: VersionCheckResult): HttpResponse = HttpResponse( status = StatusCodes.OK, entity = HttpEntity( @@ -59,11 +60,10 @@ trait VersionCheck { "scala" -> JsString(result.scala), "akkaHttp" -> JsString(result.akkaHttp), "sipi" -> JsString(result.sipi), - "fuseki" -> JsString(result.fuseki), + "fuseki" -> JsString(result.fuseki) ).compactPrint ) ) - } private def getVersion: VersionCheckResult = { var sipiVersion = VersionInfo.sipiVersion @@ -86,18 +86,17 @@ trait VersionCheck { } /** - * Provides the '/version' endpoint serving the components versions. - */ + * Provides the '/version' endpoint serving the components versions. + */ class VersionRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with VersionCheck { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("version") { get { requestContext => requestContext.complete(versionCheck) } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala index 1059ccba4a..7ab72ec505 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala @@ -35,8 +35,8 @@ object GroupsRouteADM { } /** - * Provides a routing function for API routes that deal with groups. - */ + * Provides a routing function for API routes that deal with groups. + */ @Api(value = "groups", produces = "application/json") @Path("/admin/groups") @@ -57,8 +57,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) getGroupMembers(featureFactoryConfig) /** - * Returns all groups - */ + * Returns all groups + */ private def getGroups(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath) { get { /* return all groups */ @@ -68,11 +68,10 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupsGetRequestADM( - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield GroupsGetRequestADM( + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -86,8 +85,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) } /** - * Creates a group - */ + * Creates a group + */ private def createGroup(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath) { post { /* create a new group */ @@ -97,13 +96,12 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupCreateRequestADM( - createRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield GroupCreateRequestADM( + createRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -118,8 +116,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) } /** - * Returns a single group identified by IRI. - */ + * Returns a single group identified by IRI. + */ private def getGroupByIri(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath / Segment) { value => get { @@ -133,12 +131,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupGetRequestADM( - groupIri = checkedGroupIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield GroupGetRequestADM( + groupIri = checkedGroupIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -152,8 +149,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) } /** - * Update basic group information. - */ + * Update basic group information. + */ private def updateGroup(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath / Segment) { value => put { /* update a group identified by iri */ @@ -162,12 +159,13 @@ class GroupsRouteADM(routeData: KnoraRouteData) stringFormatter.validateAndEscapeIri(value, throw BadRequestException(s"Invalid group IRI $value")) /** - * The api request is already checked at time of creation. - * See case class. - */ + * The api request is already checked at time of creation. + * See case class. + */ if (apiRequest.status.nonEmpty) { throw BadRequestException( - "The status property is not allowed to be set for this route. Please use the change status route.") + "The status property is not allowed to be set for this route. Please use the change status route." + ) } val requestMessage = for { @@ -175,14 +173,13 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupChangeRequestADM( - groupIri = checkedGroupIri, - changeGroupRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield GroupChangeRequestADM( + groupIri = checkedGroupIri, + changeGroupRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -197,8 +194,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) } /** - * Update the group's status. - */ + * Update the group's status. + */ private def changeGroupStatus(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath / Segment / "status") { value => put { @@ -208,12 +205,12 @@ class GroupsRouteADM(routeData: KnoraRouteData) stringFormatter.validateAndEscapeIri(value, throw BadRequestException(s"Invalid group IRI $value")) /** - * The api request is already checked at time of creation. - * See case class. Depending on the data sent, we are either - * doing a general update or status change. Since we are in - * the status change route, we are only interested in the - * value of the status property - */ + * The api request is already checked at time of creation. + * See case class. Depending on the data sent, we are either + * doing a general update or status change. Since we are in + * the status change route, we are only interested in the + * value of the status property + */ if (apiRequest.status.isEmpty) { throw BadRequestException("The status property is not allowed to be empty.") } @@ -223,14 +220,13 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupChangeStatusRequestADM( - groupIri = checkedGroupIri, - changeGroupRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield GroupChangeStatusRequestADM( + groupIri = checkedGroupIri, + changeGroupRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -245,8 +241,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) } /** - * Deletes a group (sets status to false) - */ + * Deletes a group (sets status to false) + */ private def deleteGroup(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath / Segment) { value => delete { /* update group status to false */ @@ -259,14 +255,13 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupChangeStatusRequestADM( - groupIri = checkedGroupIri, - changeGroupRequest = ChangeGroupApiRequestADM(status = Some(false)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield GroupChangeStatusRequestADM( + groupIri = checkedGroupIri, + changeGroupRequest = ChangeGroupApiRequestADM(status = Some(false)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -280,8 +275,8 @@ class GroupsRouteADM(routeData: KnoraRouteData) } /** - * Gets members of single group. - */ + * Gets members of single group. + */ private def getGroupMembers(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath / Segment / "members") { value => get { @@ -295,12 +290,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GroupMembersGetRequestADM( - groupIri = checkedGroupIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield GroupMembersGetRequestADM( + groupIri = checkedGroupIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala index 5958518ae0..85d14045f3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala @@ -26,16 +26,15 @@ import org.knora.webapi.routing.admin.lists._ import org.knora.webapi.routing.{KnoraRoute, KnoraRouteData} /** - * Provides an akka-http-routing function for API routes that deal with lists. - */ + * Provides an akka-http-routing function for API routes that deal with lists. + */ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) { private val featureFactory: ListsRouteADMFeatureFactory = new ListsRouteADMFeatureFactory(routeData) private val deleteNodeRoute: DeleteListItemsRouteADM = new DeleteListItemsRouteADM(routeData) private val updateNodeRoute: UpdateListItemsRouteADM = new UpdateListItemsRouteADM(routeData) - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = featureFactory.makeRoute(featureFactoryConfig) ~ deleteNodeRoute.makeRoute(featureFactoryConfig) ~ updateNodeRoute.makeRoute(featureFactoryConfig) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala index 8c39e3fb07..7c4f1ce131 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala @@ -26,18 +26,17 @@ import org.knora.webapi.routing.admin.permissions._ import org.knora.webapi.routing.{KnoraRoute, KnoraRouteData} /** - * Provides an akka-http-routing function for API routes that deal with permissions. - */ + * Provides an akka-http-routing function for API routes that deal with permissions. + */ class PermissionsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) { private val createPermissionRoute: CreatePermissionRouteADM = new CreatePermissionRouteADM(routeData) private val getPermissionRoute: GetPermissionsRouteADM = new GetPermissionsRouteADM(routeData) private val updatePermissionRoute: UpdatePermissionRouteADM = new UpdatePermissionRouteADM(routeData) private val deletePermissionRoute: DeletePermissionRouteADM = new DeletePermissionRouteADM(routeData) - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = createPermissionRoute.makeRoute(featureFactoryConfig) ~ getPermissionRoute.makeRoute(featureFactoryConfig) ~ updatePermissionRoute.makeRoute(featureFactoryConfig) ~ deletePermissionRoute.makeRoute(featureFactoryConfig) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala index a32334b12a..fb1df54acc 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala @@ -57,8 +57,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) import ProjectsRouteADM._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = getProjects(featureFactoryConfig) ~ addProject(featureFactoryConfig) ~ @@ -81,14 +81,17 @@ class ProjectsRouteADM(routeData: KnoraRouteData) getProjectData(featureFactoryConfig) /* return all projects */ - @ApiOperation(value = "Get projects", - nickname = "getProjects", - httpMethod = "GET", - response = classOf[ProjectsGetResponseADM]) + @ApiOperation( + value = "Get projects", + nickname = "getProjects", + httpMethod = "GET", + response = classOf[ProjectsGetResponseADM] + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def getProjects(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath) { get { requestContext => val requestMessage: Future[ProjectsGetRequestADM] = for { @@ -96,11 +99,10 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectsGetRequestADM( - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ProjectsGetRequestADM( + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -114,22 +116,28 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /* create a new project */ - @ApiOperation(value = "Add new project", - nickname = "addProject", - httpMethod = "POST", - response = classOf[ProjectOperationResponseADM]) + @ApiOperation( + value = "Add new project", + nickname = "addProject", + httpMethod = "POST", + response = classOf[ProjectOperationResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"project\" to create", - required = true, - dataTypeClass = classOf[CreateProjectApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"project\" to create", + required = true, + dataTypeClass = classOf[CreateProjectApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def addProject(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath) { post { entity(as[CreateProjectApiRequestADM]) { apiRequest => requestContext => @@ -138,13 +146,12 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectCreateRequestADM( - createRequest = apiRequest.validateAndEscape, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield ProjectCreateRequestADM( + createRequest = apiRequest.validateAndEscape, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -166,11 +173,10 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectsKeywordsGetRequestADM( - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ProjectsKeywordsGetRequestADM( + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -195,12 +201,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectKeywordsGetRequestADM( - projectIri = checkedProjectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ProjectKeywordsGetRequestADM( + projectIri = checkedProjectIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -214,8 +219,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * returns a single project identified through iri - */ + * returns a single project identified through iri + */ private def getProjectByIri(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment) { value => get { requestContext => @@ -226,15 +231,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( value, - throw BadRequestException(s"Invalid project IRI $value")) - - } yield - ProjectGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project IRI $value") ) + } yield ProjectGetRequestADM( + identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -247,8 +252,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * returns a single project identified through shortname. - */ + * returns a single project identified through shortname. + */ private def getProjectByShortname(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortname" / Segment) { value => get { requestContext => @@ -259,15 +264,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) shortNameDec = stringFormatter.validateAndEscapeProjectShortname( value, - throw BadRequestException(s"Invalid project shortname $value")) - - } yield - ProjectGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortname = Some(shortNameDec)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project shortname $value") ) + } yield ProjectGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortname = Some(shortNameDec)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -280,8 +285,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * returns a single project identified through shortcode. - */ + * returns a single project identified through shortcode. + */ private def getProjectByShortcode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortcode" / Segment) { value => get { requestContext => @@ -292,15 +297,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedShortcode = stringFormatter.validateAndEscapeProjectShortcode( value, - throw BadRequestException(s"Invalid project shortcode $value")) - - } yield - ProjectGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortcode = Some(checkedShortcode)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project shortcode $value") ) + } yield ProjectGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortcode = Some(checkedShortcode)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -313,8 +318,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * update a project identified by iri - */ + * update a project identified by iri + */ private def changeProject(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment) { value => put { @@ -329,14 +334,13 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectChangeRequestADM( - projectIri = checkedProjectIri, - changeProjectRequest = apiRequest.validateAndEscape, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield ProjectChangeRequestADM( + projectIri = checkedProjectIri, + changeProjectRequest = apiRequest.validateAndEscape, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -351,8 +355,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: update project status to false - */ + * API MAY CHANGE: update project status to false + */ @ApiMayChange private def deleteProject(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment) { value => @@ -365,14 +369,13 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectChangeRequestADM( - projectIri = checkedProjectIri, - changeProjectRequest = ChangeProjectApiRequestADM(status = Some(false)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield ProjectChangeRequestADM( + projectIri = checkedProjectIri, + changeProjectRequest = ChangeProjectApiRequestADM(status = Some(false)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -386,8 +389,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: returns all members part of a project identified through iri - */ + * API MAY CHANGE: returns all members part of a project identified through iri + */ @ApiMayChange private def getProjectMembersByIri(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment / "members") { value => @@ -399,15 +402,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( value, - throw BadRequestException(s"Invalid project IRI $value")) - - } yield - ProjectMembersGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project IRI $value") ) + } yield ProjectMembersGetRequestADM( + identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -420,8 +423,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: returns all members part of a project identified through shortname - */ + * API MAY CHANGE: returns all members part of a project identified through shortname + */ @ApiMayChange private def getProjectMembersByShortname(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortname" / Segment / "members") { value => @@ -433,15 +436,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) shortNameDec = stringFormatter.validateAndEscapeProjectShortname( value, - throw BadRequestException(s"Invalid project shortname $value")) - - } yield - ProjectMembersGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortname = Some(shortNameDec)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project shortname $value") ) + } yield ProjectMembersGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortname = Some(shortNameDec)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -454,8 +457,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: returns all members part of a project identified through shortcode - */ + * API MAY CHANGE: returns all members part of a project identified through shortcode + */ @ApiMayChange private def getProjectMembersByShortcode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortcode" / Segment / "members") { value => @@ -467,15 +470,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedShortcode = stringFormatter.validateAndEscapeProjectShortcode( value, - throw BadRequestException(s"Invalid project shortcode $value")) - - } yield - ProjectMembersGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortcode = Some(checkedShortcode)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project shortcode $value") ) + } yield ProjectMembersGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortcode = Some(checkedShortcode)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -488,8 +491,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: returns all admin members part of a project identified through iri - */ + * API MAY CHANGE: returns all admin members part of a project identified through iri + */ @ApiMayChange private def getProjectAdminMembersByIri(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment / "admin-members") { value => @@ -501,15 +504,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( value, - throw BadRequestException(s"Invalid project IRI $value")) - - } yield - ProjectAdminMembersGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project IRI $value") ) + } yield ProjectAdminMembersGetRequestADM( + identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -522,8 +525,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: returns all admin members part of a project identified through shortname - */ + * API MAY CHANGE: returns all admin members part of a project identified through shortname + */ @ApiMayChange private def getProjectAdminMembersByShortname(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortname" / Segment / "admin-members") { value => @@ -535,15 +538,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedShortname = stringFormatter.validateAndEscapeProjectShortname( value, - throw BadRequestException(s"Invalid project shortname $value")) - - } yield - ProjectAdminMembersGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortname = Some(checkedShortname)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project shortname $value") ) + } yield ProjectAdminMembersGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortname = Some(checkedShortname)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -556,8 +559,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * API MAY CHANGE: returns all admin members part of a project identified through shortcode - */ + * API MAY CHANGE: returns all admin members part of a project identified through shortcode + */ @ApiMayChange private def getProjectAdminMembersByShortcode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortcode" / Segment / "admin-members") { value => @@ -569,15 +572,15 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) checkedShortcode = stringFormatter.validateProjectShortcode( value, - throw BadRequestException(s"Invalid project shortcode $value")) - - } yield - ProjectAdminMembersGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortcode = Some(checkedShortcode)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid project shortcode $value") ) + } yield ProjectAdminMembersGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortcode = Some(checkedShortcode)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, requestContext = requestContext, @@ -590,8 +593,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * Returns the project's restricted view settings identified through IRI. - */ + * Returns the project's restricted view settings identified through IRI. + */ @ApiMayChange private def getProjectRestrictedViewSettingsByIri(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment / "RestrictedViewSettings") { value: String => @@ -602,12 +605,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectRestrictedViewSettingsGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(value)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ProjectRestrictedViewSettingsGetRequestADM( + identifier = ProjectIdentifierADM(maybeIri = Some(value)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -621,8 +623,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * Returns the project's restricted view settings identified through shortname. - */ + * Returns the project's restricted view settings identified through shortname. + */ @ApiMayChange private def getProjectRestrictedViewSettingsByShortname(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortname" / Segment / "RestrictedViewSettings") { value: String => @@ -634,12 +636,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) ) shortNameDec = java.net.URLDecoder.decode(value, "utf-8") - } yield - ProjectRestrictedViewSettingsGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortname = Some(shortNameDec)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ProjectRestrictedViewSettingsGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortname = Some(shortNameDec)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -653,8 +654,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) } /** - * Returns the project's restricted view settings identified through shortcode. - */ + * Returns the project's restricted view settings identified through shortcode. + */ @ApiMayChange private def getProjectRestrictedViewSettingsByShortcode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "shortcode" / Segment / "RestrictedViewSettings") { value: String => @@ -664,12 +665,11 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ProjectRestrictedViewSettingsGetRequestADM( - identifier = ProjectIdentifierADM(maybeShortcode = Some(value)), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ProjectRestrictedViewSettingsGetRequestADM( + identifier = ProjectIdentifierADM(maybeShortcode = Some(value)), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -686,8 +686,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) `Content-Disposition`(ContentDispositionTypes.attachment, Map(("filename", "project-data.trig"))) /** - * Returns all ontologies, data, and configuration belonging to a project. - */ + * Returns all ontologies, data, and configuration belonging to a project. + */ private def getProjectData(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath / "iri" / Segment / "AllData") { projectIri: IRI => get { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/SipiRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/SipiRouteADM.scala index 3f29299bf8..d877dcebe8 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/SipiRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/SipiRouteADM.scala @@ -27,18 +27,17 @@ import org.knora.webapi.messages.admin.responder.sipimessages.SipiFileInfoGetReq import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} /** - * Provides a routing function for the API that Sipi connects to. - */ + * Provides a routing function for the API that Sipi connects to. + */ class SipiRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * A routing function for the API that Sipi connects to. - */ + * A routing function for the API that Sipi connects to. + */ /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("admin" / "files" / Segments(2)) { projectIDAndFile: Seq[String] => get { requestContext => val requestMessage = for { @@ -48,18 +47,19 @@ class SipiRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) with ) projectID = stringFormatter.validateProjectShortcode( projectIDAndFile.head, - throw BadRequestException(s"Invalid project ID: '${projectIDAndFile.head}'")) + throw BadRequestException(s"Invalid project ID: '${projectIDAndFile.head}'") + ) filename = stringFormatter.toSparqlEncodedString( projectIDAndFile(1), - throw BadRequestException(s"Invalid filename: '${projectIDAndFile(1)}'")) - _ = println(s"/admin/files route called for filename $filename") - } yield - SipiFileInfoGetRequestADM( - projectID = projectID, - filename = filename, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + throw BadRequestException(s"Invalid filename: '${projectIDAndFile(1)}'") ) + _ = println(s"/admin/files route called for filename $filename") + } yield SipiFileInfoGetRequestADM( + projectID = projectID, + filename = filename, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -72,5 +72,4 @@ class SipiRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) with } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala index 6d69620286..106964e007 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala @@ -35,8 +35,8 @@ import scala.concurrent.Future import scala.concurrent.duration._ /** - * A route used to send requests which can directly affect the data stored inside the triplestore. - */ + * A route used to send requests which can directly affect the data stored inside the triplestore. + */ @Api(value = "store", produces = "application/json") @Path("/admin/store") @@ -46,14 +46,15 @@ class StoreRouteADM(routeData: KnoraRouteData) with StoresADMJsonProtocol { /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = Route { path("admin" / "store") { get { requestContext => - /** Maybe return some statistics about the store, e.g., what triplestore, number of triples in - * each named graph and in total, etc. - */ + /** + * Maybe return some statistics about the store, e.g., what triplestore, number of triples in + * each named graph and in total, etc. + */ // TODO: Implement some simple return requestContext.complete("Hello World") } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala index 614de266b4..61be5f38a2 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala @@ -81,9 +81,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit get { requestContext => val requestMessage: Future[UsersGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UsersGetRequestADM( featureFactoryConfig = featureFactoryConfig, requestingUser = requestingUser @@ -141,9 +141,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) val requestMessage: Future[UserCreateRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserCreateRequestADM( userCreatePayloadADM = user, featureFactoryConfig = featureFactoryConfig, @@ -171,9 +171,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit get { requestContext => val requestMessage: Future[UserGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserGetRequestADM( identifier = UserIdentifierADM(maybeIri = Some(userIri)), userInformationTypeADM = UserInformationTypeADM.RESTRICTED, @@ -200,9 +200,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit get { requestContext => val requestMessage: Future[UserGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserGetRequestADM( identifier = UserIdentifierADM(maybeEmail = Some(userIri)), userInformationTypeADM = UserInformationTypeADM.RESTRICTED, @@ -229,9 +229,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit get { requestContext => val requestMessage: Future[UserGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserGetRequestADM( identifier = UserIdentifierADM(maybeUsername = Some(userIri)), userInformationTypeADM = UserInformationTypeADM.RESTRICTED, @@ -304,9 +304,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /* the api request is already checked at time of creation. see case class. */ val requestMessage: Future[UsersResponderRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserChangeBasicInformationRequestADM( userIri = checkedUserIri, userUpdateBasicInformationPayload = userUpdatePayload, @@ -359,9 +359,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UsersResponderRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserChangePasswordRequestADM( userIri = checkedUserIri, userUpdatePasswordPayload = UserUpdatePasswordPayloadADM(requesterPassword, changedPassword), @@ -410,9 +410,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UsersResponderRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserChangeStatusRequestADM( userIri = checkedUserIri, status = newStatus, @@ -458,9 +458,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserChangeStatusRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserChangeStatusRequestADM( userIri = checkedUserIri, status = status, @@ -508,9 +508,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UsersResponderRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserChangeSystemAdminMembershipStatusRequestADM( userIri = checkedUserIri, systemAdmin = newSystemAdmin, @@ -545,9 +545,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserProjectMembershipsGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserProjectMembershipsGetRequestADM( userIri = checkedUserIri, featureFactoryConfig = featureFactoryConfig, @@ -593,9 +593,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserProjectMembershipAddRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserProjectMembershipAddRequestADM( userIri = checkedUserIri, projectIri = checkedProjectIri, @@ -643,9 +643,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserProjectMembershipRemoveRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserProjectMembershipRemoveRequestADM( userIri = checkedUserIri, projectIri = checkedProjectIri, @@ -679,9 +679,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserProjectAdminMembershipsGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserProjectAdminMembershipsGetRequestADM( userIri = checkedUserIri, featureFactoryConfig = featureFactoryConfig, @@ -728,9 +728,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserProjectAdminMembershipAddRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserProjectAdminMembershipAddRequestADM( userIri = checkedUserIri, projectIri = checkedProjectIri, @@ -778,9 +778,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserProjectAdminMembershipRemoveRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserProjectAdminMembershipRemoveRequestADM( userIri = checkedUserIri, projectIri = checkedProjectIri, @@ -814,9 +814,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserGroupMembershipsGetRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserGroupMembershipsGetRequestADM( userIri = checkedUserIri, featureFactoryConfig = featureFactoryConfig, @@ -859,9 +859,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserGroupMembershipAddRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserGroupMembershipAddRequestADM( userIri = checkedUserIri, groupIri = checkedGroupIri, @@ -906,9 +906,9 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val requestMessage: Future[UserGroupMembershipRemoveRequestADM] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield UserGroupMembershipRemoveRequestADM( userIri = checkedUserIri, groupIri = checkedGroupIri, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala index 6eb0a74322..e1d37439ba 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala @@ -35,10 +35,10 @@ object DeleteListItemsRouteADM { } /** - * A [[Feature]] that provides routes to delete list items. - * - * @param routeData the [[KnoraRouteData]] to be used in constructing the route. - */ + * A [[Feature]] that provides routes to delete list items. + * + * @param routeData the [[KnoraRouteData]] to be used in constructing the route. + */ class DeleteListItemsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Feature @@ -60,11 +60,12 @@ class DeleteListItemsRouteADM(routeData: KnoraRouteData) val requestMessage: Future[ListItemDeleteRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - ListItemDeleteRequestADM(nodeIri = nodeIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID()) + } yield ListItemDeleteRequestADM( + nodeIri = nodeIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/ListsRouteADMFeatureFactory.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/ListsRouteADMFeatureFactory.scala index 4a83d3e5bf..d8d04dd094 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/ListsRouteADMFeatureFactory.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/ListsRouteADMFeatureFactory.scala @@ -24,33 +24,32 @@ import org.knora.webapi.feature.{FeatureFactory, FeatureFactoryConfig} import org.knora.webapi.routing.{KnoraRouteData, KnoraRouteFactory} /** - * A [[FeatureFactory]] that constructs list admin routes. - * - * @param routeData the [[KnoraRouteData]] to be used in constructing the routes. - */ + * A [[FeatureFactory]] that constructs list admin routes. + * + * @param routeData the [[KnoraRouteData]] to be used in constructing the routes. + */ class ListsRouteADMFeatureFactory(routeData: KnoraRouteData) extends KnoraRouteFactory(routeData) with FeatureFactory { /** - * The old lists route feature. - */ + * The old lists route feature. + */ private val oldListsRouteADMFeature = new OldListsRouteADMFeature(routeData) /** - * The new lists route feature. - */ + * The new lists route feature. + */ private val newListsRouteADMFeature = new NewListsRouteADMFeature(routeData) /** - * Returns a lists route reflecting the specified feature factory configuration. - * - * @param featureFactoryConfig a [[FeatureFactoryConfig]]. - * @return a lists route. - */ - def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + * Returns a lists route reflecting the specified feature factory configuration. + * + * @param featureFactoryConfig a [[FeatureFactoryConfig]]. + * @return a lists route. + */ + def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = if (featureFactoryConfig.getToggle("new-list-admin-routes").isEnabled) { newListsRouteADMFeature.makeRoute(featureFactoryConfig) } else { oldListsRouteADMFeature.makeRoute(featureFactoryConfig) } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/NewListsRouteADMFeature.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/NewListsRouteADMFeature.scala index ee69811032..22950aa079 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/NewListsRouteADMFeature.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/NewListsRouteADMFeature.scala @@ -38,10 +38,10 @@ object NewListsRouteADMFeature { } /** - * A [[Feature]] that provides the new list admin API route. - * - * @param routeData the [[KnoraRouteData]] to be used in constructing the route. - */ + * A [[Feature]] that provides the new list admin API route. + * + * @param routeData the [[KnoraRouteData]] to be used in constructing the route. + */ @Api(value = "lists (new endpoint)", produces = "application/json") @Path("/admin/lists") class NewListsRouteADMFeature(routeData: KnoraRouteData) @@ -61,42 +61,53 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) /* return all lists optionally filtered by project */ @Path("/{IRI}") - @ApiOperation(httpMethod = "GET", - response = classOf[ListsGetResponseADM], - value = "Get lists", - nickname = "newGetLists") + @ApiOperation( + httpMethod = "GET", + response = classOf[ListsGetResponseADM], + value = "Get lists", + nickname = "newGetLists" + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "X-Knora-Feature-Toggles", - value = "new-list-admin-routes:1 = on/off", - required = true, - dataType = "string", - paramType = "header"), - new ApiImplicitParam(name = "projectIri", - value = "IRI of the project", - required = true, - dataType = "string", - paramType = "query") - )) + new ApiImplicitParam( + name = "X-Knora-Feature-Toggles", + value = "new-list-admin-routes:1 = on/off", + required = true, + dataType = "string", + paramType = "header" + ), + new ApiImplicitParam( + name = "projectIri", + value = "IRI of the project", + required = true, + dataType = "string", + paramType = "query" + ) + ) + ) /* return all lists optionally filtered by project */ private def getLists(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath) { get { /* return all lists */ parameters("projectIri".?) { maybeProjectIri: Option[IRI] => requestContext => val projectIri = - stringFormatter.toOptionalIri(maybeProjectIri, - throw BadRequestException(s"Invalid param project IRI: $maybeProjectIri")) + stringFormatter.toOptionalIri( + maybeProjectIri, + throw BadRequestException(s"Invalid param project IRI: $maybeProjectIri") + ) val requestMessage: Future[ListsGetRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - ListsGetRequestADM(projectIri = projectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser) + } yield ListsGetRequestADM( + projectIri = projectIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -111,27 +122,35 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) } /* create a new list item (root or child node)*/ - @ApiOperation(value = "Add new list item", - nickname = "newAddListItem", - httpMethod = "POST", - response = classOf[ListGetResponseADM]) + @ApiOperation( + value = "Add new list item", + nickname = "newAddListItem", + httpMethod = "POST", + response = classOf[ListGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"list\" item to create", - required = true, - dataTypeClass = classOf[CreateListApiRequestADM], - paramType = "body"), - new ApiImplicitParam(name = "X-Knora-Feature-Toggles", - value = "new-list-admin-routes:1 = on/off", - required = true, - dataType = "string", - paramType = "header") - )) + new ApiImplicitParam( + name = "body", + value = "\"list\" item to create", + required = true, + dataTypeClass = classOf[CreateListApiRequestADM], + paramType = "body" + ), + new ApiImplicitParam( + name = "X-Knora-Feature-Toggles", + value = "new-list-admin-routes:1 = on/off", + required = true, + dataType = "string", + paramType = "header" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def createListItem(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath) { post { /* create a list item (root or child node) */ @@ -139,23 +158,24 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) val requestMessage = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) // Is parent node IRI given in the payload? - createRequest = if (apiRequest.parentNodeIri.isEmpty) { - // No, create a new list with given information of its root node. - ListCreateRequestADM( - createRootNode = apiRequest.escape, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) - } else { - // Yes, create a new child and attach it to the parent node. - ListChildNodeCreateRequestADM( - createChildNodeRequest = apiRequest.escape, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) - } + createRequest = + if (apiRequest.parentNodeIri.isEmpty) { + // No, create a new list with given information of its root node. + ListCreateRequestADM( + createRootNode = apiRequest.escape, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) + } else { + // Yes, create a new child and attach it to the parent node. + ListChildNodeCreateRequestADM( + createChildNodeRequest = apiRequest.escape, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) + } } yield createRequest RouteUtilADM.runJsonRoute( @@ -172,21 +192,28 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) /* get a node (root or child) */ @Path("/{IRI}") - @ApiOperation(value = "Get a list item", - nickname = "newGetlistItem", - httpMethod = "GET", - response = classOf[ListGetResponseADM]) + @ApiOperation( + value = "Get a list item", + nickname = "newGetlistItem", + httpMethod = "GET", + response = classOf[ListGetResponseADM] + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "X-Knora-Feature-Toggles", - value = "new-list-admin-routes:1 = on/off", - required = true, - dataType = "string", - paramType = "header"))) + new ApiImplicitParam( + name = "X-Knora-Feature-Toggles", + value = "new-list-admin-routes:1 = on/off", + required = true, + dataType = "string", + paramType = "header" + ) + ) + ) private def getListItem(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment) { iri => get { /* return a node, root or child, with all children */ @@ -196,8 +223,11 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) val requestMessage: Future[ListGetRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - ListGetRequestADM(iri = listIri, featureFactoryConfig = featureFactoryConfig, requestingUser = requestingUser) + } yield ListGetRequestADM( + iri = listIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -211,30 +241,38 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) } /** - * update list - */ + * update list + */ @Path("/{IRI}") - @ApiOperation(value = "Update basic node information", - nickname = "newPutListItem", - httpMethod = "PUT", - response = classOf[NodeInfoGetResponseADM]) + @ApiOperation( + value = "Update basic node information", + nickname = "newPutListItem", + httpMethod = "PUT", + response = classOf[NodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"list\" item to update", - required = true, - dataTypeClass = classOf[ChangeNodeInfoApiRequestADM], - paramType = "body"), - new ApiImplicitParam(name = "X-Knora-Feature-Toggles", - value = "new-list-admin-routes:1 = on/off", - required = true, - dataType = "string", - paramType = "header") - )) + new ApiImplicitParam( + name = "body", + value = "\"list\" item to update", + required = true, + dataTypeClass = classOf[ChangeNodeInfoApiRequestADM], + paramType = "body" + ), + new ApiImplicitParam( + name = "X-Knora-Feature-Toggles", + value = "new-list-admin-routes:1 = on/off", + required = true, + dataType = "string", + paramType = "header" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def updateListItem(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment) { iri => put { /* update existing list node (either root or child) */ @@ -244,14 +282,13 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) val requestMessage: Future[NodeInfoChangeRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - NodeInfoChangeRequestADM( - listIri = listIri, - changeNodeRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield NodeInfoChangeRequestADM( + listIri = listIri, + changeNodeRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -266,21 +303,28 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) } @Path("/{IRI}/info") - @ApiOperation(value = "Get basic node information", - nickname = "newGetNodeInfo", - httpMethod = "PUT", - response = classOf[RootNodeInfoGetResponseADM]) + @ApiOperation( + value = "Get basic node information", + nickname = "newGetNodeInfo", + httpMethod = "PUT", + response = classOf[RootNodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "X-Knora-Feature-Toggles", - value = "new-list-admin-routes:1 = on/off", - required = true, - dataType = "string", - paramType = "header"))) + new ApiImplicitParam( + name = "X-Knora-Feature-Toggles", + value = "new-list-admin-routes:1 = on/off", + required = true, + dataType = "string", + paramType = "header" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def getNodeInfo(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment / "info") { iri => get { @@ -291,10 +335,11 @@ class NewListsRouteADMFeature(routeData: KnoraRouteData) val requestMessage: Future[ListNodeInfoGetRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - ListNodeInfoGetRequestADM(iri = listIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser) + } yield ListNodeInfoGetRequestADM( + iri = listIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala index f458d1cdf7..4fd8c658ba 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala @@ -38,10 +38,10 @@ object OldListsRouteADMFeature { } /** - * A [[Feature]] that provides the old list admin API route. - * - * @param routeData the [[KnoraRouteData]] to be used in constructing the route. - */ + * A [[Feature]] that provides the old list admin API route. + * + * @param routeData the [[KnoraRouteData]] to be used in constructing the route. + */ @Api(value = "lists (old endpoint)", produces = "application/json") @Path("/admin/lists") class OldListsRouteADMFeature(routeData: KnoraRouteData) @@ -66,27 +66,29 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) /* return all lists optionally filtered by project */ private def getLists(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath) { get { /* return all lists */ parameters("projectIri".?) { maybeProjectIri: Option[IRI] => requestContext => val projectIri = - stringFormatter.toOptionalIri(maybeProjectIri, - throw BadRequestException(s"Invalid param project IRI: $maybeProjectIri")) + stringFormatter.toOptionalIri( + maybeProjectIri, + throw BadRequestException(s"Invalid param project IRI: $maybeProjectIri") + ) val requestMessage: Future[ListsGetRequestADM] = for { requestingUser <- getUserADM( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ListsGetRequestADM( - projectIri = projectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ListsGetRequestADM( + projectIri = projectIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -101,22 +103,28 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) } /* create a new list (root node) */ - @ApiOperation(value = "Add new list", - nickname = "addList", - httpMethod = "POST", - response = classOf[ListGetResponseADM]) + @ApiOperation( + value = "Add new list", + nickname = "addList", + httpMethod = "POST", + response = classOf[ListGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"list\" to create", - required = true, - dataTypeClass = classOf[CreateListApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"list\" to create", + required = true, + dataTypeClass = classOf[CreateListApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def createList(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath) { post { /* create a list */ @@ -126,13 +134,12 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ListCreateRequestADM( - createRootNode = apiRequest.escape, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield ListCreateRequestADM( + createRootNode = apiRequest.escape, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -152,7 +159,8 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def getListOrNode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment) { iri => get { /* return a list (a graph with all list nodes) */ @@ -165,12 +173,11 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ListGetRequestADM( - iri = listIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ListGetRequestADM( + iri = listIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -184,25 +191,31 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) } /** - * update list - */ + * update list + */ @Path("/{IRI}") - @ApiOperation(value = "Update basic list information", - nickname = "putList", - httpMethod = "PUT", - response = classOf[RootNodeInfoGetResponseADM]) + @ApiOperation( + value = "Update basic list information", + nickname = "putList", + httpMethod = "PUT", + response = classOf[RootNodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"list\" to update", - required = true, - dataTypeClass = classOf[ChangeNodeInfoApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"list\" to update", + required = true, + dataTypeClass = classOf[ChangeNodeInfoApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def updateList(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment) { iri => put { /* update existing list node (either root or child) */ @@ -211,14 +224,13 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri")) val requestMessage: Future[NodeInfoChangeRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - NodeInfoChangeRequestADM( - listIri = listIri, - changeNodeRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield NodeInfoChangeRequestADM( + listIri = listIri, + changeNodeRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -233,25 +245,31 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) } /** - * create a new child node - */ + * create a new child node + */ @Path("/{IRI}") - @ApiOperation(value = "Add new node", - nickname = "addListNode", - httpMethod = "POST", - response = classOf[ChildNodeInfoGetResponseADM]) + @ApiOperation( + value = "Add new node", + nickname = "addListNode", + httpMethod = "POST", + response = classOf[ChildNodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"node\" to create", - required = true, - dataTypeClass = classOf[CreateNodeApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"node\" to create", + required = true, + dataTypeClass = classOf[CreateNodeApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def createListChildNode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment) { iri => post { @@ -264,13 +282,12 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ListChildNodeCreateRequestADM( - createChildNodeRequest = apiRequest.escape, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield ListChildNodeCreateRequestADM( + createChildNodeRequest = apiRequest.escape, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -293,10 +310,11 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri")) val requestMessage: Future[ListNodeInfoGetRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - ListNodeInfoGetRequestADM(iri = listIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser) + } yield ListNodeInfoGetRequestADM( + iri = listIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -322,12 +340,11 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ListNodeInfoGetRequestADM( - iri = listIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ListNodeInfoGetRequestADM( + iri = listIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala index 399eb6d58a..c00ef404b1 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala @@ -37,10 +37,10 @@ object UpdateListItemsRouteADM { } /** - * A [[Feature]] that provides routes to delete list items. - * - * @param routeData the [[KnoraRouteData]] to be used in constructing the route. - */ + * A [[Feature]] that provides routes to delete list items. + * + * @param routeData the [[KnoraRouteData]] to be used in constructing the route. + */ class UpdateListItemsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Feature @@ -56,25 +56,31 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) updateNodePosition(featureFactoryConfig) /** - * update node name - */ + * update node name + */ @Path("/{IRI}/name") - @ApiOperation(value = "Update Node Name", - nickname = "putNodeName", - httpMethod = "PUT", - response = classOf[NodeInfoGetResponseADM]) + @ApiOperation( + value = "Update Node Name", + nickname = "putNodeName", + httpMethod = "PUT", + response = classOf[NodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"node name\" to update", - required = true, - dataTypeClass = classOf[ChangeNodeNameApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"node name\" to update", + required = true, + dataTypeClass = classOf[ChangeNodeNameApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def updateNodeName(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment / "name") { iri => put { @@ -85,14 +91,13 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) val requestMessage: Future[NodeNameChangeRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - NodeNameChangeRequestADM( - nodeIri = nodeIri, - changeNodeNameRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield NodeNameChangeRequestADM( + nodeIri = nodeIri, + changeNodeNameRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -107,25 +112,31 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) } /** - * update node labels - */ + * update node labels + */ @Path("/{IRI}/labels") - @ApiOperation(value = "Update Node Labels", - nickname = "putNodeLabels", - httpMethod = "PUT", - response = classOf[NodeInfoGetResponseADM]) + @ApiOperation( + value = "Update Node Labels", + nickname = "putNodeLabels", + httpMethod = "PUT", + response = classOf[NodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"node labels\" to update", - required = true, - dataTypeClass = classOf[ChangeNodeLabelsApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"node labels\" to update", + required = true, + dataTypeClass = classOf[ChangeNodeLabelsApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def updateNodeLabels(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment / "labels") { iri => put { @@ -136,14 +147,13 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) val requestMessage: Future[NodeLabelsChangeRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - NodeLabelsChangeRequestADM( - nodeIri = nodeIri, - changeNodeLabelsRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield NodeLabelsChangeRequestADM( + nodeIri = nodeIri, + changeNodeLabelsRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -158,25 +168,31 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) } /** - * update node comments - */ + * update node comments + */ @Path("/{IRI}/comments") - @ApiOperation(value = "Update Node Comments", - nickname = "putNodeComments", - httpMethod = "PUT", - response = classOf[NodeInfoGetResponseADM]) + @ApiOperation( + value = "Update Node Comments", + nickname = "putNodeComments", + httpMethod = "PUT", + response = classOf[NodeInfoGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"node comments\" to update", - required = true, - dataTypeClass = classOf[ChangeNodeCommentsApiRequestADM], - paramType = "body"), - )) + new ApiImplicitParam( + name = "body", + value = "\"node comments\" to update", + required = true, + dataTypeClass = classOf[ChangeNodeCommentsApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def updateNodeComments(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment / "comments") { iri => put { @@ -187,14 +203,13 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) val requestMessage: Future[NodeCommentsChangeRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - NodeCommentsChangeRequestADM( - nodeIri = nodeIri, - changeNodeCommentsRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield NodeCommentsChangeRequestADM( + nodeIri = nodeIri, + changeNodeCommentsRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -209,25 +224,31 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) } /** - * update node position - */ + * update node position + */ @Path("/{IRI}/position") - @ApiOperation(value = "Update Node Position", - nickname = "putNodePosition", - httpMethod = "PUT", - response = classOf[ListGetResponseADM]) + @ApiOperation( + value = "Update Node Position", + nickname = "putNodePosition", + httpMethod = "PUT", + response = classOf[ListGetResponseADM] + ) @ApiImplicitParams( Array( - new ApiImplicitParam(name = "body", - value = "\"node position\" to update", - required = true, - dataTypeClass = classOf[ChangeNodeCommentsApiRequestADM], - paramType = "body") - )) + new ApiImplicitParam( + name = "body", + value = "\"node position\" to update", + required = true, + dataTypeClass = classOf[ChangeNodeCommentsApiRequestADM], + paramType = "body" + ) + ) + ) @ApiResponses( Array( new ApiResponse(code = 500, message = "Internal server error") - )) + ) + ) private def updateNodePosition(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath / Segment / "position") { iri => put { @@ -238,14 +259,13 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) val requestMessage: Future[NodePositionChangeRequestADM] = for { requestingUser <- getUserADM(requestContext, featureFactoryConfig) - } yield - NodePositionChangeRequestADM( - nodeIri = nodeIri, - changeNodePositionRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield NodePositionChangeRequestADM( + nodeIri = nodeIri, + changeNodePositionRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala index 1ee5433fb5..ff418c98bd 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala @@ -45,15 +45,15 @@ class CreatePermissionRouteADM(routeData: KnoraRouteData) import CreatePermissionRouteADM._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = createAdministrativePermission(featureFactoryConfig) ~ createDefaultObjectAccessPermission(featureFactoryConfig) /** - * Create a new administrative permission - */ + * Create a new administrative permission + */ private def createAdministrativePermission(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / "ap") { post { @@ -64,13 +64,12 @@ class CreatePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - AdministrativePermissionCreateRequestADM( - createRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield AdministrativePermissionCreateRequestADM( + createRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -85,8 +84,8 @@ class CreatePermissionRouteADM(routeData: KnoraRouteData) } /** - * Create default object access permission - */ + * Create default object access permission + */ private def createDefaultObjectAccessPermission(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / "doap") { post { @@ -97,13 +96,12 @@ class CreatePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - DefaultObjectAccessPermissionCreateRequestADM( - createRequest = apiRequest, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield DefaultObjectAccessPermissionCreateRequestADM( + createRequest = apiRequest, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala index 0c157097f9..d3f6f56de3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala @@ -44,14 +44,14 @@ class DeletePermissionRouteADM(routeData: KnoraRouteData) import DeletePermissionRouteADM._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = deletePermission(featureFactoryConfig) /** - * Delete a permission - */ + * Delete a permission + */ private def deletePermission(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / Segment) { iri => delete { requestContext => @@ -60,12 +60,11 @@ class DeletePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - PermissionDeleteRequestADM( - permissionIri = iri, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield PermissionDeleteRequestADM( + permissionIri = iri, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala index 352b865a2a..b94be96540 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala @@ -43,8 +43,8 @@ class GetPermissionsRouteADM(routeData: KnoraRouteData) import GetPermissionsRouteADM._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = getAdministrativePermissionForProjectGroup(featureFactoryConfig) ~ getAdministrativePermissionsForProject(featureFactoryConfig) ~ @@ -80,12 +80,11 @@ class GetPermissionsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - AdministrativePermissionsForProjectGetRequestADM( - projectIri = projectIri, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield AdministrativePermissionsForProjectGetRequestADM( + projectIri = projectIri, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -106,12 +105,11 @@ class GetPermissionsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - DefaultObjectAccessPermissionsForProjectGetRequestADM( - projectIri = projectIri, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield DefaultObjectAccessPermissionsForProjectGetRequestADM( + projectIri = projectIri, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -132,13 +130,12 @@ class GetPermissionsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - PermissionsForProjectGetRequestADM( - projectIri = projectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield PermissionsForProjectGetRequestADM( + projectIri = projectIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala index 4f66d02565..18c21ba03b 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala @@ -44,8 +44,8 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) import UpdatePermissionRouteADM._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = updatePermissionGroup(featureFactoryConfig) ~ updatePermissionHasPermissions(featureFactoryConfig) ~ @@ -53,8 +53,8 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) updatePermissionProperty(featureFactoryConfig) /** - * Update a permission's group - */ + * Update a permission's group + */ private def updatePermissionGroup(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / Segment / "group") { iri => put { @@ -67,13 +67,12 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - PermissionChangeGroupRequestADM( - permissionIri = permissionIri, - changePermissionGroupRequest = apiRequest, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield PermissionChangeGroupRequestADM( + permissionIri = permissionIri, + changePermissionGroupRequest = apiRequest, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -88,8 +87,8 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) } /** - * Update a permission's set of hasPermissions. - */ + * Update a permission's set of hasPermissions. + */ private def updatePermissionHasPermissions(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / Segment / "hasPermissions") { iri => put { @@ -102,13 +101,12 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - PermissionChangeHasPermissionsRequestADM( - permissionIri = permissionIri, - changePermissionHasPermissionsRequest = apiRequest, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield PermissionChangeHasPermissionsRequestADM( + permissionIri = permissionIri, + changePermissionHasPermissionsRequest = apiRequest, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -123,8 +121,8 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) } /** - * Update a doap permission by setting it for a new resource class - */ + * Update a doap permission by setting it for a new resource class + */ private def updatePermissionResourceClass(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / Segment / "resourceClass") { iri => put { @@ -137,13 +135,12 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - PermissionChangeResourceClassRequestADM( - permissionIri = permissionIri, - changePermissionResourceClassRequest = apiRequest, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield PermissionChangeResourceClassRequestADM( + permissionIri = permissionIri, + changePermissionResourceClassRequest = apiRequest, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, @@ -158,8 +155,8 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) } /** - * Update a doap permission by setting it for a new property class - */ + * Update a doap permission by setting it for a new property class + */ private def updatePermissionProperty(featureFactoryConfig: FeatureFactoryConfig): Route = path(PermissionsBasePath / Segment / "property") { iri => put { @@ -172,13 +169,12 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - PermissionChangePropertyRequestADM( - permissionIri = permissionIri, - changePermissionPropertyRequest = apiRequest, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID() - ) + } yield PermissionChangePropertyRequestADM( + permissionIri = permissionIri, + changePermissionPropertyRequest = apiRequest, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID() + ) RouteUtilADM.runJsonRoute( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala index 84e7c6bbc2..541b5481d9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala @@ -32,55 +32,51 @@ import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData} /** - * A route used for faking the image server. - */ + * A route used for faking the image server. + */ class AssetsRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v1" / "assets" / Remaining) { assetId => get { requestContext => - { - requestContext.complete { - log.debug(s"got request: ${requestContext.toString}") + requestContext.complete { + log.debug(s"got request: ${requestContext.toString}") - val (width, height, text) = assetId match { - case string if string.contains("big".toCharArray) => (1024, 1024, assetId) - case _ => (16, 16, assetId) - } + val (width, height, text) = assetId match { + case string if string.contains("big".toCharArray) => (1024, 1024, assetId) + case _ => (16, 16, assetId) + } - val dummyImage = if (text.contains("http://rdfh.ch/0a077e5a93bf".toCharArray)) { - //calling this should get me here: http://localhost:3333/v1/assets/http%3A%2F%2Frdfh.ch%2F0a077e5a93bf - val tmpImage = ImageIO.read(Paths.get("_assets/4KUN_7_000169.png").toFile) - tmpImage - } else { - /* make dummy images with the image name as content */ - val tmpImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) - val g: Graphics = tmpImage.getGraphics - //g.setColor(new Color(0,0,0)) //background color - g.setColor(new Color(255, 125, 65)) //background color - g.fillRect(0, 0, width, height) - g.setColor(new Color(0, 0, 0)) // foreground color - g.setFont(g.getFont.deriveFont(Font.BOLD, 8f)) - g.drawString(text, 0, height / 2) - g.dispose() - tmpImage - } + val dummyImage = if (text.contains("http://rdfh.ch/0a077e5a93bf".toCharArray)) { + //calling this should get me here: http://localhost:3333/v1/assets/http%3A%2F%2Frdfh.ch%2F0a077e5a93bf + val tmpImage = ImageIO.read(Paths.get("_assets/4KUN_7_000169.png").toFile) + tmpImage + } else { + /* make dummy images with the image name as content */ + val tmpImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) + val g: Graphics = tmpImage.getGraphics + //g.setColor(new Color(0,0,0)) //background color + g.setColor(new Color(255, 125, 65)) //background color + g.fillRect(0, 0, width, height) + g.setColor(new Color(0, 0, 0)) // foreground color + g.setFont(g.getFont.deriveFont(Font.BOLD, 8f)) + g.drawString(text, 0, height / 2) + g.dispose() + tmpImage + } - val baos: ByteArrayOutputStream = new ByteArrayOutputStream() - ImageIO.write(dummyImage, "PNG", baos) - baos.flush() + val baos: ByteArrayOutputStream = new ByteArrayOutputStream() + ImageIO.write(dummyImage, "PNG", baos) + baos.flush() - val byteArr: Array[Byte] = baos.toByteArray - baos.close() + val byteArr: Array[Byte] = baos.toByteArray + baos.close() - HttpResponse(entity = HttpEntity(MediaTypes.`image/png`, byteArr)) - } + HttpResponse(entity = HttpEntity(MediaTypes.`image/png`, byteArr)) } } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala index d5cfc9378f..f84a1e8216 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala @@ -25,62 +25,52 @@ import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData} /** - * A route providing authentication support. It allows the creation of "sessions", which is used in the SALSAH app. - */ + * A route providing authentication support. It allows the creation of "sessions", which is used in the SALSAH app. + */ class AuthenticationRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v1" / "authenticate") { get { requestContext => - { - requestContext.complete { - doAuthenticateV1( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } + requestContext.complete { + doAuthenticateV1( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } } } ~ path("v1" / "session") { get { requestContext => - { - requestContext.complete { - val params = requestContext.request.uri.query().toMap - if (params.contains("logout")) { - doLogoutV2(requestContext) - } else if (params.contains("login")) { - doLoginV1( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } else { - doAuthenticateV1( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } - } - } - } ~ post { requestContext => - { - requestContext.complete { + requestContext.complete { + val params = requestContext.request.uri.query().toMap + if (params.contains("logout")) { + doLogoutV2(requestContext) + } else if (params.contains("login")) { doLoginV1( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) + } else { + doAuthenticateV1( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } } + } ~ post { requestContext => + requestContext.complete { + doLoginV1( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } } ~ delete { requestContext => - { - requestContext.complete { - doLogoutV2(requestContext) - } + requestContext.complete { + doLogoutV2(requestContext) } } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala index 7d918ddba6..d7fc481a1c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala @@ -26,15 +26,14 @@ import org.knora.webapi.messages.v1.responder.ckanmessages.CkanRequestV1 import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV1} /** - * A route used to serve data to CKAN. It is used be the Ckan instance running under http://data.humanities.ch. - */ + * A route used to serve data to CKAN. It is used be the Ckan instance running under http://data.humanities.ch. + */ class CkanRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v1" / "ckan") { get { requestContext => val requestMessage = for { @@ -46,14 +45,13 @@ class CkanRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with project: Option[Seq[String]] = params.get("project").map(_.split(",")) limit: Option[Int] = params.get("limit").map(_.toInt) info: Boolean = params.getOrElse("info", false) == true - } yield - CkanRequestV1( - projects = project, - limit = limit, - info = info, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile - ) + } yield CkanRequestV1( + projects = project, + limit = limit, + info = info, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -64,5 +62,4 @@ class CkanRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with ) } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala index d316ba8965..77dcc8adbf 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala @@ -28,13 +28,13 @@ import org.knora.webapi.messages.v1.responder.listmessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV1} /** - * Provides API routes that deal with lists. - */ + * Provides API routes that deal with lists. + */ class ListsRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { val stringFormatter = StringFormatter.getGeneralInstance @@ -46,8 +46,10 @@ class ListsRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - listIri = stringFormatter.validateAndEscapeIri(iri, - throw BadRequestException(s"Invalid param list IRI: $iri")) + listIri = stringFormatter.validateAndEscapeIri( + iri, + throw BadRequestException(s"Invalid param list IRI: $iri") + ) requestMessage = requestContext.request.uri.query().get("reqtype") match { case Some("node") => NodePathGetRequestV1(listIri, userProfile) @@ -72,8 +74,10 @@ class ListsRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - selIri = stringFormatter.validateAndEscapeIri(iri, - throw BadRequestException(s"Invalid param list IRI: $iri")) + selIri = stringFormatter.validateAndEscapeIri( + iri, + throw BadRequestException(s"Invalid param list IRI: $iri") + ) requestMessage = requestContext.request.uri.query().get("reqtype") match { case Some("node") => NodePathGetRequestV1(selIri, userProfile) diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala index 3e4edf4791..dd2ccf9971 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala @@ -32,10 +32,9 @@ class ProjectsRouteV1(routeData: KnoraRouteData) with ProjectV1JsonProtocol { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v1" / "projects") { get { /* returns all projects */ @@ -45,11 +44,10 @@ class ProjectsRouteV1(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - } yield - ProjectsGetRequestV1( - featureFactoryConfig = featureFactoryConfig, - userProfile = Some(userProfile) - ) + } yield ProjectsGetRequestV1( + featureFactoryConfig = featureFactoryConfig, + userProfile = Some(userProfile) + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -70,12 +68,11 @@ class ProjectsRouteV1(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - } yield - ProjectInfoByShortnameGetRequestV1( - shortname = shortNameDec, - featureFactoryConfig = featureFactoryConfig, - userProfileV1 = Some(userProfile) - ) + } yield ProjectInfoByShortnameGetRequestV1( + shortname = shortNameDec, + featureFactoryConfig = featureFactoryConfig, + userProfileV1 = Some(userProfile) + ) } else { // identify project by iri. this is the default case. val checkedProjectIri = stringFormatter.validateAndEscapeIri(value, throw BadRequestException(s"Invalid project IRI $value")) @@ -84,12 +81,11 @@ class ProjectsRouteV1(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - } yield - ProjectInfoByIRIGetRequestV1( - iri = checkedProjectIri, - featureFactoryConfig = featureFactoryConfig, - userProfileV1 = Some(userProfile) - ) + } yield ProjectInfoByIRIGetRequestV1( + iri = checkedProjectIri, + featureFactoryConfig = featureFactoryConfig, + userProfileV1 = Some(userProfile) + ) } RouteUtilV1.runJsonRouteWithFuture( @@ -102,5 +98,4 @@ class ProjectsRouteV1(routeData: KnoraRouteData) } } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala index eda5cfcf05..7cbe814a7a 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala @@ -27,13 +27,13 @@ import org.knora.webapi.messages.v1.responder.ontologymessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV1} /** - * Provides a spray-routing function for API routes that deal with resource types. - */ + * Provides a spray-routing function for API routes that deal with resource types. + */ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { path("v1" / "resourcetypes" / Segment) { iri => @@ -47,7 +47,8 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa // TODO: Check that this is the IRI of a resource type and not just any IRI resourceTypeIri = stringFormatter.validateAndEscapeIri( iri, - throw BadRequestException(s"Invalid resource class IRI: $iri")) + throw BadRequestException(s"Invalid resource class IRI: $iri") + ) } yield ResourceTypeGetRequestV1(resourceTypeIri, userProfile) @@ -68,8 +69,10 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa ) params = requestContext.request.uri.query().toMap - vocabularyId = params.getOrElse("vocabulary", - throw BadRequestException("Required param vocabulary is missing")) + vocabularyId = params.getOrElse( + "vocabulary", + throw BadRequestException("Required param vocabulary is missing") + ) namedGraphIri = vocabularyId match { case "0" => None // if param vocabulary is set to 0, query all named graphs @@ -77,15 +80,16 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa Some( stringFormatter.validateAndEscapeIri( vocabularyId, - throw BadRequestException(s"Invalid vocabulary IRI: $vocabularyId"))) + throw BadRequestException(s"Invalid vocabulary IRI: $vocabularyId") + ) + ) } - } yield - ResourceTypesForNamedGraphGetRequestV1( - namedGraph = namedGraphIri, - featureFactoryConfig = featureFactoryConfig, - userADM = userADM - ) + } yield ResourceTypesForNamedGraphGetRequestV1( + namedGraph = namedGraphIri, + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -111,40 +115,41 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa // either the vocabulary or the restype param is set, but not both _ = if (vocabularyId.nonEmpty && resourcetypeId.nonEmpty) throw BadRequestException("Both vocabulary and restype params are set, only one is allowed") - } yield - vocabularyId match { - case Some("0") => // 0 means that all named graphs should be queried - PropertyTypesForNamedGraphGetRequestV1( - namedGraph = None, - featureFactoryConfig = featureFactoryConfig, - userADM = userADM - ) - - case Some(vocId) => - val namedGraphIri = stringFormatter.validateAndEscapeIri( - vocId, - throw BadRequestException(s"Invalid vocabulary IRI: $vocabularyId")) - PropertyTypesForNamedGraphGetRequestV1( - namedGraph = Some(namedGraphIri), - featureFactoryConfig = featureFactoryConfig, - userADM = userADM - ) - - case None => // no vocabulary id given, check for restype - resourcetypeId match { - case Some(restypeId) => // get property types for given resource type - val resourceClassIri = stringFormatter.validateAndEscapeIri( - restypeId, - throw BadRequestException(s"Invalid vocabulary IRI: $restypeId")) - PropertyTypesForResourceTypeGetRequestV1(restypeId, userADM) - case None => // no params given, get all property types (behaves like vocbulary=0) - PropertyTypesForNamedGraphGetRequestV1( - namedGraph = None, - featureFactoryConfig = featureFactoryConfig, - userADM = userADM - ) - } - } + } yield vocabularyId match { + case Some("0") => // 0 means that all named graphs should be queried + PropertyTypesForNamedGraphGetRequestV1( + namedGraph = None, + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) + + case Some(vocId) => + val namedGraphIri = stringFormatter.validateAndEscapeIri( + vocId, + throw BadRequestException(s"Invalid vocabulary IRI: $vocabularyId") + ) + PropertyTypesForNamedGraphGetRequestV1( + namedGraph = Some(namedGraphIri), + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) + + case None => // no vocabulary id given, check for restype + resourcetypeId match { + case Some(restypeId) => // get property types for given resource type + val resourceClassIri = stringFormatter.validateAndEscapeIri( + restypeId, + throw BadRequestException(s"Invalid vocabulary IRI: $restypeId") + ) + PropertyTypesForResourceTypeGetRequestV1(restypeId, userADM) + case None => // no params given, get all property types (behaves like vocbulary=0) + PropertyTypesForNamedGraphGetRequestV1( + namedGraph = None, + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) + } + } RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -162,11 +167,10 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - NamedGraphsGetRequestV1( - featureFactoryConfig = featureFactoryConfig, - userADM = userADM - ) + } yield NamedGraphsGetRequestV1( + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -184,11 +188,10 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - LoadOntologiesRequestV1( - featureFactoryConfig = featureFactoryConfig, - userADM = userADM - ) + } yield LoadOntologiesRequestV1( + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -209,7 +212,8 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa // TODO: Check that this is the IRI of a resource type and not just any IRI resourceClassIri = stringFormatter.validateAndEscapeIri( iri, - throw BadRequestException(s"Invalid resource class IRI: $iri")) + throw BadRequestException(s"Invalid resource class IRI: $iri") + ) } yield SubClassesGetRequestV1(resourceClassIri, userADM) RouteUtilV1.runJsonRouteWithFuture( diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala index aeaafc51a1..d480dc6d73 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala @@ -69,21 +69,23 @@ import scala.util.{Failure, Success, Try} import scala.xml._ /** - * Provides API routes that deal with resources. - */ + * Provides API routes that deal with resources. + */ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { // A scala.xml.PrettyPrinter for formatting generated XML import schemas. private val xmlPrettyPrinter = new scala.xml.PrettyPrinter(width = 160, step = 4) /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - def makeResourceRequestMessage(resIri: String, - resinfo: Boolean, - requestType: String, - userADM: UserADM): ResourcesResponderRequestV1 = { + def makeResourceRequestMessage( + resIri: String, + resinfo: Boolean, + requestType: String, + userADM: UserADM + ): ResourcesResponderRequestV1 = { val validResIri = stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: $resIri")) @@ -121,188 +123,206 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } - def makeResourceSearchRequestMessage(searchString: String, - resourceTypeIri: Option[IRI], - numberOfProps: Int, - limitOfResults: Int, - userProfile: UserADM): ResourceSearchGetRequestV1 = { - ResourceSearchGetRequestV1(searchString = searchString, - resourceTypeIri = resourceTypeIri, - numberOfProps = numberOfProps, - limitOfResults = limitOfResults, - userProfile = userProfile) - } + def makeResourceSearchRequestMessage( + searchString: String, + resourceTypeIri: Option[IRI], + numberOfProps: Int, + limitOfResults: Int, + userProfile: UserADM + ): ResourceSearchGetRequestV1 = + ResourceSearchGetRequestV1( + searchString = searchString, + resourceTypeIri = resourceTypeIri, + numberOfProps = numberOfProps, + limitOfResults = limitOfResults, + userProfile = userProfile + ) - def valuesToCreate(properties: Map[IRI, Seq[CreateResourceValueV1]], - acceptStandoffLinksToClientIDs: Boolean, - userProfile: UserADM): Map[IRI, Future[Seq[CreateValueV1WithComment]]] = { - properties - .map { - case (propIri: IRI, values: Seq[CreateResourceValueV1]) => - (stringFormatter.validateAndEscapeIri(propIri, throw BadRequestException(s"Invalid property IRI $propIri")), - values.map { givenValue: CreateResourceValueV1 => - givenValue.getValueClassIri match { - // create corresponding UpdateValueV1 - - case OntologyConstants.KnoraBase.TextValue => - val richtext: CreateRichtextV1 = givenValue.richtext_value.get - - // check if text has markup - if (richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty) { - // simple text - - Future( - CreateValueV1WithComment( - TextValueSimpleV1( - utf8str = stringFormatter.toSparqlEncodedString( - richtext.utf8str.get, - throw BadRequestException(s"Invalid text: '${richtext.utf8str.get}'")), - language = richtext.language - ), - givenValue.comment - )) - - } else if (richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty) { - // XML: text with markup - - val mappingIri = stringFormatter.validateAndEscapeIri( - richtext.mapping_id.get, - throw BadRequestException(s"mapping_id ${richtext.mapping_id.get} is invalid")) - - for { - - textWithStandoffTags: TextWithStandoffTagsV2 <- RouteUtilV1.convertXMLtoStandoffTagV1( - xml = richtext.xml.get, - mappingIri = mappingIri, - acceptStandoffLinksToClientIDs = acceptStandoffLinksToClientIDs, - userProfile = userProfile, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log - ) - - // collect the resource references from the linking standoff nodes - resourceReferences: Set[IRI] = stringFormatter.getResourceIrisFromStandoffTags( - textWithStandoffTags.standoffTagV2) - - } yield - CreateValueV1WithComment( - TextValueWithStandoffV1( - utf8str = stringFormatter.toSparqlEncodedString( - textWithStandoffTags.text, - throw InconsistentRepositoryDataException( - "utf8str for TextValue contains invalid characters")), - language = richtext.language, - resource_reference = resourceReferences, - standoff = textWithStandoffTags.standoffTagV2, - mappingIri = textWithStandoffTags.mapping.mappingIri, - mapping = textWithStandoffTags.mapping.mapping - ), - givenValue.comment - ) - - } else { - throw BadRequestException("invalid parameters given for TextValueV1") - } - - case OntologyConstants.KnoraBase.LinkValue => - (givenValue.link_value, givenValue.link_to_client_id) match { - case (Some(targetIri: IRI), None) => - // This is a link to an existing Knora IRI, so make sure the IRI is valid. - val validatedTargetIri = stringFormatter.validateAndEscapeIri( - targetIri, - throw BadRequestException(s"Invalid Knora resource IRI: $targetIri")) - Future(CreateValueV1WithComment(LinkUpdateV1(validatedTargetIri), givenValue.comment)) - - case (None, Some(clientIDForTargetResource: String)) => - // This is a link to the client's ID for a resource that hasn't been created yet. - Future( - CreateValueV1WithComment(LinkToClientIDUpdateV1(clientIDForTargetResource), - givenValue.comment)) - - case (_, _) => throw AssertionException(s"Invalid link: $givenValue") - } - - case OntologyConstants.KnoraBase.IntValue => - Future(CreateValueV1WithComment(IntegerValueV1(givenValue.int_value.get), givenValue.comment)) - - case OntologyConstants.KnoraBase.DecimalValue => - Future(CreateValueV1WithComment(DecimalValueV1(givenValue.decimal_value.get), givenValue.comment)) - - case OntologyConstants.KnoraBase.BooleanValue => - Future(CreateValueV1WithComment(BooleanValueV1(givenValue.boolean_value.get), givenValue.comment)) - - case OntologyConstants.KnoraBase.UriValue => - val uriValue = stringFormatter.validateAndEscapeIri( - givenValue.uri_value.get, - throw BadRequestException(s"Invalid URI: ${givenValue.uri_value.get}")) - Future(CreateValueV1WithComment(UriValueV1(uriValue), givenValue.comment)) - - case OntologyConstants.KnoraBase.DateValue => - val dateVal: JulianDayNumberValueV1 = - DateUtilV1.createJDNValueV1FromDateString(givenValue.date_value.get) - Future(CreateValueV1WithComment(dateVal, givenValue.comment)) - - case OntologyConstants.KnoraBase.ColorValue => - val colorValue = stringFormatter.validateColor( - givenValue.color_value.get, - throw BadRequestException(s"Invalid color value: ${givenValue.color_value.get}")) - Future(CreateValueV1WithComment(ColorValueV1(colorValue), givenValue.comment)) - - case OntologyConstants.KnoraBase.GeomValue => - val geometryValue = stringFormatter.validateGeometryString( - givenValue.geom_value.get, - throw BadRequestException(s"Invalid geometry value: ${givenValue.geom_value.get}")) - Future(CreateValueV1WithComment(GeomValueV1(geometryValue), givenValue.comment)) - - case OntologyConstants.KnoraBase.ListValue => - val listNodeIri = stringFormatter.validateAndEscapeIri( - givenValue.hlist_value.get, - throw BadRequestException(s"Invalid value IRI: ${givenValue.hlist_value.get}")) - Future(CreateValueV1WithComment(HierarchicalListValueV1(listNodeIri), givenValue.comment)) - - case OntologyConstants.KnoraBase.IntervalValue => - val timeVals: Seq[BigDecimal] = givenValue.interval_value.get - if (timeVals.length != 2) throw BadRequestException("parameters for interval_value invalid") - Future(CreateValueV1WithComment(IntervalValueV1(timeVals.head, timeVals(1)), givenValue.comment)) - - case OntologyConstants.KnoraBase.TimeValue => - val timeValStr: String = givenValue.time_value.get - val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( - timeValStr, - throw BadRequestException(s"Invalid timestamp: $timeValStr")) - Future(CreateValueV1WithComment(TimeValueV1(timeStamp), givenValue.comment)) - - case OntologyConstants.KnoraBase.GeonameValue => - Future(CreateValueV1WithComment(GeonameValueV1(givenValue.geoname_value.get), givenValue.comment)) - - case _ => throw BadRequestException(s"No value submitted") - - } - - }) - } - .map { - // transform Seq of Futures to a Future of a Seq - case (propIri: IRI, values: Seq[Future[CreateValueV1WithComment]]) => - (propIri, Future.sequence(values)) - } + def valuesToCreate( + properties: Map[IRI, Seq[CreateResourceValueV1]], + acceptStandoffLinksToClientIDs: Boolean, + userProfile: UserADM + ): Map[IRI, Future[Seq[CreateValueV1WithComment]]] = { + properties.map { case (propIri: IRI, values: Seq[CreateResourceValueV1]) => + ( + stringFormatter.validateAndEscapeIri(propIri, throw BadRequestException(s"Invalid property IRI $propIri")), + values.map { givenValue: CreateResourceValueV1 => + givenValue.getValueClassIri match { + // create corresponding UpdateValueV1 + + case OntologyConstants.KnoraBase.TextValue => + val richtext: CreateRichtextV1 = givenValue.richtext_value.get + + // check if text has markup + if (richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty) { + // simple text + + Future( + CreateValueV1WithComment( + TextValueSimpleV1( + utf8str = stringFormatter.toSparqlEncodedString( + richtext.utf8str.get, + throw BadRequestException(s"Invalid text: '${richtext.utf8str.get}'") + ), + language = richtext.language + ), + givenValue.comment + ) + ) + + } else if (richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty) { + // XML: text with markup + + val mappingIri = stringFormatter.validateAndEscapeIri( + richtext.mapping_id.get, + throw BadRequestException(s"mapping_id ${richtext.mapping_id.get} is invalid") + ) + + for { + + textWithStandoffTags: TextWithStandoffTagsV2 <- RouteUtilV1.convertXMLtoStandoffTagV1( + xml = richtext.xml.get, + mappingIri = mappingIri, + acceptStandoffLinksToClientIDs = acceptStandoffLinksToClientIDs, + userProfile = userProfile, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log + ) + + // collect the resource references from the linking standoff nodes + resourceReferences: Set[IRI] = stringFormatter.getResourceIrisFromStandoffTags( + textWithStandoffTags.standoffTagV2 + ) + + } yield CreateValueV1WithComment( + TextValueWithStandoffV1( + utf8str = stringFormatter.toSparqlEncodedString( + textWithStandoffTags.text, + throw InconsistentRepositoryDataException("utf8str for TextValue contains invalid characters") + ), + language = richtext.language, + resource_reference = resourceReferences, + standoff = textWithStandoffTags.standoffTagV2, + mappingIri = textWithStandoffTags.mapping.mappingIri, + mapping = textWithStandoffTags.mapping.mapping + ), + givenValue.comment + ) + + } else { + throw BadRequestException("invalid parameters given for TextValueV1") + } + + case OntologyConstants.KnoraBase.LinkValue => + (givenValue.link_value, givenValue.link_to_client_id) match { + case (Some(targetIri: IRI), None) => + // This is a link to an existing Knora IRI, so make sure the IRI is valid. + val validatedTargetIri = stringFormatter.validateAndEscapeIri( + targetIri, + throw BadRequestException(s"Invalid Knora resource IRI: $targetIri") + ) + Future(CreateValueV1WithComment(LinkUpdateV1(validatedTargetIri), givenValue.comment)) + + case (None, Some(clientIDForTargetResource: String)) => + // This is a link to the client's ID for a resource that hasn't been created yet. + Future( + CreateValueV1WithComment(LinkToClientIDUpdateV1(clientIDForTargetResource), givenValue.comment) + ) + + case (_, _) => throw AssertionException(s"Invalid link: $givenValue") + } + + case OntologyConstants.KnoraBase.IntValue => + Future(CreateValueV1WithComment(IntegerValueV1(givenValue.int_value.get), givenValue.comment)) + + case OntologyConstants.KnoraBase.DecimalValue => + Future(CreateValueV1WithComment(DecimalValueV1(givenValue.decimal_value.get), givenValue.comment)) + + case OntologyConstants.KnoraBase.BooleanValue => + Future(CreateValueV1WithComment(BooleanValueV1(givenValue.boolean_value.get), givenValue.comment)) + + case OntologyConstants.KnoraBase.UriValue => + val uriValue = stringFormatter.validateAndEscapeIri( + givenValue.uri_value.get, + throw BadRequestException(s"Invalid URI: ${givenValue.uri_value.get}") + ) + Future(CreateValueV1WithComment(UriValueV1(uriValue), givenValue.comment)) + + case OntologyConstants.KnoraBase.DateValue => + val dateVal: JulianDayNumberValueV1 = + DateUtilV1.createJDNValueV1FromDateString(givenValue.date_value.get) + Future(CreateValueV1WithComment(dateVal, givenValue.comment)) + + case OntologyConstants.KnoraBase.ColorValue => + val colorValue = stringFormatter.validateColor( + givenValue.color_value.get, + throw BadRequestException(s"Invalid color value: ${givenValue.color_value.get}") + ) + Future(CreateValueV1WithComment(ColorValueV1(colorValue), givenValue.comment)) + + case OntologyConstants.KnoraBase.GeomValue => + val geometryValue = stringFormatter.validateGeometryString( + givenValue.geom_value.get, + throw BadRequestException(s"Invalid geometry value: ${givenValue.geom_value.get}") + ) + Future(CreateValueV1WithComment(GeomValueV1(geometryValue), givenValue.comment)) + + case OntologyConstants.KnoraBase.ListValue => + val listNodeIri = stringFormatter.validateAndEscapeIri( + givenValue.hlist_value.get, + throw BadRequestException(s"Invalid value IRI: ${givenValue.hlist_value.get}") + ) + Future(CreateValueV1WithComment(HierarchicalListValueV1(listNodeIri), givenValue.comment)) + + case OntologyConstants.KnoraBase.IntervalValue => + val timeVals: Seq[BigDecimal] = givenValue.interval_value.get + if (timeVals.length != 2) throw BadRequestException("parameters for interval_value invalid") + Future(CreateValueV1WithComment(IntervalValueV1(timeVals.head, timeVals(1)), givenValue.comment)) + + case OntologyConstants.KnoraBase.TimeValue => + val timeValStr: String = givenValue.time_value.get + val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( + timeValStr, + throw BadRequestException(s"Invalid timestamp: $timeValStr") + ) + Future(CreateValueV1WithComment(TimeValueV1(timeStamp), givenValue.comment)) + + case OntologyConstants.KnoraBase.GeonameValue => + Future(CreateValueV1WithComment(GeonameValueV1(givenValue.geoname_value.get), givenValue.comment)) + + case _ => throw BadRequestException(s"No value submitted") + + } + + } + ) + }.map { + // transform Seq of Futures to a Future of a Seq + case (propIri: IRI, values: Seq[Future[CreateValueV1WithComment]]) => + (propIri, Future.sequence(values)) + } } - def makeCreateResourceRequestMessage(apiRequest: CreateResourceApiRequestV1, - featureFactoryConfig: FeatureFactoryConfig, - userADM: UserADM): Future[ResourceCreateRequestV1] = { + def makeCreateResourceRequestMessage( + apiRequest: CreateResourceApiRequestV1, + featureFactoryConfig: FeatureFactoryConfig, + userADM: UserADM + ): Future[ResourceCreateRequestV1] = { val projectIri = stringFormatter.validateAndEscapeIri( apiRequest.project_id, - throw BadRequestException(s"Invalid project IRI: ${apiRequest.project_id}")) + throw BadRequestException(s"Invalid project IRI: ${apiRequest.project_id}") + ) val resourceTypeIri = stringFormatter.validateAndEscapeIri( apiRequest.restype_id, - throw BadRequestException(s"Invalid resource IRI: ${apiRequest.restype_id}")) + throw BadRequestException(s"Invalid resource IRI: ${apiRequest.restype_id}") + ) val label = stringFormatter.toSparqlEncodedString( apiRequest.label, - throw BadRequestException(s"Invalid label: '${apiRequest.label}'")) + throw BadRequestException(s"Invalid label: '${apiRequest.label}'") + ) for { projectShortcode: String <- for { @@ -321,14 +341,15 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) for { fileMetadataResponse: GetFileMetadataResponse <- (storeManager ? GetFileMetadataRequest( fileUrl = tempFileUrl, - requestingUser = userADM)).mapTo[GetFileMetadataResponse] - } yield - Some( - RouteUtilV1.makeFileValue( - filename = filename, - fileMetadataResponse = fileMetadataResponse, - projectShortcode = projectShortcode - )) + requestingUser = userADM + )).mapTo[GetFileMetadataResponse] + } yield Some( + RouteUtilV1.makeFileValue( + filename = filename, + fileMetadataResponse = fileMetadataResponse, + projectShortcode = projectShortcode + ) + ) case None => FastFuture.successful(None) } @@ -341,23 +362,25 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // make the whole Map a Future valuesToBeCreated: Map[IRI, Seq[CreateValueV1WithComment]] <- ActorUtil.sequenceFutureSeqsInMap( - valuesToBeCreatedWithFuture) - } yield - ResourceCreateRequestV1( - resourceTypeIri = resourceTypeIri, - label = label, - projectIri = projectIri, - values = valuesToBeCreated, - file = file, - featureFactoryConfig = featureFactoryConfig, - userProfile = userADM, - apiRequestID = UUID.randomUUID + valuesToBeCreatedWithFuture ) + } yield ResourceCreateRequestV1( + resourceTypeIri = resourceTypeIri, + label = label, + projectIri = projectIri, + values = valuesToBeCreated, + file = file, + featureFactoryConfig = featureFactoryConfig, + userProfile = userADM, + apiRequestID = UUID.randomUUID + ) } - def createOneResourceRequestFromXmlImport(resourceRequest: CreateResourceFromXmlImportRequestV1, - projectShortcode: String, - userProfile: UserADM): Future[OneOfMultipleResourceCreateRequestV1] = { + def createOneResourceRequestFromXmlImport( + resourceRequest: CreateResourceFromXmlImportRequestV1, + projectShortcode: String, + userProfile: UserADM + ): Future[OneOfMultipleResourceCreateRequestV1] = { val values: Map[IRI, Future[Seq[CreateValueV1WithComment]]] = valuesToCreate( properties = resourceRequest.properties, acceptStandoffLinksToClientIDs = true, @@ -377,35 +400,38 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) for { fileMetadataResponse: GetFileMetadataResponse <- (storeManager ? GetFileMetadataRequest( fileUrl = tempFileUrl, - requestingUser = userProfile)).mapTo[GetFileMetadataResponse] - } yield - Some( - RouteUtilV1.makeFileValue( - filename = filename, - fileMetadataResponse = fileMetadataResponse, - projectShortcode = projectShortcode - )) + requestingUser = userProfile + )).mapTo[GetFileMetadataResponse] + } yield Some( + RouteUtilV1.makeFileValue( + filename = filename, + fileMetadataResponse = fileMetadataResponse, + projectShortcode = projectShortcode + ) + ) case None => FastFuture.successful(None) } - } yield - OneOfMultipleResourceCreateRequestV1( - resourceTypeIri = resourceRequest.restype_id, - clientResourceID = resourceRequest.client_id, - label = stringFormatter.toSparqlEncodedString( - resourceRequest.label, - throw BadRequestException(s"The resource label is invalid: '${resourceRequest.label}'")), - values = valuesToBeCreated, - file = convertedFile, - creationDate = resourceRequest.creationDate - ) + } yield OneOfMultipleResourceCreateRequestV1( + resourceTypeIri = resourceRequest.restype_id, + clientResourceID = resourceRequest.client_id, + label = stringFormatter.toSparqlEncodedString( + resourceRequest.label, + throw BadRequestException(s"The resource label is invalid: '${resourceRequest.label}'") + ), + values = valuesToBeCreated, + file = convertedFile, + creationDate = resourceRequest.creationDate + ) } - def makeMultiResourcesRequestMessage(resourceRequest: Seq[CreateResourceFromXmlImportRequestV1], - projectId: IRI, - apiRequestID: UUID, - featureFactoryConfig: FeatureFactoryConfig, - userProfile: UserADM): Future[MultipleResourceCreateRequestV1] = { + def makeMultiResourcesRequestMessage( + resourceRequest: Seq[CreateResourceFromXmlImportRequestV1], + projectId: IRI, + apiRequestID: UUID, + featureFactoryConfig: FeatureFactoryConfig, + userProfile: UserADM + ): Future[MultipleResourceCreateRequestV1] = { // Make sure there are no duplicate client resource IDs. val duplicateClientIDs: immutable.Iterable[String] = resourceRequest.map(_.client_id).groupBy(identity).collect { @@ -414,7 +440,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) if (duplicateClientIDs.nonEmpty) { throw BadRequestException( - s"One or more client resource IDs were used for multiple resources: ${duplicateClientIDs.mkString(", ")}") + s"One or more client resource IDs were used for multiple resources: ${duplicateClientIDs.mkString(", ")}" + ) } for { @@ -436,73 +463,76 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) } resToCreateCollection: Seq[OneOfMultipleResourceCreateRequestV1] <- Future.sequence(resourcesToCreate) - } yield - MultipleResourceCreateRequestV1( - resourcesToCreate = resToCreateCollection, - projectIri = projectId, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile, - apiRequestID = apiRequestID - ) + } yield MultipleResourceCreateRequestV1( + resourcesToCreate = resToCreateCollection, + projectIri = projectId, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile, + apiRequestID = apiRequestID + ) } - def makeGetPropertiesRequestMessage(resIri: IRI, userADM: UserADM): PropertiesGetRequestV1 = { + def makeGetPropertiesRequestMessage(resIri: IRI, userADM: UserADM): PropertiesGetRequestV1 = PropertiesGetRequestV1( iri = resIri, featureFactoryConfig = featureFactoryConfig, userProfile = userADM ) - } - def makeResourceDeleteMessage(resIri: IRI, - deleteComment: Option[String], - userADM: UserADM): ResourceDeleteRequestV1 = { + def makeResourceDeleteMessage( + resIri: IRI, + deleteComment: Option[String], + userADM: UserADM + ): ResourceDeleteRequestV1 = ResourceDeleteRequestV1( resourceIri = stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: $resIri")), deleteComment = deleteComment.map(comment => - stringFormatter.toSparqlEncodedString(comment, throw BadRequestException(s"Invalid comment: '$comment'"))), + stringFormatter.toSparqlEncodedString(comment, throw BadRequestException(s"Invalid comment: '$comment'")) + ), featureFactoryConfig = featureFactoryConfig, userADM = userADM, apiRequestID = UUID.randomUUID ) - } /** - * Given the IRI the main internal ontology to be used in an XML import, recursively gets instances of - * [[NamedGraphEntityInfoV1]] for that ontology, for `knora-base`, and for any other ontologies containing - * classes used in object class constraints in the main ontology. - * - * @param mainOntologyIri the IRI of the main ontology used in the XML import. - * @param userProfile the profile of the user making the request. - * @return a map of internal ontology IRIs to [[NamedGraphEntityInfoV1]] objects. - */ + * Given the IRI the main internal ontology to be used in an XML import, recursively gets instances of + * [[NamedGraphEntityInfoV1]] for that ontology, for `knora-base`, and for any other ontologies containing + * classes used in object class constraints in the main ontology. + * + * @param mainOntologyIri the IRI of the main ontology used in the XML import. + * @param userProfile the profile of the user making the request. + * @return a map of internal ontology IRIs to [[NamedGraphEntityInfoV1]] objects. + */ def getNamedGraphInfos(mainOntologyIri: IRI, userProfile: UserADM): Future[Map[IRI, NamedGraphEntityInfoV1]] = { /** - * Does the actual recursion for `getNamedGraphInfos`, loading only information about project-specific - * ontologies (i.e. ontologies other than `knora-base`). - * - * @param initialOntologyIri the IRI of the internal project-specific ontology to start with. - * @param intermediateResults the intermediate results collected so far (a map of internal ontology IRIs to - * [[NamedGraphEntityInfoV1]] objects). When this method is first called, this - * collection must already contain a [[NamedGraphEntityInfoV1]] for - * the `knora-base` ontology. This is an optimisation to avoid getting - * information about `knora-base` repeatedly, since every project-specific - * ontology depends on `knora-base`. - * @param userProfile the profile of the user making the request. - * @return a map of internal ontology IRIs to [[NamedGraphEntityInfoV1]] objects. - */ - def getNamedGraphInfosRec(initialOntologyIri: IRI, - intermediateResults: Map[IRI, NamedGraphEntityInfoV1], - userProfile: UserADM): Future[Map[IRI, NamedGraphEntityInfoV1]] = { + * Does the actual recursion for `getNamedGraphInfos`, loading only information about project-specific + * ontologies (i.e. ontologies other than `knora-base`). + * + * @param initialOntologyIri the IRI of the internal project-specific ontology to start with. + * @param intermediateResults the intermediate results collected so far (a map of internal ontology IRIs to + * [[NamedGraphEntityInfoV1]] objects). When this method is first called, this + * collection must already contain a [[NamedGraphEntityInfoV1]] for + * the `knora-base` ontology. This is an optimisation to avoid getting + * information about `knora-base` repeatedly, since every project-specific + * ontology depends on `knora-base`. + * @param userProfile the profile of the user making the request. + * @return a map of internal ontology IRIs to [[NamedGraphEntityInfoV1]] objects. + */ + def getNamedGraphInfosRec( + initialOntologyIri: IRI, + intermediateResults: Map[IRI, NamedGraphEntityInfoV1], + userProfile: UserADM + ): Future[Map[IRI, NamedGraphEntityInfoV1]] = { assert(intermediateResults.contains(OntologyConstants.KnoraBase.KnoraBaseOntologyIri)) for { // Get a NamedGraphEntityInfoV1 listing the IRIs of the classes and properties defined in the initial ontology. initialNamedGraphInfo: NamedGraphEntityInfoV1 <- (responderManager ? NamedGraphEntityInfoRequestV1( initialOntologyIri, - userProfile)).mapTo[NamedGraphEntityInfoV1] + userProfile + )).mapTo[NamedGraphEntityInfoV1] // Get details about those classes and properties. entityInfoResponse: EntityInfoGetResponseV1 <- (responderManager ? EntityInfoGetRequestV1( @@ -542,24 +572,27 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) val propertyObjectClassConstraint = propertyInfo.getPredicateObject(OntologyConstants.KnoraBase.ObjectClassConstraint).getOrElse { throw InconsistentRepositoryDataException( - s"Property $propertyIri has no knora-base:objectClassConstraint") + s"Property $propertyIri has no knora-base:objectClassConstraint" + ) } propertyObjectClassConstraint.toSmartIri.getOntologyFromEntity.toString }.toSet -- intermediateResults.keySet - initialOntologyIri // Make a set of all the ontologies referenced by the initial ontology. - referencedOntologies: Set[IRI] = ontologyIrisFromBaseClasses ++ ontologyIrisFromCardinalities ++ ontologyIrisFromObjectClassConstraints + referencedOntologies: Set[IRI] = + ontologyIrisFromBaseClasses ++ ontologyIrisFromCardinalities ++ ontologyIrisFromObjectClassConstraints // Recursively get NamedGraphEntityInfoV1 instances for each of those ontologies. lastResults: Map[IRI, NamedGraphEntityInfoV1] <- referencedOntologies.foldLeft( - FastFuture.successful(intermediateResults + (initialOntologyIri -> initialNamedGraphInfo))) { - case (accFuture, ontologyIri) => - for { - acc: Map[IRI, NamedGraphEntityInfoV1] <- accFuture + FastFuture.successful(intermediateResults + (initialOntologyIri -> initialNamedGraphInfo)) + ) { case (accFuture, ontologyIri) => + for { + acc: Map[IRI, NamedGraphEntityInfoV1] <- accFuture - // Has a previous recursion already dealt with this ontology? - nextResults: Map[IRI, NamedGraphEntityInfoV1] <- if (acc.contains(ontologyIri)) { + // Has a previous recursion already dealt with this ontology? + nextResults: Map[IRI, NamedGraphEntityInfoV1] <- + if (acc.contains(ontologyIri)) { // Yes, so there's no need to get it again. FastFuture.successful(acc) } else { @@ -570,7 +603,7 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) userProfile = userProfile ) } - } yield acc ++ nextResults + } yield acc ++ nextResults } } yield lastResults } @@ -579,7 +612,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Get a NamedGraphEntityInfoV1 for the knora-base ontology. knoraBaseGraphEntityInfo <- (responderManager ? NamedGraphEntityInfoRequestV1( OntologyConstants.KnoraBase.KnoraBaseOntologyIri, - userProfile)).mapTo[NamedGraphEntityInfoV1] + userProfile + )).mapTo[NamedGraphEntityInfoV1] // Recursively get NamedGraphEntityInfoV1 instances for the main ontology to be used in the XML import, // as well as any other project-specific ontologies it depends on. @@ -592,24 +626,26 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) } /** - * Given the IRI of an internal project-specific ontology, generates an [[XmlImportSchemaBundleV1]] for validating - * XML imports for that ontology and any other ontologies it depends on. - * - * @param internalOntologyIri the IRI of the main internal project-specific ontology to be used in the XML import. - * @param userProfile the profile of the user making the request. - * @return an [[XmlImportSchemaBundleV1]] for validating the import. - */ - def generateSchemasFromOntologies(internalOntologyIri: IRI, - userProfile: UserADM): Future[XmlImportSchemaBundleV1] = { + * Given the IRI of an internal project-specific ontology, generates an [[XmlImportSchemaBundleV1]] for validating + * XML imports for that ontology and any other ontologies it depends on. + * + * @param internalOntologyIri the IRI of the main internal project-specific ontology to be used in the XML import. + * @param userProfile the profile of the user making the request. + * @return an [[XmlImportSchemaBundleV1]] for validating the import. + */ + def generateSchemasFromOntologies( + internalOntologyIri: IRI, + userProfile: UserADM + ): Future[XmlImportSchemaBundleV1] = { /** - * Called by the schema generation template to get the prefix label for an internal ontology - * entity IRI. The schema generation template gets these IRIs from resource cardinalities - * and property object class constraints, which we get from the ontology responder. - * - * @param internalEntityIri an internal ontology entity IRI. - * @return the prefix label that Knora uses to refer to the ontology. - */ + * Called by the schema generation template to get the prefix label for an internal ontology + * entity IRI. The schema generation template gets these IRIs from resource cardinalities + * and property object class constraints, which we get from the ontology responder. + * + * @param internalEntityIri an internal ontology entity IRI. + * @return the prefix label that Knora uses to refer to the ontology. + */ def getNamespacePrefixLabel(internalEntityIri: IRI): String = { val prefixLabel = internalEntityIri.toSmartIri.getLongPrefixLabel @@ -623,21 +659,22 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) } /** - * Called by the schema generation template to get the entity name (i.e. the local name part) of an - * internal ontology entity IRI. The schema generation template gets these IRIs from resource cardinalities - * and property object class constraints, which we get from the ontology responder. - * - * @param internalEntityIri an internal ontology entity IRI. - * @return the local name of the entity. - */ - def getEntityName(internalEntityIri: IRI): String = { + * Called by the schema generation template to get the entity name (i.e. the local name part) of an + * internal ontology entity IRI. The schema generation template gets these IRIs from resource cardinalities + * and property object class constraints, which we get from the ontology responder. + * + * @param internalEntityIri an internal ontology entity IRI. + * @return the local name of the entity. + */ + def getEntityName(internalEntityIri: IRI): String = internalEntityIri.toSmartIri.getEntityName - } for { // Get a NamedGraphEntityInfoV1 for each ontology that we need to generate an XML schema for. - namedGraphInfos: Map[IRI, NamedGraphEntityInfoV1] <- getNamedGraphInfos(mainOntologyIri = internalOntologyIri, - userProfile = userProfile) + namedGraphInfos: Map[IRI, NamedGraphEntityInfoV1] <- getNamedGraphInfos( + mainOntologyIri = internalOntologyIri, + userProfile = userProfile + ) // Get information about the resource classes and properties in each ontology. entityInfoResponseFutures: immutable.Iterable[Future[(IRI, EntityInfoGetResponseV1)]] = namedGraphInfos.map { @@ -653,7 +690,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Sequence the futures of entity info responses. entityInfoResponses: immutable.Iterable[(IRI, EntityInfoGetResponseV1)] <- Future.sequence( - entityInfoResponseFutures) + entityInfoResponseFutures + ) // Make a Map of internal ontology IRIs to EntityInfoGetResponseV1 objects. entityInfoResponsesMap: Map[IRI, EntityInfoGetResponseV1] = entityInfoResponses.toMap @@ -665,10 +703,10 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Make a map of internal ontology IRIs to XmlImportNamespaceInfoV1 objects describing the XML namespace // of each schema to be generated. Don't generate a schema for knora-base, because the built-in Knora // types are specified in the handwritten standard Knora XML import v1 schema. - schemasToGenerate: Map[IRI, XmlImportNamespaceInfoV1] = (namedGraphInfos.keySet - OntologyConstants.KnoraBase.KnoraBaseOntologyIri).map { - ontologyIri => + schemasToGenerate: Map[IRI, XmlImportNamespaceInfoV1] = + (namedGraphInfos.keySet - OntologyConstants.KnoraBase.KnoraBaseOntologyIri).map { ontologyIri => ontologyIri -> stringFormatter.internalOntologyIriToXmlNamespaceInfoV1(ontologyIri.toSmartIri) - }.toMap + }.toMap // Make an XmlImportNamespaceInfoV1 for the standard Knora XML import v1 schema's namespace. knoraXmlImportSchemaNamespaceInfo: XmlImportNamespaceInfoV1 = XmlImportNamespaceInfoV1( @@ -678,7 +716,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Read the standard Knora XML import v1 schema from a file. knoraXmlImportSchemaXml: String = FileUtil.readTextResource( - OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespacePrefixLabel + ".xsd") + OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespacePrefixLabel + ".xsd" + ) // Construct an XmlImportSchemaV1 for the standard Knora XML import v1 schema. knoraXmlImportSchema: XmlImportSchemaV1 = XmlImportSchemaV1( @@ -687,71 +726,71 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) ) // Generate a schema for each project-specific ontology. - generatedSchemas: Map[IRI, XmlImportSchemaV1] = schemasToGenerate.map { - case (ontologyIri, namespaceInfo) => - // Each schema imports all the other generated schemas, plus the standard Knora XML import v1 schema. - // Sort the imports to make schema generation deterministic. - val importedNamespaceInfos - : Seq[XmlImportNamespaceInfoV1] = (schemasToGenerate - ontologyIri).values.toVector.sortBy { - importedNamespaceInfo => - importedNamespaceInfo.prefixLabel + generatedSchemas: Map[IRI, XmlImportSchemaV1] = schemasToGenerate.map { case (ontologyIri, namespaceInfo) => + // Each schema imports all the other generated schemas, plus the standard Knora XML import v1 schema. + // Sort the imports to make schema generation deterministic. + val importedNamespaceInfos: Seq[XmlImportNamespaceInfoV1] = + (schemasToGenerate - ontologyIri).values.toVector.sortBy { importedNamespaceInfo => + importedNamespaceInfo.prefixLabel } :+ knoraXmlImportSchemaNamespaceInfo - // Generate the schema using a Twirl template. - val unformattedSchemaXml = org.knora.webapi.messages.twirl.xsd.v1.xml - .xmlImport( - targetNamespaceInfo = namespaceInfo, - importedNamespaces = importedNamespaceInfos, - knoraXmlImportNamespacePrefixLabel = - OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespacePrefixLabel, - resourceClassInfoMap = entityInfoResponsesMap(ontologyIri).resourceClassInfoMap, - propertyInfoMap = propertyInfoMap, - getNamespacePrefixLabel = internalEntityIri => getNamespacePrefixLabel(internalEntityIri), - getEntityName = internalEntityIri => getEntityName(internalEntityIri) - ) - .toString() - .trim + // Generate the schema using a Twirl template. + val unformattedSchemaXml = org.knora.webapi.messages.twirl.xsd.v1.xml + .xmlImport( + targetNamespaceInfo = namespaceInfo, + importedNamespaces = importedNamespaceInfos, + knoraXmlImportNamespacePrefixLabel = + OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespacePrefixLabel, + resourceClassInfoMap = entityInfoResponsesMap(ontologyIri).resourceClassInfoMap, + propertyInfoMap = propertyInfoMap, + getNamespacePrefixLabel = internalEntityIri => getNamespacePrefixLabel(internalEntityIri), + getEntityName = internalEntityIri => getEntityName(internalEntityIri) + ) + .toString() + .trim - // Parse the generated XML schema. - val parsedSchemaXml = try { + // Parse the generated XML schema. + val parsedSchemaXml = + try { XML.loadString(unformattedSchemaXml) } catch { case parseEx: org.xml.sax.SAXParseException => throw AssertionException( s"Generated XML schema for namespace ${namespaceInfo.namespace} is not valid XML. Please report this as a bug.", parseEx, - log) + log + ) } - // Format the generated XML schema nicely. - val formattedSchemaXml = xmlPrettyPrinter.format(parsedSchemaXml) + // Format the generated XML schema nicely. + val formattedSchemaXml = xmlPrettyPrinter.format(parsedSchemaXml) - // Wrap it in an XmlImportSchemaV1 object along with its XML namespace information. - val schema = XmlImportSchemaV1( - namespaceInfo = namespaceInfo, - schemaXml = formattedSchemaXml - ) + // Wrap it in an XmlImportSchemaV1 object along with its XML namespace information. + val schema = XmlImportSchemaV1( + namespaceInfo = namespaceInfo, + schemaXml = formattedSchemaXml + ) - namespaceInfo.namespace -> schema + namespaceInfo.namespace -> schema } // The schema bundle to be returned contains the generated schemas plus the standard Knora XML import v1 schema. - allSchemasForBundle: Map[IRI, XmlImportSchemaV1] = generatedSchemas + (OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespaceV1 -> knoraXmlImportSchema) - } yield - XmlImportSchemaBundleV1( - mainNamespace = schemasToGenerate(internalOntologyIri).namespace, - schemas = allSchemasForBundle - ) + allSchemasForBundle: Map[IRI, XmlImportSchemaV1] = + generatedSchemas + (OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespaceV1 -> knoraXmlImportSchema) + } yield XmlImportSchemaBundleV1( + mainNamespace = schemasToGenerate(internalOntologyIri).namespace, + schemas = allSchemasForBundle + ) } /** - * Generates a byte array representing a Zip file containing XML schemas for validating XML import data. - * - * @param internalOntologyIri the IRI of the main internal ontology for which data will be imported. - * @param userProfile the profile of the user making the request. - * @return a byte array representing a Zip file containing XML schemas. - */ - def generateSchemaZipFile(internalOntologyIri: IRI, userProfile: UserADM): Future[Array[Byte]] = { + * Generates a byte array representing a Zip file containing XML schemas for validating XML import data. + * + * @param internalOntologyIri the IRI of the main internal ontology for which data will be imported. + * @param userProfile the profile of the user making the request. + * @return a byte array representing a Zip file containing XML schemas. + */ + def generateSchemaZipFile(internalOntologyIri: IRI, userProfile: UserADM): Future[Array[Byte]] = for { // Generate a bundle of XML schemas. schemaBundle: XmlImportSchemaBundleV1 <- generateSchemasFromOntologies( @@ -766,17 +805,16 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) schemaFilename -> schemaXmlBytes }.toMap } yield FileUtil.createZipFileBytes(zipFileContents) - } /** - * Validates bulk import XML using project-specific XML schemas and the Knora XML import schema v1. - * - * @param xml the XML to be validated. - * @param defaultNamespace the default namespace of the submitted XML. This should be the Knora XML import - * namespace corresponding to the main internal ontology used in the import. - * @param userADM the profile of the user making the request. - * @return a `Future` containing `()` if successful, otherwise a failed future. - */ + * Validates bulk import XML using project-specific XML schemas and the Knora XML import schema v1. + * + * @param xml the XML to be validated. + * @param defaultNamespace the default namespace of the submitted XML. This should be the Knora XML import + * namespace corresponding to the main internal ontology used in the import. + * @param userADM the profile of the user making the request. + * @return a `Future` containing `()` if successful, otherwise a failed future. + */ def validateImportXml(xml: String, defaultNamespace: IRI, userADM: UserADM): Future[Unit] = { // Convert the default namespace of the submitted XML to an internal ontology IRI. This should be the // IRI of the main ontology used in the import. @@ -808,34 +846,34 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) } yield () // If the XML fails schema validation, return a failed Future containing a BadRequestException. - validationFuture.recover { - case e @ (_: IllegalArgumentException | _: SAXException) => - throw BadRequestException(s"XML import did not pass XML schema validation: $e") + validationFuture.recover { case e @ (_: IllegalArgumentException | _: SAXException) => + throw BadRequestException(s"XML import did not pass XML schema validation: $e") } } /** - * Converts parsed import XML into a sequence of [[CreateResourceFromXmlImportRequestV1]] for each resource - * described in the XML. - * - * @param rootElement the root element of an XML document describing multiple resources to be created. - * @return Seq[CreateResourceFromXmlImportRequestV1] a collection of resource creation requests. - */ + * Converts parsed import XML into a sequence of [[CreateResourceFromXmlImportRequestV1]] for each resource + * described in the XML. + * + * @param rootElement the root element of an XML document describing multiple resources to be created. + * @return Seq[CreateResourceFromXmlImportRequestV1] a collection of resource creation requests. + */ def importXmlToCreateResourceRequests(rootElement: Elem): Seq[CreateResourceFromXmlImportRequestV1] = { rootElement.head.child .filter(node => node.label != "#PCDATA") - .map(resourceNode => { + .map { resourceNode => // Get the client's unique ID for the resource. val clientIDForResource: String = (resourceNode \ "@id").toString // Get the optional resource creation date. val creationDate: Option[Instant] = resourceNode .attribute("creationDate") - .map( - creationDateNode => - stringFormatter.xsdDateTimeStampToInstant( - creationDateNode.text, - throw BadRequestException(s"Invalid resource creation date: ${creationDateNode.text}"))) + .map(creationDateNode => + stringFormatter.xsdDateTimeStampToInstant( + creationDateNode.text, + throw BadRequestException(s"Invalid resource creation date: ${creationDateNode.text}") + ) + ) // Convert the XML element's label and namespace to an internal resource class IRI. @@ -855,7 +893,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) case Some(firstChildElem) => firstChildElem.text case None => throw BadRequestException( - s"Resource '$clientIDForResource' contains no ${OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespacePrefixLabel}:label element") + s"Resource '$clientIDForResource' contains no ${OntologyConstants.KnoraXmlImportV1.KnoraXmlImportNamespacePrefixLabel}:label element" + ) } val childElementsAfterLabel = childElements.tail @@ -882,48 +921,45 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Traverse the property value elements. This produces a sequence in which the same property IRI // can occur multiple times, once for each value. - val propertiesWithValues: Seq[(IRI, CreateResourceValueV1)] = propertyElements.map { - propertyNode => - // Is this a property from another ontology (in the form prefixLabel__localName)? - val propertyIri = stringFormatter.toPropertyIriFromOtherOntologyInXmlImport(propertyNode.label) match { - case Some(iri) => - // Yes. Use the corresponding entity IRI for it. - iri - - case None => - // No. Convert the XML element's label and namespace to an internal property IRI. - - val propertyNodeNamespace = propertyNode.getNamespace(propertyNode.prefix) - - stringFormatter.xmlImportElementNameToInternalOntologyIriV1( - namespace = propertyNodeNamespace, - elementLabel = propertyNode.label, - errorFun = throw BadRequestException(s"Invalid XML namespace: $propertyNodeNamespace")) - } + val propertiesWithValues: Seq[(IRI, CreateResourceValueV1)] = propertyElements.map { propertyNode => + // Is this a property from another ontology (in the form prefixLabel__localName)? + val propertyIri = stringFormatter.toPropertyIriFromOtherOntologyInXmlImport(propertyNode.label) match { + case Some(iri) => + // Yes. Use the corresponding entity IRI for it. + iri - // If the property element has one child element with a knoraType attribute, it's a link - // property, otherwise it's an ordinary value property. + case None => + // No. Convert the XML element's label and namespace to an internal property IRI. - val valueNodes: Seq[Node] = propertyNode.child.filterNot(_.label == "#PCDATA") + val propertyNodeNamespace = propertyNode.getNamespace(propertyNode.prefix) - if (valueNodes.size == 1 && valueNodes.head.attribute("knoraType").isDefined) { - propertyIri -> knoraDataTypeXml(valueNodes.head) - } else { - propertyIri -> knoraDataTypeXml(propertyNode) - } + stringFormatter.xmlImportElementNameToInternalOntologyIriV1( + namespace = propertyNodeNamespace, + elementLabel = propertyNode.label, + errorFun = throw BadRequestException(s"Invalid XML namespace: $propertyNodeNamespace") + ) + } + + // If the property element has one child element with a knoraType attribute, it's a link + // property, otherwise it's an ordinary value property. + + val valueNodes: Seq[Node] = propertyNode.child.filterNot(_.label == "#PCDATA") + + if (valueNodes.size == 1 && valueNodes.head.attribute("knoraType").isDefined) { + propertyIri -> knoraDataTypeXml(valueNodes.head) + } else { + propertyIri -> knoraDataTypeXml(propertyNode) + } } // Group the values by property IRI. - val groupedPropertiesWithValues: Map[IRI, Seq[CreateResourceValueV1]] = propertiesWithValues - .groupBy { - case (propertyIri: IRI, _) => propertyIri - } - .map { - case (propertyIri: IRI, resultsForProperty: Seq[(IRI, CreateResourceValueV1)]) => - propertyIri -> resultsForProperty.map { - case (_, propertyValue: CreateResourceValueV1) => propertyValue - } + val groupedPropertiesWithValues: Map[IRI, Seq[CreateResourceValueV1]] = propertiesWithValues.groupBy { + case (propertyIri: IRI, _) => propertyIri + }.map { case (propertyIri: IRI, resultsForProperty: Seq[(IRI, CreateResourceValueV1)]) => + propertyIri -> resultsForProperty.map { case (_, propertyValue: CreateResourceValueV1) => + propertyValue } + } CreateResourceFromXmlImportRequestV1( restype_id = restype_id, @@ -933,17 +969,17 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) file = file, creationDate = creationDate ) - }) + } .toSeq } /** - * Given an XML element representing a property value in an XML import, returns a [[CreateResourceValueV1]] - * describing the value to be created. - * - * @param node the XML element. - * @return a [[CreateResourceValueV1]] requesting the creation of the value described by the element. - */ + * Given an XML element representing a property value in an XML import, returns a [[CreateResourceValueV1]] + * describing the value to be created. + * + * @param node the XML element. + * @return a [[CreateResourceValueV1]] requesting the creation of the value described by the element. + */ def knoraDataTypeXml(node: Node): CreateResourceValueV1 = { val knoraType: Seq[Node] = node .attribute("knoraType") @@ -961,7 +997,9 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) val mappingIri: Option[IRI] = Some( stringFormatter.validateAndEscapeIri( mappingID.toString, - throw BadRequestException(s"Invalid mapping ID in element '${node.label}: '$mappingID"))) + throw BadRequestException(s"Invalid mapping ID in element '${node.label}: '$mappingID") + ) + ) val childElements = node.child.filterNot(_.label == "#PCDATA") if (childElements.nonEmpty) { @@ -969,19 +1007,25 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) val embeddedXmlDoc = """""" + embeddedXmlRootNode.toString CreateResourceValueV1( richtext_value = Some( - CreateRichtextV1(utf8str = None, - language = language, - xml = Some(embeddedXmlDoc), - mapping_id = mappingIri))) + CreateRichtextV1( + utf8str = None, + language = language, + xml = Some(embeddedXmlDoc), + mapping_id = mappingIri + ) + ) + ) } else { throw BadRequestException( - s"Element '${node.label}' provides a mapping_id, but its content is not XML") + s"Element '${node.label}' provides a mapping_id, but its content is not XML" + ) } case None => // We don't escape the input string here, because it will be escaped by valuesToCreate(). CreateResourceValueV1( - richtext_value = Some(CreateRichtextV1(utf8str = Some(elementValue), language = language))) + richtext_value = Some(CreateRichtextV1(utf8str = Some(elementValue), language = language)) + ) } case "link_value" => @@ -1001,10 +1045,14 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) link_value = Some( stringFormatter.validateAndEscapeIri( target, - throw BadRequestException(s"Invalid IRI in element '${node.label}': '$target'")))) + throw BadRequestException(s"Invalid IRI in element '${node.label}': '$target'") + ) + ) + ) case other => throw BadRequestException( - s"Unrecognised value '$other' in attribute 'linkType' of element '${node.label}'") + s"Unrecognised value '$other' in attribute 'linkType' of element '${node.label}'" + ) } case None => throw BadRequestException(s"Attribute 'ref' missing in element '${node.label}'") @@ -1015,56 +1063,80 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) int_value = Some( stringFormatter.validateInt( elementValue, - throw BadRequestException(s"Invalid integer value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid integer value in element '${node.label}: '$elementValue'") + ) + ) + ) case "decimal_value" => CreateResourceValueV1( decimal_value = Some( stringFormatter.validateBigDecimal( elementValue, - throw BadRequestException(s"Invalid decimal value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid decimal value in element '${node.label}: '$elementValue'") + ) + ) + ) case "boolean_value" => CreateResourceValueV1( boolean_value = Some( stringFormatter.validateBoolean( elementValue, - throw BadRequestException(s"Invalid boolean value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid boolean value in element '${node.label}: '$elementValue'") + ) + ) + ) case "uri_value" => CreateResourceValueV1( uri_value = Some( stringFormatter.validateAndEscapeIri( elementValue, - throw BadRequestException(s"Invalid URI value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid URI value in element '${node.label}: '$elementValue'") + ) + ) + ) case "date_value" => CreateResourceValueV1( date_value = Some( stringFormatter.validateDate( elementValue, - throw BadRequestException(s"Invalid date value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid date value in element '${node.label}: '$elementValue'") + ) + ) + ) case "color_value" => CreateResourceValueV1( color_value = Some( stringFormatter.validateColor( elementValue, - throw BadRequestException(s"Invalid date value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid date value in element '${node.label}: '$elementValue'") + ) + ) + ) case "geom_value" => CreateResourceValueV1( geom_value = Some( stringFormatter.validateGeometryString( elementValue, - throw BadRequestException(s"Invalid geometry value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid geometry value in element '${node.label}: '$elementValue'") + ) + ) + ) case "hlist_value" => CreateResourceValueV1( hlist_value = Some( stringFormatter.validateAndEscapeIri( elementValue, - throw BadRequestException(s"Invalid hlist value in element '${node.label}: '$elementValue'")))) + throw BadRequestException(s"Invalid hlist value in element '${node.label}: '$elementValue'") + ) + ) + ) case "interval_value" => Try(elementValue.split(",")) match { @@ -1075,7 +1147,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) val tVals: Seq[BigDecimal] = timeVals.map { timeVal => stringFormatter.validateBigDecimal( timeVal, - throw BadRequestException(s"Invalid decimal value in element '${node.label}: '$timeVal'")) + throw BadRequestException(s"Invalid decimal value in element '${node.label}: '$timeVal'") + ) } CreateResourceValueV1(interval_value = Some(tVals)) @@ -1087,7 +1160,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) case "time_value" => val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( elementValue, - throw BadRequestException(s"Invalid timestamp in element '${node.label}': $elementValue")) + throw BadRequestException(s"Invalid timestamp in element '${node.label}': $elementValue") + ) CreateResourceValueV1(time_value = Some(timeStamp.toString)) case "geoname_value" => @@ -1121,34 +1195,40 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) searchString = stringFormatter.toSparqlEncodedString( searchstr, - throw BadRequestException(s"Invalid search string: '$searchstr'")) + throw BadRequestException(s"Invalid search string: '$searchstr'") + ) resourceTypeIri: Option[IRI] = restype match { case "-1" => None case restype: IRI => Some( - stringFormatter.validateAndEscapeIri(restype, - throw BadRequestException(s"Invalid param restype: $restype"))) + stringFormatter.validateAndEscapeIri( + restype, + throw BadRequestException(s"Invalid param restype: $restype") + ) + ) } numberOfProps: Int = stringFormatter.validateInt( numprops, - throw BadRequestException(s"Invalid param numprops: $numprops")) match { + throw BadRequestException(s"Invalid param numprops: $numprops") + ) match { case number: Int => if (number < 1) 1 else number // numberOfProps must not be smaller than 1 } - limitOfResults = stringFormatter.validateInt(limit, - throw BadRequestException(s"Invalid param limit: $limit")) - - } yield - makeResourceSearchRequestMessage( - searchString = searchString, - resourceTypeIri = resourceTypeIri, - numberOfProps = numberOfProps, - limitOfResults = limitOfResults, - userProfile = userProfile + limitOfResults = stringFormatter.validateInt( + limit, + throw BadRequestException(s"Invalid param limit: $limit") ) + } yield makeResourceSearchRequestMessage( + searchString = searchString, + resourceTypeIri = resourceTypeIri, + numberOfProps = numberOfProps, + limitOfResults = limitOfResults, + userProfile = userProfile + ) + RouteUtilV1.runJsonRouteWithFuture( requestMessageF = requestMessage, requestContext = requestContext, @@ -1193,11 +1273,12 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) ) requestType = reqtypeParam.getOrElse("") resinfo = resinfoParam.getOrElse(false) - } yield - makeResourceRequestMessage(resIri = resIri, - resinfo = resinfo, - requestType = requestType, - userADM = userADM) + } yield makeResourceRequestMessage( + resIri = resIri, + resinfo = resinfo, + requestType = requestType, + userADM = userADM + ) RouteUtilV1.runJsonRouteWithFuture( requestMessageF = requestMessage, @@ -1239,13 +1320,13 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) ) resIri = stringFormatter.validateAndEscapeIri( iri, - throw BadRequestException(s"Invalid param resource IRI: $iri")) - } yield - ResourceFullGetRequestV1( - iri = resIri, - featureFactoryConfig = featureFactoryConfig, - userADM = userADM + throw BadRequestException(s"Invalid param resource IRI: $iri") ) + } yield ResourceFullGetRequestV1( + iri = resIri, + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) case other => throw BadRequestException(s"Invalid request type: $other") } @@ -1265,8 +1346,10 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - resIri = stringFormatter.validateAndEscapeIri(iri, - throw BadRequestException(s"Invalid param resource IRI: $iri")) + resIri = stringFormatter.validateAndEscapeIri( + iri, + throw BadRequestException(s"Invalid param resource IRI: $iri") + ) } yield makeGetPropertiesRequestMessage(resIri, userADM) RouteUtilV1.runJsonRouteWithFuture( @@ -1288,18 +1371,19 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) ) resIri = stringFormatter.validateAndEscapeIri( iri, - throw BadRequestException(s"Invalid param resource IRI: $iri")) + throw BadRequestException(s"Invalid param resource IRI: $iri") + ) label = stringFormatter.toSparqlEncodedString( apiRequest.label, - throw BadRequestException(s"Invalid label: '${apiRequest.label}'")) - } yield - ChangeResourceLabelRequestV1( - resourceIri = resIri, - label = label, - apiRequestID = UUID.randomUUID, - featureFactoryConfig = featureFactoryConfig, - userADM = userADM + throw BadRequestException(s"Invalid label: '${apiRequest.label}'") ) + } yield ChangeResourceLabelRequestV1( + resourceIri = resIri, + label = label, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = featureFactoryConfig, + userADM = userADM + ) RouteUtilV1.runJsonRouteWithFuture( requestMessageF = requestMessage, @@ -1320,7 +1404,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) ) resourceIri = stringFormatter.validateAndEscapeIri( iri, - throw BadRequestException(s"Invalid param resource IRI: $iri")) + throw BadRequestException(s"Invalid param resource IRI: $iri") + ) } yield GraphDataGetRequestV1(resourceIri, depth.getOrElse(4), userADM) RouteUtilV1.runJsonRouteWithFuture( @@ -1362,12 +1447,14 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) _ = if (userADM.isAnonymousUser) { throw ForbiddenException( - "You are not logged in, and only a system administrator or project administrator can perform a bulk import") + "You are not logged in, and only a system administrator or project administrator can perform a bulk import" + ) } _ = if (!(userADM.permissions.isSystemAdmin || userADM.permissions.isProjectAdmin(projectId))) { throw ForbiddenException( - s"You are logged in as ${userADM.email}, but only a system administrator or project administrator can perform a bulk import") + s"You are logged in as ${userADM.email}, but only a system administrator or project administrator can perform a bulk import" + ) } // Parse the submitted XML. @@ -1391,7 +1478,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Make a CreateResourceFromXmlImportRequestV1 for each resource to be created. resourcesToCreate: Seq[CreateResourceFromXmlImportRequestV1] = importXmlToCreateResourceRequests( - rootElement) + rootElement + ) // Make a MultipleResourceCreateRequestV1 for the creation of all the resources. apiRequestID: UUID = UUID.randomUUID @@ -1418,7 +1506,8 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) get { // Get the prefix label of the specified internal ontology. val internalOntologySmartIri: SmartIri = internalOntologyIri.toSmartIriWithErr( - throw BadRequestException(s"Invalid internal project-specific ontology IRI: $internalOntologyIri")) + throw BadRequestException(s"Invalid internal project-specific ontology IRI: $internalOntologyIri") + ) if (!internalOntologySmartIri.isKnoraOntologyIri || internalOntologySmartIri.isKnoraBuiltInDefinitionIri) { throw BadRequestException(s"Invalid internal project-specific ontology IRI: $internalOntologyIri") @@ -1428,55 +1517,56 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) // Respond with a Content-Disposition header specifying the filename of the generated Zip file. respondWithHeader( - `Content-Disposition`(ContentDispositionTypes.attachment, - Map("filename" -> (internalOntologyPrefixLabel + "-xml-schemas.zip")))) { - requestContext => - val httpResponseFuture: Future[HttpResponse] = for { - userProfile <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - schemaZipFileBytes: Array[Byte] <- generateSchemaZipFile( - internalOntologyIri = internalOntologyIri, - userProfile = userProfile - ) - } yield - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity(bytes = schemaZipFileBytes) - ) + `Content-Disposition`( + ContentDispositionTypes.attachment, + Map("filename" -> (internalOntologyPrefixLabel + "-xml-schemas.zip")) + ) + ) { requestContext => + val httpResponseFuture: Future[HttpResponse] = for { + userProfile <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + schemaZipFileBytes: Array[Byte] <- generateSchemaZipFile( + internalOntologyIri = internalOntologyIri, + userProfile = userProfile + ) + } yield HttpResponse( + status = StatusCodes.OK, + entity = HttpEntity(bytes = schemaZipFileBytes) + ) - requestContext.complete(httpResponseFuture) + requestContext.complete(httpResponseFuture) } } } } /** - * Represents an XML import schema corresponding to an ontology. - * - * @param namespaceInfo information about the schema's namespace. - * @param schemaXml the XML text of the schema. - */ + * Represents an XML import schema corresponding to an ontology. + * + * @param namespaceInfo information about the schema's namespace. + * @param schemaXml the XML text of the schema. + */ case class XmlImportSchemaV1(namespaceInfo: XmlImportNamespaceInfoV1, schemaXml: String) /** - * Represents a bundle of XML import schemas corresponding to ontologies. - * - * @param mainNamespace the XML namespace corresponding to the main ontology to be used in the XML import. - * @param schemas a map of XML namespaces to schemas. - */ + * Represents a bundle of XML import schemas corresponding to ontologies. + * + * @param mainNamespace the XML namespace corresponding to the main ontology to be used in the XML import. + * @param schemas a map of XML namespaces to schemas. + */ case class XmlImportSchemaBundleV1(mainNamespace: IRI, schemas: Map[IRI, XmlImportSchemaV1]) /** - * An implementation of [[LSResourceResolver]] that resolves resources from a [[XmlImportSchemaBundleV1]]. - * This is used to allow the XML schema validator to load additional schemas during XML import data validation. - * - * @param schemaBundle an [[XmlImportSchemaBundleV1]]. - */ + * An implementation of [[LSResourceResolver]] that resolves resources from a [[XmlImportSchemaBundleV1]]. + * This is used to allow the XML schema validator to load additional schemas during XML import data validation. + * + * @param schemaBundle an [[XmlImportSchemaBundleV1]]. + */ class SchemaBundleResolver(schemaBundle: XmlImportSchemaBundleV1) extends LSResourceResolver { - private val contents: Map[IRI, Array[Byte]] = schemaBundle.schemas.map { - case (namespace, schema) => namespace -> schema.schemaXml.getBytes(StandardCharsets.UTF_8) + private val contents: Map[IRI, Array[Byte]] = schemaBundle.schemas.map { case (namespace, schema) => + namespace -> schema.schemaXml.getBytes(StandardCharsets.UTF_8) } private class ByteArrayLSInput(content: Array[Byte]) extends LSInput { @@ -1513,13 +1603,14 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) override def setSystemId(systemId: String): Unit = () } - override def resolveResource(`type`: String, - namespaceURI: String, - publicId: String, - systemId: String, - baseURI: String): LSInput = { + override def resolveResource( + `type`: String, + namespaceURI: String, + publicId: String, + systemId: String, + baseURI: String + ): LSInput = new ByteArrayLSInput(contents(namespaceURI)) - } } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala index 5b0de95792..bd3921bbc2 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala @@ -38,22 +38,24 @@ import scala.language.postfixOps // slash after path without following segment /** - * Provides a spray-routing function for API routes that deal with search. - */ + * Provides a spray-routing function for API routes that deal with search. + */ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * The default number of rows to show in search results. - */ + * The default number of rows to show in search results. + */ private val defaultShowNRows = 25 - def makeExtendedSearchRequestMessage(userADM: UserADM, - reverseParams: Map[String, Seq[String]]): ExtendedSearchGetRequestV1 = { + def makeExtendedSearchRequestMessage( + userADM: UserADM, + reverseParams: Map[String, Seq[String]] + ): ExtendedSearchGetRequestV1 = { val stringFormatter = StringFormatter.getGeneralInstance // Spray returns the parameters in reverse order, so reverse them before processing, because the JavaScript GUI expects the order to be preserved. - val params = reverseParams.map { - case (key, value) => key -> value.reverse + val params = reverseParams.map { case (key, value) => + key -> value.reverse } //println(params) @@ -70,8 +72,10 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit stringFormatter.validateAndEscapeIri( restype, throw BadRequestException( - s"Value for param 'filter_by_restype' for extended search $restype is not a valid IRI. Please make sure that it was correctly URL encoded.") - )) + s"Value for param 'filter_by_restype' for extended search $restype is not a valid IRI. Please make sure that it was correctly URL encoded." + ) + ) + ) case other => None } @@ -82,8 +86,10 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit stringFormatter.validateAndEscapeIri( project, throw BadRequestException( - s"Value for param 'filter_by_project' for extended search $project is not a valid IRI. Please make sure that it was correctly URL encoded.") - )) + s"Value for param 'filter_by_project' for extended search $project is not a valid IRI. Please make sure that it was correctly URL encoded." + ) + ) + ) case other => None } @@ -94,20 +100,22 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit stringFormatter.validateAndEscapeIri( owner, throw BadRequestException( - s"Value for param 'filter_by_owner' for extended search $owner is not a valid IRI. Please make sure that it was correctly URL encoded.") - )) + s"Value for param 'filter_by_owner' for extended search $owner is not a valid IRI. Please make sure that it was correctly URL encoded." + ) + ) + ) case other => None } // here, also multiple values can be given val propertyIri: Seq[IRI] = params.get("property_id") match { case Some(propertyList: Seq[IRI]) => - propertyList.map( - prop => - stringFormatter.validateAndEscapeIri( - prop, - throw BadRequestException( - s"Value for param 'property_id' for extended search $prop is not a valid IRI. Please make sure that it was correctly URL encoded.") + propertyList.map(prop => + stringFormatter.validateAndEscapeIri( + prop, + throw BadRequestException( + s"Value for param 'property_id' for extended search $prop is not a valid IRI. Please make sure that it was correctly URL encoded." + ) ) ) case other => Nil @@ -117,11 +125,9 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit // convert string to enum (SearchComparisonOperatorV1), throw error if unknown val compop: Seq[SearchComparisonOperatorV1.Value] = params.get("compop") match { case Some(compopList: Seq[String]) => - compopList.map( - (compop: String) => { - SearchComparisonOperatorV1.lookup(compop) - } - ) + compopList.map { (compop: String) => + SearchComparisonOperatorV1.lookup(compop) + } case other => Nil } @@ -144,7 +150,9 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val showNRowsVal = stringFormatter.validateInt( showNRowsStrList.head, throw BadRequestException( - s"Can't parse integer parameter 'show_nrows' for extended search: $showNRowsStrList")) + s"Can't parse integer parameter 'show_nrows' for extended search: $showNRowsStrList" + ) + ) showNRowsVal match { case -1 => defaultShowNRows case _ => showNRowsVal @@ -156,7 +164,8 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case Some(startAtStrList: Seq[String]) => stringFormatter.validateInt( startAtStrList.head, - throw BadRequestException(s"Can't parse integer parameter 'start_at' for extended search: $startAtStrList")) + throw BadRequestException(s"Can't parse integer parameter 'start_at' for extended search: $startAtStrList") + ) case None => 0 } @@ -173,9 +182,11 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } - def makeFulltextSearchRequestMessage(userADM: UserADM, - searchval: String, - params: Map[String, String]): FulltextSearchGetRequestV1 = { + def makeFulltextSearchRequestMessage( + userADM: UserADM, + searchval: String, + params: Map[String, String] + ): FulltextSearchGetRequestV1 = { val stringFormatter = StringFormatter.getGeneralInstance params.get("searchtype") match { @@ -188,7 +199,9 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit Some( stringFormatter.validateAndEscapeIri( restype, - throw BadRequestException(s"Unexpected param 'filter_by_restype' for extended search: $restype"))) + throw BadRequestException(s"Unexpected param 'filter_by_restype' for extended search: $restype") + ) + ) case other => None } val projectIri: Option[IRI] = params.get("filter_by_project") match { @@ -196,19 +209,23 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit Some( stringFormatter.validateAndEscapeIri( project, - throw BadRequestException(s"Unexpected param 'filter_by_project' for extended search: $project"))) + throw BadRequestException(s"Unexpected param 'filter_by_project' for extended search: $project") + ) + ) case other => None } val searchString = stringFormatter.toSparqlEncodedString( searchval, - throw BadRequestException(s"Invalid search string: '$searchval'")) + throw BadRequestException(s"Invalid search string: '$searchval'") + ) val showNRows: Int = params.get("show_nrows") match { case Some(showNRowsStr) => val showNRowsVal = stringFormatter.validateInt( showNRowsStr, - throw BadRequestException(s"Can't parse integer parameter 'show_nrows' for extended search: $showNRowsStr")) + throw BadRequestException(s"Can't parse integer parameter 'show_nrows' for extended search: $showNRowsStr") + ) showNRowsVal match { case -1 => defaultShowNRows case _ => showNRowsVal @@ -220,7 +237,8 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case Some(startAtStr) => stringFormatter.validateInt( startAtStr, - throw BadRequestException(s"Can't parse integer parameter 'start_at' for extended search: $startAtStr")) + throw BadRequestException(s"Can't parse integer parameter 'start_at' for extended search: $startAtStr") + ) case None => 0 } @@ -235,35 +253,32 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v1" / "search" /) { // in the original API, there is a slash after "search": "http://www.salsah.org/api/search/?searchtype=extended" get { requestContext => - { - val requestMessage = for { - userADM <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - params: Map[String, Seq[String]] = requestContext.request.uri.query().toMultiMap - } yield makeExtendedSearchRequestMessage(userADM, params) - - RouteUtilV1.runJsonRouteWithFuture( - requestMessage, - requestContext, - settings, - responderManager, - log + val requestMessage = for { + userADM <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig ) - } + params: Map[String, Seq[String]] = requestContext.request.uri.query().toMultiMap + } yield makeExtendedSearchRequestMessage(userADM, params) + + RouteUtilV1.runJsonRouteWithFuture( + requestMessage, + requestContext, + settings, + responderManager, + log + ) } } ~ - path("v1" / "search" / Segment) { searchval => // TODO: if a space is encoded as a "+", this is not converted back to a space - get { requestContext => - { + path("v1" / "search" / Segment) { + searchval => // TODO: if a space is encoded as a "+", this is not converted back to a space + get { requestContext => val requestMessage = for { userADM <- getUserADM( requestContext = requestContext, @@ -280,7 +295,5 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit log ) } - } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala index b5f94b3f33..8773c5ee34 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala @@ -36,15 +36,14 @@ import scala.concurrent.Future import scala.concurrent.duration._ /** - * A route used to convert XML to standoff. - */ + * A route used to convert XML to standoff. + */ class StandoffRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v1" / "mapping") { post { entity(as[Multipart.FormData]) { formdata: Multipart.FormData => requestContext => @@ -88,38 +87,44 @@ class StandoffRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) w allParts: Map[Name, String] <- allPartsFuture // get the json params and turn them into a case class - standoffApiJSONRequest: CreateMappingApiRequestV1 = try { + standoffApiJSONRequest: CreateMappingApiRequestV1 = + try { - val jsonString: String = allParts.getOrElse( - JSON_PART, - throw BadRequestException(s"MultiPart POST request was sent without required '$JSON_PART' part!")) + val jsonString: String = allParts.getOrElse( + JSON_PART, + throw BadRequestException(s"MultiPart POST request was sent without required '$JSON_PART' part!") + ) - jsonString.parseJson.convertTo[CreateMappingApiRequestV1] - } catch { - case e: DeserializationException => - throw BadRequestException("JSON params structure is invalid: " + e.toString) - } + jsonString.parseJson.convertTo[CreateMappingApiRequestV1] + } catch { + case e: DeserializationException => + throw BadRequestException("JSON params structure is invalid: " + e.toString) + } xml: String = allParts .getOrElse( XML_PART, - throw BadRequestException(s"MultiPart POST request was sent without required '$XML_PART' part!")) + throw BadRequestException(s"MultiPart POST request was sent without required '$XML_PART' part!") + ) .toString - } yield - CreateMappingRequestV1( - xml = xml, - label = - stringFormatter.toSparqlEncodedString(standoffApiJSONRequest.label, - throw BadRequestException("'label' contains invalid characters")), - projectIri = stringFormatter.validateAndEscapeIri(standoffApiJSONRequest.project_id, - throw BadRequestException("invalid project IRI")), - mappingName = stringFormatter.toSparqlEncodedString( - standoffApiJSONRequest.mappingName, - throw BadRequestException("'mappingName' contains invalid characters")), - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile, - apiRequestID = UUID.randomUUID - ) + } yield CreateMappingRequestV1( + xml = xml, + label = stringFormatter.toSparqlEncodedString( + standoffApiJSONRequest.label, + throw BadRequestException("'label' contains invalid characters") + ), + projectIri = stringFormatter.validateAndEscapeIri( + standoffApiJSONRequest.project_id, + throw BadRequestException("invalid project IRI") + ), + mappingName = stringFormatter.toSparqlEncodedString( + standoffApiJSONRequest.mappingName, + throw BadRequestException("'mappingName' contains invalid characters") + ), + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile, + apiRequestID = UUID.randomUUID + ) RouteUtilV1.runJsonRouteWithFuture( requestMessageFuture, @@ -131,5 +136,4 @@ class StandoffRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) w } } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala index 352784f04f..f91615a8d3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala @@ -30,16 +30,16 @@ import org.knora.webapi.messages.v1.responder.usermessages._ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilV1} /** - * Provides a spray-routing function for API routes that deal with lists. - */ + * Provides a spray-routing function for API routes that deal with lists. + */ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { private val schemes = Array("http", "https") private val urlValidator = new UrlValidator(schemes) /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { path("v1" / "users") { @@ -73,28 +73,28 @@ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - } yield - UserProfileByEmailGetRequestV1( - email = value, - userProfileType = UserProfileTypeV1.RESTRICTED, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile - ) + } yield UserProfileByEmailGetRequestV1( + email = value, + userProfileType = UserProfileTypeV1.RESTRICTED, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile + ) } else { for { userProfile <- getUserADM( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ).map(_.asUserProfileV1) - userIri = stringFormatter.validateAndEscapeIri(value, - throw BadRequestException(s"Invalid user IRI $value")) - } yield - UserProfileByIRIGetRequestV1( - userIri = userIri, - userProfileType = UserProfileTypeV1.RESTRICTED, - featureFactoryConfig = featureFactoryConfig, - userProfile = userProfile + userIri = stringFormatter.validateAndEscapeIri( + value, + throw BadRequestException(s"Invalid user IRI $value") ) + } yield UserProfileByIRIGetRequestV1( + userIri = userIri, + userProfileType = UserProfileTypeV1.RESTRICTED, + featureFactoryConfig = featureFactoryConfig, + userProfile = userProfile + ) } RouteUtilV1.runJsonRouteWithFuture( @@ -103,7 +103,7 @@ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with settings, responderManager, log - ) + ) } } } ~ @@ -118,13 +118,13 @@ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with ).map(_.asUserProfileV1) checkedUserIri = stringFormatter.validateAndEscapeIri( userIri, - throw BadRequestException(s"Invalid user IRI $userIri")) - } yield - UserProjectMembershipsGetRequestV1( - userIri = checkedUserIri, - userProfileV1 = userProfile, - apiRequestID = UUID.randomUUID() + throw BadRequestException(s"Invalid user IRI $userIri") ) + } yield UserProjectMembershipsGetRequestV1( + userIri = checkedUserIri, + userProfileV1 = userProfile, + apiRequestID = UUID.randomUUID() + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -146,13 +146,13 @@ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with ).map(_.asUserProfileV1) checkedUserIri = stringFormatter.validateAndEscapeIri( userIri, - throw BadRequestException(s"Invalid user IRI $userIri")) - } yield - UserProjectAdminMembershipsGetRequestV1( - userIri = checkedUserIri, - userProfileV1 = userProfile, - apiRequestID = UUID.randomUUID() + throw BadRequestException(s"Invalid user IRI $userIri") ) + } yield UserProjectAdminMembershipsGetRequestV1( + userIri = checkedUserIri, + userProfileV1 = userProfile, + apiRequestID = UUID.randomUUID() + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, @@ -174,13 +174,13 @@ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with ).map(_.asUserProfileV1) checkedUserIri = stringFormatter.validateAndEscapeIri( userIri, - throw BadRequestException(s"Invalid user IRI $userIri")) - } yield - UserGroupMembershipsGetRequestV1( - userIri = checkedUserIri, - userProfileV1 = userProfile, - apiRequestID = UUID.randomUUID() + throw BadRequestException(s"Invalid user IRI $userIri") ) + } yield UserGroupMembershipsGetRequestV1( + userIri = checkedUserIri, + userProfileV1 = userProfile, + apiRequestID = UUID.randomUUID() + ) RouteUtilV1.runJsonRouteWithFuture( requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala index acb0a37874..889f4004b3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala @@ -42,13 +42,13 @@ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, Rout import scala.concurrent.Future /** - * Provides an Akka routing function for API routes that deal with values. - */ + * Provides an Akka routing function for API routes that deal with values. + */ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { def makeVersionHistoryRequestMessage(iris: Seq[IRI], userADM: UserADM): ValueVersionHistoryGetRequestV1 = { @@ -59,13 +59,16 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val resourceIri = stringFormatter.validateAndEscapeIri( resourceIriStr, - throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) + throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") + ) val propertyIri = stringFormatter.validateAndEscapeIri( propertyIriStr, - throw BadRequestException(s"Invalid property IRI: $propertyIriStr")) + throw BadRequestException(s"Invalid property IRI: $propertyIriStr") + ) val currentValueIri = stringFormatter.validateAndEscapeIri( currentValueIriStr, - throw BadRequestException(s"Invalid value IRI: $currentValueIriStr")) + throw BadRequestException(s"Invalid value IRI: $currentValueIriStr") + ) ValueVersionHistoryGetRequestV1( resourceIri = resourceIri, @@ -83,13 +86,16 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val subjectIri = stringFormatter.validateAndEscapeIri( subjectIriStr, - throw BadRequestException(s"Invalid subject IRI: $subjectIriStr")) + throw BadRequestException(s"Invalid subject IRI: $subjectIriStr") + ) val predicateIri = stringFormatter.validateAndEscapeIri( predicateIriStr, - throw BadRequestException(s"Invalid predicate IRI: $predicateIriStr")) + throw BadRequestException(s"Invalid predicate IRI: $predicateIriStr") + ) val objectIri = stringFormatter.validateAndEscapeIri( objectIriStr, - throw BadRequestException(s"Invalid object IRI: $objectIriStr")) + throw BadRequestException(s"Invalid object IRI: $objectIriStr") + ) LinkValueGetRequestV1( subjectIri = subjectIri, @@ -100,14 +106,18 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } - def makeCreateValueRequestMessage(apiRequest: CreateValueApiRequestV1, - userADM: UserADM): Future[CreateValueRequestV1] = { + def makeCreateValueRequestMessage( + apiRequest: CreateValueApiRequestV1, + userADM: UserADM + ): Future[CreateValueRequestV1] = { val resourceIri = stringFormatter.validateAndEscapeIri( apiRequest.res_id, - throw BadRequestException(s"Invalid resource IRI ${apiRequest.res_id}")) + throw BadRequestException(s"Invalid resource IRI ${apiRequest.res_id}") + ) val propertyIri = stringFormatter.validateAndEscapeIri( apiRequest.prop, - throw BadRequestException(s"Invalid property IRI ${apiRequest.prop}")) + throw BadRequestException(s"Invalid property IRI ${apiRequest.prop}") + ) for { (value: UpdateValueV1, commentStr: Option[String]) <- apiRequest.getValueClassIri match { @@ -119,17 +129,24 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit if (richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty) { // simple text Future( - (TextValueSimpleV1(stringFormatter.toSparqlEncodedString( - richtext.utf8str.get, - throw BadRequestException(s"Invalid text: '${richtext.utf8str.get}'")), - richtext.language), - apiRequest.comment)) + ( + TextValueSimpleV1( + stringFormatter.toSparqlEncodedString( + richtext.utf8str.get, + throw BadRequestException(s"Invalid text: '${richtext.utf8str.get}'") + ), + richtext.language + ), + apiRequest.comment + ) + ) } else if (richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty) { // XML: text with markup val mappingIri = stringFormatter.validateAndEscapeIri( richtext.mapping_id.get, - throw BadRequestException(s"mapping_id ${richtext.mapping_id.get} is invalid")) + throw BadRequestException(s"mapping_id ${richtext.mapping_id.get} is invalid") + ) for { @@ -146,21 +163,23 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit // collect the resource references from the linking standoff nodes resourceReferences: Set[IRI] = stringFormatter.getResourceIrisFromStandoffTags( - textWithStandoffTags.standoffTagV2) - - } yield - (TextValueWithStandoffV1( - utf8str = - stringFormatter.toSparqlEncodedString(textWithStandoffTags.text, - throw InconsistentRepositoryDataException( - "utf8str for for TextValue contains invalid characters")), - language = textWithStandoffTags.language, - resource_reference = resourceReferences, - standoff = textWithStandoffTags.standoffTagV2, - mappingIri = textWithStandoffTags.mapping.mappingIri, - mapping = textWithStandoffTags.mapping.mapping - ), - apiRequest.comment) + textWithStandoffTags.standoffTagV2 + ) + + } yield ( + TextValueWithStandoffV1( + utf8str = stringFormatter.toSparqlEncodedString( + textWithStandoffTags.text, + throw InconsistentRepositoryDataException("utf8str for for TextValue contains invalid characters") + ), + language = textWithStandoffTags.language, + resource_reference = resourceReferences, + standoff = textWithStandoffTags.standoffTagV2, + mappingIri = textWithStandoffTags.mapping.mappingIri, + mapping = textWithStandoffTags.mapping.mapping + ), + apiRequest.comment + ) } else { throw BadRequestException("invalid parameters given for TextValueV1") @@ -169,7 +188,8 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.LinkValue => val resourceIRI = stringFormatter.validateAndEscapeIri( apiRequest.link_value.get, - throw BadRequestException(s"Invalid resource IRI: ${apiRequest.link_value.get}")) + throw BadRequestException(s"Invalid resource IRI: ${apiRequest.link_value.get}") + ) Future(LinkUpdateV1(targetResourceIri = resourceIRI), apiRequest.comment) case OntologyConstants.KnoraBase.IntValue => @@ -183,11 +203,16 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.UriValue => Future( - (UriValueV1( - stringFormatter.validateAndEscapeIri( - apiRequest.uri_value.get, - throw BadRequestException(s"Invalid URI: ${apiRequest.uri_value.get}"))), - apiRequest.comment)) + ( + UriValueV1( + stringFormatter.validateAndEscapeIri( + apiRequest.uri_value.get, + throw BadRequestException(s"Invalid URI: ${apiRequest.uri_value.get}") + ) + ), + apiRequest.comment + ) + ) case OntologyConstants.KnoraBase.DateValue => Future(DateUtilV1.createJDNValueV1FromDateString(apiRequest.date_value.get), apiRequest.comment) @@ -195,19 +220,22 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.ColorValue => val colorValue = stringFormatter.validateColor( apiRequest.color_value.get, - throw BadRequestException(s"Invalid color value: ${apiRequest.color_value.get}")) + throw BadRequestException(s"Invalid color value: ${apiRequest.color_value.get}") + ) Future(ColorValueV1(colorValue), apiRequest.comment) case OntologyConstants.KnoraBase.GeomValue => val geometryValue = stringFormatter.validateGeometryString( apiRequest.geom_value.get, - throw BadRequestException(s"Invalid geometry value: ${apiRequest.geom_value.get}")) + throw BadRequestException(s"Invalid geometry value: ${apiRequest.geom_value.get}") + ) Future(GeomValueV1(geometryValue), apiRequest.comment) case OntologyConstants.KnoraBase.ListValue => val listNodeIri = stringFormatter.validateAndEscapeIri( apiRequest.hlist_value.get, - throw BadRequestException(s"Invalid value IRI: ${apiRequest.hlist_value.get}")) + throw BadRequestException(s"Invalid value IRI: ${apiRequest.hlist_value.get}") + ) Future(HierarchicalListValueV1(listNodeIri), apiRequest.comment) case OntologyConstants.KnoraBase.IntervalValue => @@ -220,7 +248,8 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.TimeValue => val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( apiRequest.time_value.get, - throw BadRequestException(s"Invalid timestamp: ${apiRequest.time_value.get}")) + throw BadRequestException(s"Invalid timestamp: ${apiRequest.time_value.get}") + ) Future(TimeValueV1(timeStamp), apiRequest.comment) case OntologyConstants.KnoraBase.GeonameValue => @@ -228,22 +257,24 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case _ => throw BadRequestException(s"No value submitted") } - } yield - CreateValueRequestV1( - resourceIri = resourceIri, - propertyIri = propertyIri, - value = value, - comment = commentStr.map(str => - stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid comment: '$str'"))), - featureFactoryConfig = featureFactoryConfig, - userProfile = userADM, - apiRequestID = UUID.randomUUID - ) + } yield CreateValueRequestV1( + resourceIri = resourceIri, + propertyIri = propertyIri, + value = value, + comment = commentStr.map(str => + stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid comment: '$str'")) + ), + featureFactoryConfig = featureFactoryConfig, + userProfile = userADM, + apiRequestID = UUID.randomUUID + ) } - def makeAddValueVersionRequestMessage(valueIriStr: IRI, - apiRequest: ChangeValueApiRequestV1, - userADM: UserADM): Future[ChangeValueRequestV1] = { + def makeAddValueVersionRequestMessage( + valueIriStr: IRI, + apiRequest: ChangeValueApiRequestV1, + userADM: UserADM + ): Future[ChangeValueRequestV1] = { val valueIri = stringFormatter.validateAndEscapeIri(valueIriStr, throw BadRequestException(s"Invalid value IRI: $valueIriStr")) @@ -257,17 +288,24 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit if (richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty) { // simple text Future( - (TextValueSimpleV1(stringFormatter.toSparqlEncodedString( - richtext.utf8str.get, - throw BadRequestException(s"Invalid text: '${richtext.utf8str.get}'")), - richtext.language), - apiRequest.comment)) + ( + TextValueSimpleV1( + stringFormatter.toSparqlEncodedString( + richtext.utf8str.get, + throw BadRequestException(s"Invalid text: '${richtext.utf8str.get}'") + ), + richtext.language + ), + apiRequest.comment + ) + ) } else if (richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty) { // XML: text with markup val mappingIri = stringFormatter.validateAndEscapeIri( richtext.mapping_id.get, - throw BadRequestException(s"mapping_id ${richtext.mapping_id.get} is invalid")) + throw BadRequestException(s"mapping_id ${richtext.mapping_id.get} is invalid") + ) for { @@ -284,21 +322,23 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit // collect the resource references from the linking standoff nodes resourceReferences: Set[IRI] = stringFormatter.getResourceIrisFromStandoffTags( - textWithStandoffTags.standoffTagV2) - - } yield - (TextValueWithStandoffV1( - utf8str = - stringFormatter.toSparqlEncodedString(textWithStandoffTags.text, - throw InconsistentRepositoryDataException( - "utf8str for for TextValue contains invalid characters")), - language = richtext.language, - resource_reference = resourceReferences, - standoff = textWithStandoffTags.standoffTagV2, - mappingIri = textWithStandoffTags.mapping.mappingIri, - mapping = textWithStandoffTags.mapping.mapping - ), - apiRequest.comment) + textWithStandoffTags.standoffTagV2 + ) + + } yield ( + TextValueWithStandoffV1( + utf8str = stringFormatter.toSparqlEncodedString( + textWithStandoffTags.text, + throw InconsistentRepositoryDataException("utf8str for for TextValue contains invalid characters") + ), + language = richtext.language, + resource_reference = resourceReferences, + standoff = textWithStandoffTags.standoffTagV2, + mappingIri = textWithStandoffTags.mapping.mappingIri, + mapping = textWithStandoffTags.mapping.mapping + ), + apiRequest.comment + ) } else { throw BadRequestException("invalid parameters given for TextValueV1") @@ -307,7 +347,8 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.LinkValue => val resourceIRI = stringFormatter.validateAndEscapeIri( apiRequest.link_value.get, - throw BadRequestException(s"Invalid resource IRI: ${apiRequest.link_value.get}")) + throw BadRequestException(s"Invalid resource IRI: ${apiRequest.link_value.get}") + ) Future(LinkUpdateV1(targetResourceIri = resourceIRI), apiRequest.comment) case OntologyConstants.KnoraBase.IntValue => @@ -321,11 +362,16 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.UriValue => Future( - (UriValueV1( - stringFormatter.validateAndEscapeIri( - apiRequest.uri_value.get, - throw BadRequestException(s"Invalid URI: ${apiRequest.uri_value.get}"))), - apiRequest.comment)) + ( + UriValueV1( + stringFormatter.validateAndEscapeIri( + apiRequest.uri_value.get, + throw BadRequestException(s"Invalid URI: ${apiRequest.uri_value.get}") + ) + ), + apiRequest.comment + ) + ) case OntologyConstants.KnoraBase.DateValue => Future(DateUtilV1.createJDNValueV1FromDateString(apiRequest.date_value.get), apiRequest.comment) @@ -333,19 +379,22 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.ColorValue => val colorValue = stringFormatter.validateColor( apiRequest.color_value.get, - throw BadRequestException(s"Invalid color value: ${apiRequest.color_value.get}")) + throw BadRequestException(s"Invalid color value: ${apiRequest.color_value.get}") + ) Future(ColorValueV1(colorValue), apiRequest.comment) case OntologyConstants.KnoraBase.GeomValue => val geometryValue = stringFormatter.validateGeometryString( apiRequest.geom_value.get, - throw BadRequestException(s"Invalid geometry value: ${apiRequest.geom_value.get}")) + throw BadRequestException(s"Invalid geometry value: ${apiRequest.geom_value.get}") + ) Future(GeomValueV1(geometryValue), apiRequest.comment) case OntologyConstants.KnoraBase.ListValue => val listNodeIri = stringFormatter.validateAndEscapeIri( apiRequest.hlist_value.get, - throw BadRequestException(s"Invalid value IRI: ${apiRequest.hlist_value.get}")) + throw BadRequestException(s"Invalid value IRI: ${apiRequest.hlist_value.get}") + ) Future(HierarchicalListValueV1(listNodeIri), apiRequest.comment) case OntologyConstants.KnoraBase.IntervalValue => @@ -358,7 +407,8 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case OntologyConstants.KnoraBase.TimeValue => val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( apiRequest.time_value.get, - throw BadRequestException(s"Invalid timestamp: ${apiRequest.time_value.get}")) + throw BadRequestException(s"Invalid timestamp: ${apiRequest.time_value.get}") + ) Future(TimeValueV1(timeStamp), apiRequest.comment) case OntologyConstants.KnoraBase.GeonameValue => @@ -366,59 +416,64 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case _ => throw BadRequestException(s"No value submitted") } - } yield - ChangeValueRequestV1( - valueIri = valueIri, - value = value, - comment = commentStr.map(str => - stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid comment: '$str'"))), - featureFactoryConfig = featureFactoryConfig, - userProfile = userADM, - apiRequestID = UUID.randomUUID - ) + } yield ChangeValueRequestV1( + valueIri = valueIri, + value = value, + comment = commentStr.map(str => + stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid comment: '$str'")) + ), + featureFactoryConfig = featureFactoryConfig, + userProfile = userADM, + apiRequestID = UUID.randomUUID + ) } - def makeChangeCommentRequestMessage(valueIriStr: IRI, - comment: Option[String], - userADM: UserADM): ChangeCommentRequestV1 = { + def makeChangeCommentRequestMessage( + valueIriStr: IRI, + comment: Option[String], + userADM: UserADM + ): ChangeCommentRequestV1 = ChangeCommentRequestV1( - valueIri = stringFormatter.validateAndEscapeIri(valueIriStr, - throw BadRequestException(s"Invalid value IRI: $valueIriStr")), + valueIri = stringFormatter + .validateAndEscapeIri(valueIriStr, throw BadRequestException(s"Invalid value IRI: $valueIriStr")), comment = comment.map(str => - stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid comment: '$str'"))), + stringFormatter.toSparqlEncodedString(str, throw BadRequestException(s"Invalid comment: '$str'")) + ), featureFactoryConfig = featureFactoryConfig, userProfile = userADM, apiRequestID = UUID.randomUUID ) - } - def makeDeleteValueRequest(valueIriStr: IRI, - deleteComment: Option[String], - userADM: UserADM): DeleteValueRequestV1 = { + def makeDeleteValueRequest( + valueIriStr: IRI, + deleteComment: Option[String], + userADM: UserADM + ): DeleteValueRequestV1 = DeleteValueRequestV1( - valueIri = stringFormatter.validateAndEscapeIri(valueIriStr, - throw BadRequestException(s"Invalid value IRI: $valueIriStr")), + valueIri = stringFormatter + .validateAndEscapeIri(valueIriStr, throw BadRequestException(s"Invalid value IRI: $valueIriStr")), deleteComment = deleteComment.map(comment => - stringFormatter.toSparqlEncodedString(comment, throw BadRequestException(s"Invalid comment: '$comment'"))), + stringFormatter.toSparqlEncodedString(comment, throw BadRequestException(s"Invalid comment: '$comment'")) + ), featureFactoryConfig = featureFactoryConfig, userProfile = userADM, apiRequestID = UUID.randomUUID ) - } - def makeGetValueRequest(valueIriStr: IRI, userADM: UserADM): ValueGetRequestV1 = { + def makeGetValueRequest(valueIriStr: IRI, userADM: UserADM): ValueGetRequestV1 = ValueGetRequestV1( - valueIri = stringFormatter.validateAndEscapeIri(valueIriStr, - throw BadRequestException(s"Invalid value IRI: $valueIriStr")), + valueIri = stringFormatter + .validateAndEscapeIri(valueIriStr, throw BadRequestException(s"Invalid value IRI: $valueIriStr")), featureFactoryConfig = featureFactoryConfig, userProfile = userADM ) - } - def makeChangeFileValueRequest(resIriStr: IRI, - projectShortcode: String, - apiRequest: ChangeFileValueApiRequestV1, - userADM: UserADM): Future[ChangeFileValueRequestV1] = { + def makeChangeFileValueRequest( + resIriStr: IRI, + projectShortcode: String, + apiRequest: ChangeFileValueApiRequestV1, + userADM: UserADM + ): Future[ChangeFileValueRequestV1] = { val resourceIri = stringFormatter.validateAndEscapeIri(resIriStr, throw BadRequestException(s"Invalid resource IRI: $resIriStr")) val tempFileUrl = stringFormatter.makeSipiTempFileUrl(settings, apiRequest.file) @@ -426,40 +481,38 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit for { fileMetadataResponse: GetFileMetadataResponse <- (storeManager ? GetFileMetadataRequest( fileUrl = tempFileUrl, - requestingUser = userADM)).mapTo[GetFileMetadataResponse] - } yield - ChangeFileValueRequestV1( - resourceIri = resourceIri, - file = RouteUtilV1.makeFileValue( - filename = apiRequest.file, - fileMetadataResponse = fileMetadataResponse, - projectShortcode = projectShortcode - ), - apiRequestID = UUID.randomUUID, - featureFactoryConfig = featureFactoryConfig, - userProfile = userADM - ) + requestingUser = userADM + )).mapTo[GetFileMetadataResponse] + } yield ChangeFileValueRequestV1( + resourceIri = resourceIri, + file = RouteUtilV1.makeFileValue( + filename = apiRequest.file, + fileMetadataResponse = fileMetadataResponse, + projectShortcode = projectShortcode + ), + apiRequestID = UUID.randomUUID, + featureFactoryConfig = featureFactoryConfig, + userProfile = userADM + ) } // Version history request requires 3 URL path segments: resource IRI, property IRI, and current value IRI path("v1" / "values" / "history" / Segments) { iris => get { requestContext => - { - val requestMessage = for { - userADM <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield makeVersionHistoryRequestMessage(iris = iris, userADM = userADM) - - RouteUtilV1.runJsonRouteWithFuture( - requestMessage, - requestContext, - settings, - responderManager, - log + val requestMessage = for { + userADM <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig ) - } + } yield makeVersionHistoryRequestMessage(iris = iris, userADM = userADM) + + RouteUtilV1.runJsonRouteWithFuture( + requestMessage, + requestContext, + settings, + responderManager, + log + ) } } ~ path("v1" / "values") { post { @@ -483,22 +536,20 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } ~ path("v1" / "values" / Segment) { valueIriStr => get { requestContext => - { - val requestMessage = for { - userADM <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield makeGetValueRequest(valueIriStr = valueIriStr, userADM = userADM) - - RouteUtilV1.runJsonRouteWithFuture( - requestMessage, - requestContext, - settings, - responderManager, - log + val requestMessage = for { + userADM <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig ) - } + } yield makeGetValueRequest(valueIriStr = valueIriStr, userADM = userADM) + + RouteUtilV1.runJsonRouteWithFuture( + requestMessage, + requestContext, + settings, + responderManager, + log + ) } ~ put { entity(as[ChangeValueApiRequestV1]) { apiRequest => requestContext => // In API v1, you cannot change a value and its comment in a single request. So we know that here, @@ -511,9 +562,8 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit request <- apiRequest match { case ChangeValueApiRequestV1(_, _, _, _, _, _, _, _, _, _, _, _, _, Some(comment)) => FastFuture.successful( - makeChangeCommentRequestMessage(valueIriStr = valueIriStr, - comment = Some(comment), - userADM = userADM)) + makeChangeCommentRequestMessage(valueIriStr = valueIriStr, comment = Some(comment), userADM = userADM) + ) case _ => makeAddValueVersionRequestMessage(valueIriStr = valueIriStr, apiRequest = apiRequest, userADM = userADM) } @@ -525,66 +575,60 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit settings, responderManager, log - ) + ) } } ~ delete { requestContext => - { - val requestMessage = for { - userADM <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - params = requestContext.request.uri.query().toMap - deleteComment = params.get("deleteComment") - } yield makeDeleteValueRequest(valueIriStr = valueIriStr, deleteComment = deleteComment, userADM = userADM) - - RouteUtilV1.runJsonRouteWithFuture( - requestMessage, - requestContext, - settings, - responderManager, - log + val requestMessage = for { + userADM <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig ) - } + params = requestContext.request.uri.query().toMap + deleteComment = params.get("deleteComment") + } yield makeDeleteValueRequest(valueIriStr = valueIriStr, deleteComment = deleteComment, userADM = userADM) + + RouteUtilV1.runJsonRouteWithFuture( + requestMessage, + requestContext, + settings, + responderManager, + log + ) } } ~ path("v1" / "valuecomments" / Segment) { valueIriStr => delete { requestContext => - { - val requestMessage = for { - userADM <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield makeChangeCommentRequestMessage(valueIriStr = valueIriStr, comment = None, userADM = userADM) - - RouteUtilV1.runJsonRouteWithFuture( - requestMessage, - requestContext, - settings, - responderManager, - log + val requestMessage = for { + userADM <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig ) - } + } yield makeChangeCommentRequestMessage(valueIriStr = valueIriStr, comment = None, userADM = userADM) + + RouteUtilV1.runJsonRouteWithFuture( + requestMessage, + requestContext, + settings, + responderManager, + log + ) } } ~ path("v1" / "links" / Segments) { iris => // Link value request requires 3 URL path segments: subject IRI, predicate IRI, and object IRI get { requestContext => - { - val requestMessage = for { - userADM <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield makeLinkValueGetRequestMessage(iris = iris, userADM = userADM) - - RouteUtilV1.runJsonRouteWithFuture( - requestMessage, - requestContext, - settings, - responderManager, - log + val requestMessage = for { + userADM <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig ) - } + } yield makeLinkValueGetRequestMessage(iris = iris, userADM = userADM) + + RouteUtilV1.runJsonRouteWithFuture( + requestMessage, + requestContext, + settings, + responderManager, + log + ) } } ~ path("v1" / "filevalue" / Segment) { resIriStr: IRI => put { @@ -596,7 +640,8 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) resourceIri = stringFormatter.validateAndEscapeIri( resIriStr, - throw BadRequestException(s"Invalid resource IRI: $resIriStr")) + throw BadRequestException(s"Invalid resource IRI: $resIriStr") + ) resourceInfoResponse <- (responderManager ? ResourceInfoGetRequestV1( iri = resourceIri, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala index 2b1fd86ed9..12177bffad 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala @@ -31,28 +31,25 @@ import org.knora.webapi.messages.v2.routing.authenticationmessages.{ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData} /** - * A route providing API v2 authentication support. It allows the creation of "sessions", which are used in the SALSAH app. - */ + * A route providing API v2 authentication support. It allows the creation of "sessions", which are used in the SALSAH app. + */ class AuthenticationRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator with AuthenticationV2JsonProtocol { /** - * Returns the route. - */ - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { - + * Returns the route. + */ + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "authentication") { get { // authenticate credentials requestContext => - { - requestContext.complete { - doAuthenticateV2( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } + requestContext.complete { + doAuthenticateV2( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } } ~ post { // login @@ -95,10 +92,8 @@ class AuthenticationRouteV2(routeData: KnoraRouteData) path("v2" / "login") { get { // html login interface (necessary for IIIF Authentication API support) requestContext => - { - requestContext.complete { - presentLoginFormV2(requestContext) - } + requestContext.complete { + presentLoginFormV2(requestContext) } } ~ post { // called by html login interface (necessary for IIIF Authentication API support) @@ -119,5 +114,4 @@ class AuthenticationRouteV2(routeData: KnoraRouteData) } } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala index e1915a4b03..a4a81f76b3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala @@ -30,13 +30,13 @@ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, Rout import scala.concurrent.Future /** - * Provides a function for API routes that deal with lists and nodes. - */ + * Provides a function for API routes that deal with lists and nodes. + */ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = getList(featureFactoryConfig) ~ getNode(featureFactoryConfig) @@ -51,14 +51,15 @@ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - listIri: IRI = stringFormatter.validateAndEscapeIri(lIri, - throw BadRequestException(s"Invalid list IRI: '$lIri'")) - } yield - ListGetRequestV2( - listIri = listIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + listIri: IRI = stringFormatter.validateAndEscapeIri( + lIri, + throw BadRequestException(s"Invalid list IRI: '$lIri'") ) + } yield ListGetRequestV2( + listIri = listIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, @@ -83,14 +84,15 @@ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - nodeIri: IRI = stringFormatter.validateAndEscapeIri(nIri, - throw BadRequestException(s"Invalid list IRI: '$nIri'")) - } yield - NodeGetRequestV2( - nodeIri = nodeIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser + nodeIri: IRI = stringFormatter.validateAndEscapeIri( + nIri, + throw BadRequestException(s"Invalid list IRI: '$nIri'") ) + } yield NodeGetRequestV2( + nodeIri = nodeIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/MetadataRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/MetadataRouteV2.scala index a6cc44e343..1d14835f32 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/MetadataRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/MetadataRouteV2.scala @@ -17,63 +17,60 @@ object MetadataRouteV2 { } /** - * Provides a routing function for API v2 routes that deal with metadata. - */ + * Provides a routing function for API v2 routes that deal with metadata. + */ class MetadataRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { import MetadataRouteV2._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = getMetadata(featureFactoryConfig) ~ setMetadata(featureFactoryConfig) /** - * Route to get metadata. - */ + * Route to get metadata. + */ private def getMetadata(featureFactoryConfig: FeatureFactoryConfig): Route = path(MetadataBasePath / Segment) { projectIri => get { requestContext => - { - // Make the request message. - val requestMessageFuture: Future[MetadataGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - - project <- getProjectADM( - projectIri = projectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - } yield - MetadataGetRequestV2( - projectADM = project, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - // Send it to the responder. - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + // Make the request message. + val requestMessageFuture: Future[MetadataGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + + project <- getProjectADM( + projectIri = projectIri, featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = InternalSchema, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + requestingUser = requestingUser ) - } + } yield MetadataGetRequestV2( + projectADM = project, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + // Send it to the responder. + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = InternalSchema, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } /** - * Route to set a project's metadata, replacing any existing metadata for the project. - */ + * Route to set a project's metadata, replacing any existing metadata for the project. + */ private def setMetadata(featureFactoryConfig: FeatureFactoryConfig): Route = path(MetadataBasePath / Segment) { projectIri => put { @@ -98,14 +95,13 @@ class MetadataRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) w featureFactoryConfig = featureFactoryConfig, requestingUser = requestingUser ) - } yield - MetadataPutRequestV2( - rdfModel = requestModel, - projectADM = project, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = UUID.randomUUID - ) + } yield MetadataPutRequestV2( + rdfModel = requestModel, + projectADM = project, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = UUID.randomUUID + ) // Send it to the responder. RouteUtilV2.runRdfRouteWithFuture( diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala index 1eb232809f..e51dd48e6f 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala @@ -45,7 +45,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) import OntologiesRouteV2._ - private val ALL_LANGUAGES = "allLanguages" + private val ALL_LANGUAGES = "allLanguages" private val LAST_MODIFICATION_DATE = "lastModificationDate" /** @@ -110,7 +110,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguagesStr = params.get(ALL_LANGUAGES) val allLanguages = stringFormatter.optionStringToBoolean( allLanguagesStr, throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") @@ -118,9 +118,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[OntologyEntitiesGetRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield OntologyEntitiesGetRequestV2( ontologyIri = requestedOntology, allLanguages = allLanguages, @@ -147,9 +147,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[OntologyMetadataGetByProjectRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield OntologyMetadataGetByProjectRequestV2( projectIris = maybeProjectIri.toSet, requestingUser = requestingUser @@ -177,20 +177,20 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[ChangeOntologyMetadataRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestMessage: ChangeOntologyMetadataRequestV2 <- ChangeOntologyMetadataRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -213,9 +213,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) get { requestContext => val requestMessageFuture: Future[OntologyMetadataGetByProjectRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) validatedProjectIris = projectIris @@ -254,7 +254,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguagesStr = params.get(ALL_LANGUAGES) val allLanguages = stringFormatter.optionStringToBoolean( params.get(ALL_LANGUAGES), throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") @@ -262,9 +262,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[OntologyEntitiesGetRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield OntologyEntitiesGetRequestV2( ontologyIri = requestedOntologyIri, allLanguages = allLanguages, @@ -291,22 +291,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CreateClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: CreateClassRequestV2 <- CreateClassRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -331,22 +331,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangeClassLabelsOrCommentsRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage <- ChangeClassLabelsOrCommentsRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -372,22 +372,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[AddCardinalitiesToClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: AddCardinalitiesToClassRequestV2 <- AddCardinalitiesToClassRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -417,9 +417,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanChangeCardinalitiesRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield CanChangeCardinalitiesRequestV2( classIri = classIri, featureFactoryConfig = featureFactoryConfig, @@ -448,22 +448,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangeCardinalitiesRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangeCardinalitiesRequestV2 <- ChangeCardinalitiesRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -488,9 +488,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CanDeleteCardinalitiesFromClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) @@ -531,22 +531,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[DeleteCardinalitiesFromClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: DeleteCardinalitiesFromClassRequestV2 <- DeleteCardinalitiesFromClassRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -572,22 +572,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangeGuiOrderRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangeGuiOrderRequestV2 <- ChangeGuiOrderRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -641,7 +641,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguagesStr = params.get(ALL_LANGUAGES) val allLanguages = stringFormatter.optionStringToBoolean( params.get(ALL_LANGUAGES), throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") @@ -649,9 +649,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[ClassesGetRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield ClassesGetRequestV2( classIris = classesForResponder, allLanguages = allLanguages, @@ -683,9 +683,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanDeleteClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield CanDeleteClassRequestV2( classIri = classIri, featureFactoryConfig = featureFactoryConfig, @@ -731,9 +731,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[DeleteClassRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield DeleteClassRequestV2( classIri = classIri, lastModificationDate = lastModificationDate, @@ -776,9 +776,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[DeleteOntologyCommentRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield DeleteOntologyCommentRequestV2( ontologyIri = ontologyIri, lastModificationDate = lastModificationDate, @@ -808,22 +808,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CreatePropertyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: CreatePropertyRequestV2 <- CreatePropertyRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -849,24 +849,23 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangePropertyLabelsOrCommentsRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangePropertyLabelsOrCommentsRequestV2 <- ChangePropertyLabelsOrCommentsRequestV2 - .fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = - featureFactoryConfig, - settings = settings, - log = log - ) + .fromJsonLD( + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -892,23 +891,23 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[ChangePropertyGuiElementRequest] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: ChangePropertyGuiElementRequest <- ChangePropertyGuiElementRequest - .fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + .fromJsonLD( + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -962,7 +961,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } val params: Map[String, String] = requestContext.request.uri.query().toMap - val allLanguagesStr = params.get(ALL_LANGUAGES) + val allLanguagesStr = params.get(ALL_LANGUAGES) val allLanguages = stringFormatter.optionStringToBoolean( params.get(ALL_LANGUAGES), throw BadRequestException(s"Invalid boolean for $ALL_LANGUAGES: $allLanguagesStr") @@ -970,9 +969,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[PropertiesGetRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield PropertiesGetRequestV2( propertyIris = propsForResponder, allLanguages = allLanguages, @@ -1004,9 +1003,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanDeletePropertyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield CanDeletePropertyRequestV2( propertyIri = propertyIri, featureFactoryConfig = featureFactoryConfig, @@ -1052,9 +1051,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[DeletePropertyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield DeletePropertyRequestV2( propertyIri = propertyIri, lastModificationDate = lastModificationDate, @@ -1083,22 +1082,22 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) { val requestMessageFuture: Future[CreateOntologyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest) requestMessage: CreateOntologyRequestV2 <- CreateOntologyRequestV2.fromJsonLD( - jsonLDDocument = requestDoc, - apiRequestID = UUID.randomUUID, - requestingUser = requestingUser, - responderManager = responderManager, - storeManager = storeManager, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - log = log - ) + jsonLDDocument = requestDoc, + apiRequestID = UUID.randomUUID, + requestingUser = requestingUser, + responderManager = responderManager, + storeManager = storeManager, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + log = log + ) } yield requestMessage RouteUtilV2.runRdfRouteWithFuture( @@ -1128,9 +1127,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[CanDeleteOntologyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield CanDeleteOntologyRequestV2( ontologyIri = ontologyIri, featureFactoryConfig = featureFactoryConfig, @@ -1174,9 +1173,9 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) val requestMessageFuture: Future[DeleteOntologyRequestV2] = for { requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) } yield DeleteOntologyRequestV2( ontologyIri = ontologyIri, lastModificationDate = lastModificationDate, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala index 3a46e707a0..2b10530edf 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala @@ -41,8 +41,8 @@ object ResourcesRouteV2 { } /** - * Provides a routing function for API v2 routes that deal with resources. - */ + * Provides a routing function for API v2 routes that deal with resources. + */ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { import ResourcesRouteV2._ @@ -59,8 +59,8 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private val Both = "both" /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = getIIIFManifest(featureFactoryConfig) ~ createResource(featureFactoryConfig) ~ @@ -80,20 +80,21 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) path(ResourcesBasePath / "iiifmanifest" / Segment) { resourceIriStr: IRI => get { requestContext => val resourceIri: IRI = - stringFormatter.validateAndEscapeIri(resourceIriStr, - throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) + stringFormatter.validateAndEscapeIri( + resourceIriStr, + throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") + ) val requestMessageFuture: Future[ResourceIIIFManifestGetRequestV2] = for { requestingUser <- getUserADM( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - ResourceIIIFManifestGetRequestV2( - resourceIri = resourceIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ResourceIIIFManifestGetRequestV2( + resourceIri = resourceIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, @@ -188,59 +189,106 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) private def getResourcesInProject(featureFactoryConfig: FeatureFactoryConfig): Route = path(ResourcesBasePath) { get { requestContext => - { - val projectIri: SmartIri = RouteUtilV2 - .getProject(requestContext) - .getOrElse(throw BadRequestException(s"This route requires the request header ${RouteUtilV2.PROJECT_HEADER}")) - val params: Map[String, String] = requestContext.request.uri.query().toMap + val projectIri: SmartIri = RouteUtilV2 + .getProject(requestContext) + .getOrElse(throw BadRequestException(s"This route requires the request header ${RouteUtilV2.PROJECT_HEADER}")) + val params: Map[String, String] = requestContext.request.uri.query().toMap + + val resourceClassStr: String = + params.getOrElse( + "resourceClass", + throw BadRequestException(s"This route requires the parameter 'resourceClass'") + ) + val resourceClass: SmartIri = + resourceClassStr.toSmartIriWithErr(throw BadRequestException(s"Invalid resource class IRI: $resourceClassStr")) - val resourceClassStr: String = - params.getOrElse("resourceClass", - throw BadRequestException(s"This route requires the parameter 'resourceClass'")) - val resourceClass: SmartIri = resourceClassStr.toSmartIriWithErr( - throw BadRequestException(s"Invalid resource class IRI: $resourceClassStr")) + if (!(resourceClass.isKnoraApiV2EntityIri && resourceClass.getOntologySchema.contains(ApiV2Complex))) { + throw BadRequestException(s"Invalid resource class IRI: $resourceClassStr") + } - if (!(resourceClass.isKnoraApiV2EntityIri && resourceClass.getOntologySchema.contains(ApiV2Complex))) { - throw BadRequestException(s"Invalid resource class IRI: $resourceClassStr") + val maybeOrderByPropertyStr: Option[String] = params.get("orderByProperty") + val maybeOrderByProperty: Option[SmartIri] = maybeOrderByPropertyStr.map { orderByPropertyStr => + val orderByProperty = + orderByPropertyStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: $orderByPropertyStr")) + + if (!(orderByProperty.isKnoraApiV2EntityIri && orderByProperty.getOntologySchema.contains(ApiV2Complex))) { + throw BadRequestException(s"Invalid property IRI: $orderByPropertyStr") } - val maybeOrderByPropertyStr: Option[String] = params.get("orderByProperty") - val maybeOrderByProperty: Option[SmartIri] = maybeOrderByPropertyStr.map { orderByPropertyStr => - val orderByProperty = orderByPropertyStr.toSmartIriWithErr( - throw BadRequestException(s"Invalid property IRI: $orderByPropertyStr")) + orderByProperty.toOntologySchema(ApiV2Complex) + } - if (!(orderByProperty.isKnoraApiV2EntityIri && orderByProperty.getOntologySchema.contains(ApiV2Complex))) { - throw BadRequestException(s"Invalid property IRI: $orderByPropertyStr") - } + val pageStr: String = + params.getOrElse("page", throw BadRequestException(s"This route requires the parameter 'page'")) + val page: Int = + stringFormatter.validateInt(pageStr, throw BadRequestException(s"Invalid page number: $pageStr")) - orderByProperty.toOntologySchema(ApiV2Complex) - } + val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - val pageStr: String = - params.getOrElse("page", throw BadRequestException(s"This route requires the parameter 'page'")) - val page: Int = - stringFormatter.validateInt(pageStr, throw BadRequestException(s"Invalid page number: $pageStr")) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) + val requestMessageFuture: Future[SearchResourcesByProjectAndClassRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield SearchResourcesByProjectAndClassRequestV2( + projectIri = projectIri, + resourceClass = resourceClass.toOntologySchema(ApiV2Complex), + orderByProperty = maybeOrderByProperty, + page = page, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = schemaOptions + ) + } + } - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + private def getResourceHistory(featureFactoryConfig: FeatureFactoryConfig): Route = + path(ResourcesBasePath / "history" / Segment) { resourceIriStr: IRI => + get { requestContext => + val resourceIri = + stringFormatter.validateAndEscapeIri( + resourceIriStr, + throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") + ) + val params: Map[String, String] = requestContext.request.uri.query().toMap + val startDate: Option[Instant] = params + .get("startDate") + .map(dateStr => + stringFormatter + .xsdDateTimeStampToInstant(dateStr, throw BadRequestException(s"Invalid start date: $dateStr")) + ) + val endDate = params + .get("endDate") + .map(dateStr => + stringFormatter.xsdDateTimeStampToInstant(dateStr, throw BadRequestException(s"Invalid end date: $dateStr")) + ) - val requestMessageFuture: Future[SearchResourcesByProjectAndClassRequestV2] = for { + val requestMessageFuture: Future[ResourceVersionHistoryGetRequestV2] = for { requestingUser <- getUserADM( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - SearchResourcesByProjectAndClassRequestV2( - projectIri = projectIri, - resourceClass = resourceClass.toOntologySchema(ApiV2Complex), - orderByProperty = maybeOrderByProperty, - page = page, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield ResourceVersionHistoryGetRequestV2( + resourceIri = resourceIri, + startDate = startDate, + endDate = endDate, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, @@ -250,322 +298,258 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) responderManager = responderManager, log = log, targetSchema = ApiV2Complex, - schemaOptions = schemaOptions + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) ) } } - } - - private def getResourceHistory(featureFactoryConfig: FeatureFactoryConfig): Route = - path(ResourcesBasePath / "history" / Segment) { resourceIriStr: IRI => - get { requestContext => - { - val resourceIri = - stringFormatter.validateAndEscapeIri(resourceIriStr, - throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) - val params: Map[String, String] = requestContext.request.uri.query().toMap - val startDate: Option[Instant] = params - .get("startDate") - .map(dateStr => - stringFormatter.xsdDateTimeStampToInstant(dateStr, - throw BadRequestException(s"Invalid start date: $dateStr"))) - val endDate = params - .get("endDate") - .map(dateStr => - stringFormatter.xsdDateTimeStampToInstant(dateStr, - throw BadRequestException(s"Invalid end date: $dateStr"))) - - val requestMessageFuture: Future[ResourceVersionHistoryGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ResourceVersionHistoryGetRequestV2( - resourceIri = resourceIri, - startDate = startDate, - endDate = endDate, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } - } - } private def getResourceHistoryEvents(featureFactoryConfig: FeatureFactoryConfig): Route = path(ResourcesBasePath / "resourceHistoryEvents" / Segment) { resourceIri: IRI => get { requestContext => - { - val requestMessageFuture: Future[ResourceHistoryEventsGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ResourceHistoryEventsGetRequestV2( - resourceIri = resourceIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[ResourceHistoryEventsGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + featureFactoryConfig = featureFactoryConfig ) - } + } yield ResourceHistoryEventsGetRequestV2( + resourceIri = resourceIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } private def getProjectResourceAndValueHistory(featureFactoryConfig: FeatureFactoryConfig): Route = path(ResourcesBasePath / "projectHistoryEvents" / Segment) { projectIri: IRI => get { requestContext => - { - val requestMessageFuture: Future[ProjectResourcesWithHistoryGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ProjectResourcesWithHistoryGetRequestV2( - projectIri = projectIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[ProjectResourcesWithHistoryGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + featureFactoryConfig = featureFactoryConfig ) - } + } yield ProjectResourcesWithHistoryGetRequestV2( + projectIri = projectIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } private def getResources(featureFactoryConfig: FeatureFactoryConfig): Route = path(ResourcesBasePath / Segments) { resIris: Seq[String] => get { requestContext => - { - - if (resIris.size > settings.v2ResultsPerPage) - throw BadRequestException(s"List of provided resource Iris exceeds limit of ${settings.v2ResultsPerPage}") + if (resIris.size > settings.v2ResultsPerPage) + throw BadRequestException(s"List of provided resource Iris exceeds limit of ${settings.v2ResultsPerPage}") - val resourceIris: Seq[IRI] = resIris.map { resIri: String => - stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: <$resIri>")) - } + val resourceIris: Seq[IRI] = resIris.map { resIri: String => + stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: <$resIri>")) + } - val params: Map[String, String] = requestContext.request.uri.query().toMap + val params: Map[String, String] = requestContext.request.uri.query().toMap - // Was a version date provided? - val versionDate: Option[Instant] = params.get("version").map { versionStr => - def errorFun: Nothing = throw BadRequestException(s"Invalid version date: $versionStr") + // Was a version date provided? + val versionDate: Option[Instant] = params.get("version").map { versionStr => + def errorFun: Nothing = throw BadRequestException(s"Invalid version date: $versionStr") - // Yes. Try to parse it as an xsd:dateTimeStamp. - try { - stringFormatter.xsdDateTimeStampToInstant(versionStr, errorFun) - } catch { - // If that doesn't work, try to parse it as a Knora ARK timestamp. - case _: Exception => stringFormatter.arkTimestampToInstant(versionStr, errorFun) - } + // Yes. Try to parse it as an xsd:dateTimeStamp. + try { + stringFormatter.xsdDateTimeStampToInstant(versionStr, errorFun) + } catch { + // If that doesn't work, try to parse it as a Knora ARK timestamp. + case _: Exception => stringFormatter.arkTimestampToInstant(versionStr, errorFun) } + } - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - - val requestMessageFuture: Future[ResourcesGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ResourcesGetRequestV2( - resourceIris = resourceIris, - versionDate = versionDate, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[ResourcesGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = schemaOptions + featureFactoryConfig = featureFactoryConfig ) - } + } yield ResourcesGetRequestV2( + resourceIris = resourceIris, + versionDate = versionDate, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) } } private def getResourcesPreview(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "resourcespreview" / Segments) { resIris: Seq[String] => get { requestContext => - { - if (resIris.size > settings.v2ResultsPerPage) - throw BadRequestException(s"List of provided resource Iris exceeds limit of ${settings.v2ResultsPerPage}") + if (resIris.size > settings.v2ResultsPerPage) + throw BadRequestException(s"List of provided resource Iris exceeds limit of ${settings.v2ResultsPerPage}") - val resourceIris: Seq[IRI] = resIris.map { resIri: String => - stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: <$resIri>")) - } - - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val resourceIris: Seq[IRI] = resIris.map { resIri: String => + stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: <$resIri>")) + } - val requestMessageFuture: Future[ResourcesPreviewGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ResourcesPreviewGetRequestV2( - resourceIris = resourceIris, - targetSchema = targetSchema, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[ResourcesPreviewGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = RouteUtilV2.getOntologySchema(requestContext), - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + featureFactoryConfig = featureFactoryConfig ) - } + } yield ResourcesPreviewGetRequestV2( + resourceIris = resourceIris, + targetSchema = targetSchema, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = RouteUtilV2.getOntologySchema(requestContext), + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } private def getResourcesTei(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "tei" / Segment) { resIri: String => get { requestContext => - { - val resourceIri: IRI = - stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: <$resIri>")) - - val params: Map[String, String] = requestContext.request.uri.query().toMap + val resourceIri: IRI = + stringFormatter.validateAndEscapeIri(resIri, throw BadRequestException(s"Invalid resource IRI: <$resIri>")) - // the the property that represents the text - val textProperty: SmartIri = getTextPropertyFromParams(params) + val params: Map[String, String] = requestContext.request.uri.query().toMap - val mappingIri: Option[IRI] = getMappingIriFromParams(params) + // the the property that represents the text + val textProperty: SmartIri = getTextPropertyFromParams(params) - val gravsearchTemplateIri: Option[IRI] = getGravsearchTemplateIriFromParams(params) + val mappingIri: Option[IRI] = getMappingIriFromParams(params) - val headerXSLTIri = getHeaderXSLTIriFromParams(params) + val gravsearchTemplateIri: Option[IRI] = getGravsearchTemplateIriFromParams(params) - val requestMessageFuture: Future[ResourceTEIGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ResourceTEIGetRequestV2( - resourceIri = resourceIri, - textProperty = textProperty, - mappingIri = mappingIri, - gravsearchTemplateIri = gravsearchTemplateIri, - headerXSLTIri = headerXSLTIri, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val headerXSLTIri = getHeaderXSLTIriFromParams(params) - RouteUtilV2.runTEIXMLRoute( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[ResourceTEIGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = RouteUtilV2.getOntologySchema(requestContext) + featureFactoryConfig = featureFactoryConfig ) - } + } yield ResourceTEIGetRequestV2( + resourceIri = resourceIri, + textProperty = textProperty, + mappingIri = mappingIri, + gravsearchTemplateIri = gravsearchTemplateIri, + headerXSLTIri = headerXSLTIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runTEIXMLRoute( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = RouteUtilV2.getOntologySchema(requestContext) + ) } } private def getResourcesGraph(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "graph" / Segment) { resIriStr: String => get { requestContext => - { - val resourceIri: IRI = - stringFormatter.validateAndEscapeIri(resIriStr, - throw BadRequestException(s"Invalid resource IRI: <$resIriStr>")) - val params: Map[String, String] = requestContext.request.uri.query().toMap - val depth: Int = params.get(Depth).map(_.toInt).getOrElse(settings.defaultGraphDepth) - - if (depth < 1) { - throw BadRequestException(s"$Depth must be at least 1") - } + val resourceIri: IRI = + stringFormatter.validateAndEscapeIri( + resIriStr, + throw BadRequestException(s"Invalid resource IRI: <$resIriStr>") + ) + val params: Map[String, String] = requestContext.request.uri.query().toMap + val depth: Int = params.get(Depth).map(_.toInt).getOrElse(settings.defaultGraphDepth) - if (depth > settings.maxGraphDepth) { - throw BadRequestException(s"$Depth cannot be greater than ${settings.maxGraphDepth}") - } + if (depth < 1) { + throw BadRequestException(s"$Depth must be at least 1") + } - val direction: String = params.getOrElse(Direction, Outbound) - val excludeProperty: Option[SmartIri] = params - .get(ExcludeProperty) - .map(propIriStr => - propIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: <$propIriStr>"))) - - val (inbound: Boolean, outbound: Boolean) = direction match { - case Inbound => (true, false) - case Outbound => (false, true) - case Both => (true, true) - case other => throw BadRequestException(s"Invalid direction: $other") - } + if (depth > settings.maxGraphDepth) { + throw BadRequestException(s"$Depth cannot be greater than ${settings.maxGraphDepth}") + } - val requestMessageFuture: Future[GraphDataGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - GraphDataGetRequestV2( - resourceIri = resourceIri, - depth = depth, - inbound = inbound, - outbound = outbound, - excludeProperty = excludeProperty, - requestingUser = requestingUser - ) + val direction: String = params.getOrElse(Direction, Outbound) + val excludeProperty: Option[SmartIri] = params + .get(ExcludeProperty) + .map(propIriStr => + propIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: <$propIriStr>")) + ) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val (inbound: Boolean, outbound: Boolean) = direction match { + case Inbound => (true, false) + case Outbound => (false, true) + case Both => (true, true) + case other => throw BadRequestException(s"Invalid direction: $other") + } + + val requestMessageFuture: Future[GraphDataGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = RouteUtilV2.getOntologySchema(requestContext), - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + featureFactoryConfig = featureFactoryConfig ) - } + } yield GraphDataGetRequestV2( + resourceIri = resourceIri, + depth = depth, + inbound = inbound, + outbound = outbound, + excludeProperty = excludeProperty, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = RouteUtilV2.getOntologySchema(requestContext), + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) } } @@ -647,11 +631,11 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } /** - * Gets the Iri of the property that represents the text of the resource. - * - * @param params the GET parameters. - * @return the internal resource class, if any. - */ + * Gets the Iri of the property that represents the text of the resource. + * + * @param params the GET parameters. + * @return the internal resource class, if any. + */ private def getTextPropertyFromParams(params: Map[String, String]): SmartIri = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val textProperty = params.get(Text_Property) @@ -672,11 +656,11 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } /** - * Gets the Iri of the mapping to be used to convert standoff to XML. - * - * @param params the GET parameters. - * @return the internal resource class, if any. - */ + * Gets the Iri of the mapping to be used to convert standoff to XML. + * + * @param params the GET parameters. + * @return the internal resource class, if any. + */ private def getMappingIriFromParams(params: Map[String, String]): Option[IRI] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val mappingIriStr = params.get(Mapping_Iri) @@ -684,18 +668,19 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) mappingIriStr match { case Some(mapping: String) => Some( - stringFormatter.validateAndEscapeIri(mapping, throw BadRequestException(s"Invalid mapping IRI: <$mapping>"))) + stringFormatter.validateAndEscapeIri(mapping, throw BadRequestException(s"Invalid mapping IRI: <$mapping>")) + ) case None => None } } /** - * Gets the Iri of Gravsearch template to be used to query for the resource's metadata. - * - * @param params the GET parameters. - * @return the internal resource class, if any. - */ + * Gets the Iri of Gravsearch template to be used to query for the resource's metadata. + * + * @param params the GET parameters. + * @return the internal resource class, if any. + */ private def getGravsearchTemplateIriFromParams(params: Map[String, String]): Option[IRI] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val gravsearchTemplateIriStr = params.get(GravsearchTemplate_Iri) @@ -703,19 +688,22 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) gravsearchTemplateIriStr match { case Some(gravsearch: String) => Some( - stringFormatter.validateAndEscapeIri(gravsearch, - throw BadRequestException(s"Invalid template IRI: <$gravsearch>"))) + stringFormatter.validateAndEscapeIri( + gravsearch, + throw BadRequestException(s"Invalid template IRI: <$gravsearch>") + ) + ) case None => None } } /** - * Gets the Iri of the XSL transformation to be used to convert the TEI header's metadata. - * - * @param params the GET parameters. - * @return the internal resource class, if any. - */ + * Gets the Iri of the XSL transformation to be used to convert the TEI header's metadata. + * + * @param params the GET parameters. + * @return the internal resource class, if any. + */ private def getHeaderXSLTIriFromParams(params: Map[String, String]): Option[IRI] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val headerXSLTIriStr = params.get(TEIHeader_XSLT_IRI) diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala index b5f95864a3..ff78c75ce2 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala @@ -33,8 +33,8 @@ import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, Rout import scala.concurrent.Future /** - * Provides a function for API routes that deal with search. - */ + * Provides a function for API routes that deal with search. + */ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { private val LIMIT_TO_PROJECT = "limitToProject" @@ -44,8 +44,8 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit private val RETURN_FILES = "returnFiles" /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = fullTextSearchCount(featureFactoryConfig) ~ fullTextSearch(featureFactoryConfig) ~ @@ -57,11 +57,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit searchByLabel(featureFactoryConfig) /** - * Gets the requested offset. Returns zero if no offset is indicated. - * - * @param params the GET parameters. - * @return the offset to be used for paging. - */ + * Gets the requested offset. Returns zero if no offset is indicated. + * + * @param params the GET parameters. + * @return the offset to be used for paging. + */ private def getOffsetFromParams(params: Map[String, String]): Int = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val offsetStr = params.get(OFFSET) @@ -70,7 +70,8 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case Some(offset: String) => val offsetInt: Int = stringFormatter.validateInt( offset, - throw BadRequestException(s"offset is expected to be an Integer, but $offset given")) + throw BadRequestException(s"offset is expected to be an Integer, but $offset given") + ) if (offsetInt < 0) throw BadRequestException(s"offset must be an Integer >= 0, but $offsetInt given.") @@ -81,11 +82,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } /** - * Gets the the project the search should be restricted to, if any. - * - * @param params the GET parameters. - * @return the project Iri, if any. - */ + * Gets the the project the search should be restricted to, if any. + * + * @param params the GET parameters. + * @return the project Iri, if any. + */ private def getProjectFromParams(params: Map[String, String]): Option[IRI] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val limitToProjectIriStr = params.get(LIMIT_TO_PROJECT) @@ -95,7 +96,8 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit case Some(projectIriStr: String) => val projectIri = stringFormatter.validateAndEscapeIri( projectIriStr, - throw BadRequestException(s"$projectIriStr is not a valid Iri")) + throw BadRequestException(s"$projectIriStr is not a valid Iri") + ) Some(projectIri) @@ -108,11 +110,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } /** - * Gets the resource class the search should be restricted to, if any. - * - * @param params the GET parameters. - * @return the internal resource class, if any. - */ + * Gets the resource class the search should be restricted to, if any. + * + * @param params the GET parameters. + * @return the internal resource class, if any. + */ private def getResourceClassFromParams(params: Map[String, String]): Option[SmartIri] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val limitToResourceClassIriStr = params.get(LIMIT_TO_RESOURCE_CLASS) @@ -120,7 +122,8 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit limitToResourceClassIriStr match { case Some(resourceClassIriStr: String) => val externalResourceClassIri = resourceClassIriStr.toSmartIriWithErr( - throw BadRequestException(s"Invalid resource class IRI: $resourceClassIriStr")) + throw BadRequestException(s"Invalid resource class IRI: $resourceClassIriStr") + ) if (!externalResourceClassIri.isKnoraApiV2EntityIri) { throw BadRequestException(s"$resourceClassIriStr is not a valid knora-api resource class IRI") @@ -133,11 +136,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } /** - * Gets the standoff class the search should be restricted to. - * - * @param params the GET parameters. - * @return the internal standoff class, if any. - */ + * Gets the standoff class the search should be restricted to. + * + * @param params the GET parameters. + * @return the internal standoff class, if any. + */ private def getStandoffClass(params: Map[String, String]): Option[SmartIri] = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val limitToStandoffClassIriStr: Option[String] = params.get(LIMIT_TO_STANDOFF_CLASS) @@ -145,7 +148,8 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit limitToStandoffClassIriStr match { case Some(standoffClassIriStr: String) => val externalStandoffClassIri = standoffClassIriStr.toSmartIriWithErr( - throw BadRequestException(s"Invalid standoff class IRI: $limitToStandoffClassIriStr")) + throw BadRequestException(s"Invalid standoff class IRI: $limitToStandoffClassIriStr") + ) if (!externalStandoffClassIri.getOntologySchema.contains(ApiV2Complex)) { throw BadRequestException(s"$externalStandoffClassIri is not a valid standoff class IRI") @@ -158,111 +162,48 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } private def fullTextSearchCount(featureFactoryConfig: FeatureFactoryConfig): Route = - path("v2" / "search" / "count" / Segment) { searchStr => // TODO: if a space is encoded as a "+", this is not converted back to a space - get { requestContext => - if (searchStr.contains(OntologyConstants.KnoraApi.ApiOntologyHostname)) { - throw BadRequestException("It looks like you are submitting a Gravsearch request to a full-text search route") - } - - val escapedSearchStr = - stringFormatter.toSparqlEncodedString(searchStr, - throw BadRequestException(s"Invalid search string: '$searchStr'")) - - if (escapedSearchStr.length < settings.searchValueMinLength) { - throw BadRequestException( - s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$escapedSearchStr' given of length ${escapedSearchStr.length}.") - } - - val params: Map[String, String] = requestContext.request.uri.query().toMap - - val limitToProject: Option[IRI] = getProjectFromParams(params) - - val limitToResourceClass: Option[SmartIri] = getResourceClassFromParams(params) - - val limitToStandoffClass: Option[SmartIri] = getStandoffClass(params) - - val requestMessage: Future[FullTextSearchCountRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - FullTextSearchCountRequestV2( - searchValue = escapedSearchStr, - limitToProject = limitToProject, - limitToResourceClass = limitToResourceClass, - limitToStandoffClass = limitToStandoffClass, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessage, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = RouteUtilV2.getOntologySchema(requestContext), - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } - } - - private def fullTextSearch(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "search" / Segment) { - searchStr => // TODO: if a space is encoded as a "+", this is not converted back to a space - get { requestContext => - { + path("v2" / "search" / "count" / Segment) { + searchStr => // TODO: if a space is encoded as a "+", this is not converted back to a space + get { requestContext => if (searchStr.contains(OntologyConstants.KnoraApi.ApiOntologyHostname)) { throw BadRequestException( - "It looks like you are submitting a Gravsearch request to a full-text search route") + "It looks like you are submitting a Gravsearch request to a full-text search route" + ) } val escapedSearchStr = - stringFormatter.toSparqlEncodedString(searchStr, - throw BadRequestException(s"Invalid search string: '$searchStr'")) + stringFormatter.toSparqlEncodedString( + searchStr, + throw BadRequestException(s"Invalid search string: '$searchStr'") + ) if (escapedSearchStr.length < settings.searchValueMinLength) { throw BadRequestException( - s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$escapedSearchStr' given of length ${escapedSearchStr.length}.") + s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$escapedSearchStr' given of length ${escapedSearchStr.length}." + ) } val params: Map[String, String] = requestContext.request.uri.query().toMap - val offset = getOffsetFromParams(params) - val limitToProject: Option[IRI] = getProjectFromParams(params) val limitToResourceClass: Option[SmartIri] = getResourceClassFromParams(params) val limitToStandoffClass: Option[SmartIri] = getStandoffClass(params) - val returnFiles: Boolean = stringFormatter.optionStringToBoolean( - params.get(RETURN_FILES), - throw BadRequestException(s"Invalid boolean value for '$RETURN_FILES'") - ) - - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - - val requestMessage: Future[FulltextSearchRequestV2] = for { + val requestMessage: Future[FullTextSearchCountRequestV2] = for { requestingUser <- getUserADM( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - FulltextSearchRequestV2( - searchValue = escapedSearchStr, - offset = offset, - limitToProject = limitToProject, - limitToResourceClass = limitToResourceClass, - limitToStandoffClass = limitToStandoffClass, - returnFiles = returnFiles, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - targetSchema = targetSchema, - schemaOptions = schemaOptions - ) + } yield FullTextSearchCountRequestV2( + searchValue = escapedSearchStr, + limitToProject = limitToProject, + limitToResourceClass = limitToResourceClass, + limitToStandoffClass = limitToStandoffClass, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, @@ -271,17 +212,84 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit settings = settings, responderManager = responderManager, log = log, - targetSchema = targetSchema, - schemaOptions = schemaOptions + targetSchema = RouteUtilV2.getOntologySchema(requestContext), + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) ) } + } + + private def fullTextSearch(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "search" / Segment) { + searchStr => // TODO: if a space is encoded as a "+", this is not converted back to a space + get { requestContext => + if (searchStr.contains(OntologyConstants.KnoraApi.ApiOntologyHostname)) { + throw BadRequestException("It looks like you are submitting a Gravsearch request to a full-text search route") + } + + val escapedSearchStr = + stringFormatter.toSparqlEncodedString( + searchStr, + throw BadRequestException(s"Invalid search string: '$searchStr'") + ) + + if (escapedSearchStr.length < settings.searchValueMinLength) { + throw BadRequestException( + s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$escapedSearchStr' given of length ${escapedSearchStr.length}." + ) + } + + val params: Map[String, String] = requestContext.request.uri.query().toMap + + val offset = getOffsetFromParams(params) + + val limitToProject: Option[IRI] = getProjectFromParams(params) + + val limitToResourceClass: Option[SmartIri] = getResourceClassFromParams(params) + + val limitToStandoffClass: Option[SmartIri] = getStandoffClass(params) + + val returnFiles: Boolean = stringFormatter.optionStringToBoolean( + params.get(RETURN_FILES), + throw BadRequestException(s"Invalid boolean value for '$RETURN_FILES'") + ) + + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) + + val requestMessage: Future[FulltextSearchRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield FulltextSearchRequestV2( + searchValue = escapedSearchStr, + offset = offset, + limitToProject = limitToProject, + limitToResourceClass = limitToResourceClass, + limitToStandoffClass = limitToStandoffClass, + returnFiles = returnFiles, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessage, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) } } private def gravsearchCountGet(featureFactoryConfig: FeatureFactoryConfig): Route = - path("v2" / "searchextended" / "count" / Segment) { gravsearchQuery => // Segment is a URL encoded string representing a Gravsearch query - get { requestContext => - { + path("v2" / "searchextended" / "count" / Segment) { + gravsearchQuery => // Segment is a URL encoded string representing a Gravsearch query + get { requestContext => val constructQuery = GravsearchParser.parseQuery(gravsearchQuery) val requestMessage: Future[GravsearchCountRequestV2] = for { @@ -289,12 +297,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GravsearchCountRequestV2( - constructQuery = constructQuery, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield GravsearchCountRequestV2( + constructQuery = constructQuery, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, @@ -307,7 +314,6 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) ) } - } } private def gravsearchCountPost(featureFactoryConfig: FeatureFactoryConfig): Route = @@ -321,12 +327,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GravsearchCountRequestV2( - constructQuery = constructQuery, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield GravsearchCountRequestV2( + constructQuery = constructQuery, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, @@ -343,40 +348,38 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } } - private def gravsearchGet(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "searchextended" / Segment) { - sparql => // Segment is a URL encoded string representing a Gravsearch query - get { requestContext => - { - val constructQuery = GravsearchParser.parseQuery(sparql) - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) + private def gravsearchGet(featureFactoryConfig: FeatureFactoryConfig): Route = path( + "v2" / "searchextended" / Segment + ) { sparql => // Segment is a URL encoded string representing a Gravsearch query + get { requestContext => + val constructQuery = GravsearchParser.parseQuery(sparql) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - val requestMessage: Future[GravsearchRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - GravsearchRequestV2( - constructQuery = constructQuery, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) - - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessage, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = schemaOptions - ) - } - } + val requestMessage: Future[GravsearchRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield GravsearchRequestV2( + constructQuery = constructQuery, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessage, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) + } } private def gravsearchPost(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "searchextended") { @@ -392,14 +395,13 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - GravsearchRequestV2( - constructQuery = constructQuery, - targetSchema = targetSchema, - schemaOptions = schemaOptions, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield GravsearchRequestV2( + constructQuery = constructQuery, + targetSchema = targetSchema, + schemaOptions = schemaOptions, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, @@ -417,17 +419,19 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } private def searchByLabelCount(featureFactoryConfig: FeatureFactoryConfig): Route = - path("v2" / "searchbylabel" / "count" / Segment) { searchval => // TODO: if a space is encoded as a "+", this is not converted back to a space - get { requestContext => - { - + path("v2" / "searchbylabel" / "count" / Segment) { + searchval => // TODO: if a space is encoded as a "+", this is not converted back to a space + get { requestContext => val searchString = - stringFormatter.toSparqlEncodedString(searchval, - throw BadRequestException(s"Invalid search string: '$searchval'")) + stringFormatter.toSparqlEncodedString( + searchval, + throw BadRequestException(s"Invalid search string: '$searchval'") + ) if (searchString.length < settings.searchValueMinLength) { throw BadRequestException( - s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$searchString' given of length ${searchString.length}.") + s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$searchString' given of length ${searchString.length}." + ) } val params: Map[String, String] = requestContext.request.uri.query().toMap @@ -441,14 +445,13 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - } yield - SearchResourceByLabelCountRequestV2( - searchValue = searchString, - limitToProject = limitToProject, - limitToResourceClass = limitToResourceClass, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + } yield SearchResourceByLabelCountRequestV2( + searchValue = searchString, + limitToProject = limitToProject, + limitToResourceClass = limitToResourceClass, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessage, @@ -461,60 +464,60 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) ) } - } } - private def searchByLabel(featureFactoryConfig: FeatureFactoryConfig): Route = path("v2" / "searchbylabel" / Segment) { - searchval => // TODO: if a space is encoded as a "+", this is not converted back to a space - get { requestContext => - { - val searchString = - stringFormatter.toSparqlEncodedString(searchval, - throw BadRequestException(s"Invalid search string: '$searchval'")) - - if (searchString.length < settings.searchValueMinLength) { - throw BadRequestException( - s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$searchString' given of length ${searchString.length}.") - } + private def searchByLabel(featureFactoryConfig: FeatureFactoryConfig): Route = path( + "v2" / "searchbylabel" / Segment + ) { searchval => // TODO: if a space is encoded as a "+", this is not converted back to a space + get { requestContext => + val searchString = + stringFormatter.toSparqlEncodedString( + searchval, + throw BadRequestException(s"Invalid search string: '$searchval'") + ) - val params: Map[String, String] = requestContext.request.uri.query().toMap + if (searchString.length < settings.searchValueMinLength) { + throw BadRequestException( + s"A search value is expected to have at least length of ${settings.searchValueMinLength}, but '$searchString' given of length ${searchString.length}." + ) + } - val offset = getOffsetFromParams(params) + val params: Map[String, String] = requestContext.request.uri.query().toMap - val limitToProject: Option[IRI] = getProjectFromParams(params) + val offset = getOffsetFromParams(params) - val limitToResourceClass: Option[SmartIri] = getResourceClassFromParams(params) + val limitToProject: Option[IRI] = getProjectFromParams(params) - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val limitToResourceClass: Option[SmartIri] = getResourceClassFromParams(params) - val requestMessage: Future[SearchResourceByLabelRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - SearchResourceByLabelRequestV2( - searchValue = searchString, - offset = offset, - limitToProject = limitToProject, - limitToResourceClass = limitToResourceClass, - targetSchema = targetSchema, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessage, - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = RouteUtilV2.getOntologySchema(requestContext), - schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) - ) - } - } + val requestMessage: Future[SearchResourceByLabelRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield SearchResourceByLabelRequestV2( + searchValue = searchString, + offset = offset, + limitToProject = limitToProject, + limitToResourceClass = limitToResourceClass, + targetSchema = targetSchema, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessage, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = RouteUtilV2.getOntologySchema(requestContext), + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) + } } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala index d9439e80a2..b97b83b546 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala @@ -43,65 +43,62 @@ import scala.concurrent.Future import scala.concurrent.duration._ /** - * Provides a function for API routes that deal with search. - */ + * Provides a function for API routes that deal with search. + */ class StandoffRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { path("v2" / "standoff" / Segment / Segment / Segment) { (resourceIriStr: String, valueIriStr: String, offsetStr: String) => get { requestContext => - { - val resourceIri: SmartIri = - resourceIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) - - if (!resourceIri.isKnoraResourceIri) { - throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") - } + val resourceIri: SmartIri = + resourceIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) - val valueIri: SmartIri = - valueIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid value IRI: $valueIriStr")) + if (!resourceIri.isKnoraResourceIri) { + throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") + } - if (!valueIri.isKnoraValueIri) { - throw BadRequestException(s"Invalid value IRI: $valueIriStr") - } + val valueIri: SmartIri = + valueIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid value IRI: $valueIriStr")) - val offset: Int = - stringFormatter.validateInt(offsetStr, throw BadRequestException(s"Invalid offset: $offsetStr")) - val schemaOptions: Set[SchemaOption] = SchemaOptions.ForStandoffSeparateFromTextValues + if (!valueIri.isKnoraValueIri) { + throw BadRequestException(s"Invalid value IRI: $valueIriStr") + } - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val offset: Int = + stringFormatter.validateInt(offsetStr, throw BadRequestException(s"Invalid offset: $offsetStr")) + val schemaOptions: Set[SchemaOption] = SchemaOptions.ForStandoffSeparateFromTextValues - val requestMessageFuture: Future[GetStandoffPageRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - GetStandoffPageRequestV2( - resourceIri = resourceIri.toString, - valueIri = valueIri.toString, - offset = offset, - targetSchema = targetSchema, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[GetStandoffPageRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = ApiV2Complex, - schemaOptions = schemaOptions + featureFactoryConfig = featureFactoryConfig ) - } + } yield GetStandoffPageRequestV2( + resourceIri = resourceIri.toString, + valueIri = valueIri.toString, + offset = offset, + targetSchema = targetSchema, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = schemaOptions + ) } } ~ path("v2" / "mapping") { post { @@ -149,8 +146,10 @@ class StandoffRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) w allParts .getOrElse( JSON_PART, - throw BadRequestException(s"MultiPart POST request was sent without required '$JSON_PART' part!")) - .toString) + throw BadRequestException(s"MultiPart POST request was sent without required '$JSON_PART' part!") + ) + .toString + ) metadata: CreateMappingRequestMetadataV2 <- CreateMappingRequestMetadataV2.fromJsonLD( jsonLDDocument = jsonldDoc, @@ -166,16 +165,16 @@ class StandoffRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) w xml: String = allParts .getOrElse( XML_PART, - throw BadRequestException(s"MultiPart POST request was sent without required '$XML_PART' part!")) + throw BadRequestException(s"MultiPart POST request was sent without required '$XML_PART' part!") + ) .toString - } yield - CreateMappingRequestV2( - metadata = metadata, - xml = CreateMappingRequestXMLV2(xml), - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser, - apiRequestID = apiRequestID - ) + } yield CreateMappingRequestV2( + metadata = metadata, + xml = CreateMappingRequestXMLV2(xml), + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser, + apiRequestID = apiRequestID + ) RouteUtilV2.runRdfRouteWithFuture( requestMessageF = requestMessageFuture, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala index d27067109b..ee29921aec 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala @@ -41,15 +41,15 @@ object ValuesRouteV2 { } /** - * Provides a routing function for API v2 routes that deal with values. - */ + * Provides a routing function for API v2 routes that deal with values. + */ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with Authenticator { import ValuesRouteV2._ /** - * Returns the route. - */ + * Returns the route. + */ override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = getValue(featureFactoryConfig) ~ createValue(featureFactoryConfig) ~ @@ -59,62 +59,61 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit private def getValue(featureFactoryConfig: FeatureFactoryConfig): Route = path(ValuesBasePath / Segment / Segment) { (resourceIriStr: IRI, valueUuidStr: String) => get { requestContext => - { - val resourceIri: SmartIri = - resourceIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) + val resourceIri: SmartIri = + resourceIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid resource IRI: $resourceIriStr")) - if (!resourceIri.isKnoraResourceIri) { - throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") - } + if (!resourceIri.isKnoraResourceIri) { + throw BadRequestException(s"Invalid resource IRI: $resourceIriStr") + } - val valueUuid: UUID = - stringFormatter.decodeUuidWithErr(valueUuidStr, - throw BadRequestException(s"Invalid value UUID: $valueUuidStr")) + val valueUuid: UUID = + stringFormatter.decodeUuidWithErr( + valueUuidStr, + throw BadRequestException(s"Invalid value UUID: $valueUuidStr") + ) - val params: Map[String, String] = requestContext.request.uri.query().toMap + val params: Map[String, String] = requestContext.request.uri.query().toMap - // Was a version date provided? - val versionDate: Option[Instant] = params.get("version").map { versionStr => - def errorFun: Nothing = throw BadRequestException(s"Invalid version date: $versionStr") + // Was a version date provided? + val versionDate: Option[Instant] = params.get("version").map { versionStr => + def errorFun: Nothing = throw BadRequestException(s"Invalid version date: $versionStr") - // Yes. Try to parse it as an xsd:dateTimeStamp. - try { - stringFormatter.xsdDateTimeStampToInstant(versionStr, errorFun) - } catch { - // If that doesn't work, try to parse it as a Knora ARK timestamp. - case _: Exception => stringFormatter.arkTimestampToInstant(versionStr, errorFun) - } + // Yes. Try to parse it as an xsd:dateTimeStamp. + try { + stringFormatter.xsdDateTimeStampToInstant(versionStr, errorFun) + } catch { + // If that doesn't work, try to parse it as a Knora ARK timestamp. + case _: Exception => stringFormatter.arkTimestampToInstant(versionStr, errorFun) } + } - val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) - val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - - val requestMessageFuture: Future[ResourcesGetRequestV2] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) - } yield - ResourcesGetRequestV2( - resourceIris = Seq(resourceIri.toString), - valueUuid = Some(valueUuid), - versionDate = versionDate, - targetSchema = targetSchema, - featureFactoryConfig = featureFactoryConfig, - requestingUser = requestingUser - ) + val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext) + val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext) - RouteUtilV2.runRdfRouteWithFuture( - requestMessageF = requestMessageFuture, + val requestMessageFuture: Future[ResourcesGetRequestV2] = for { + requestingUser <- getUserADM( requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig, - settings = settings, - responderManager = responderManager, - log = log, - targetSchema = targetSchema, - schemaOptions = schemaOptions + featureFactoryConfig = featureFactoryConfig ) - } + } yield ResourcesGetRequestV2( + resourceIris = Seq(resourceIri.toString), + valueUuid = Some(valueUuid), + versionDate = versionDate, + targetSchema = targetSchema, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = targetSchema, + schemaOptions = schemaOptions + ) } } diff --git a/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala b/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala index 62011d7f41..65ed0b9d2e 100644 --- a/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala +++ b/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala @@ -34,8 +34,8 @@ import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} /** - * Reads application settings that come from `application.conf`. - */ + * Reads application settings that come from `application.conf`. + */ class KnoraSettingsImpl(config: Config, log: LoggingAdapter) extends Extension { import KnoraSettings._ @@ -57,10 +57,10 @@ class KnoraSettingsImpl(config: Config, log: LoggingAdapter) extends Extension { val externalKnoraApiHostPort: String = externalKnoraApiHost + (if (externalKnoraApiPort != 80) ":" + externalKnoraApiPort else "") - val externalKnoraApiBaseUrl - : String = externalKnoraApiProtocol + "://" + externalKnoraApiHost + (if (externalKnoraApiPort != 80) - ":" + externalKnoraApiPort - else "") + val externalKnoraApiBaseUrl: String = + externalKnoraApiProtocol + "://" + externalKnoraApiHost + (if (externalKnoraApiPort != 80) + ":" + externalKnoraApiPort + else "") // If the external hostname is localhost, include the configured external port number in ontology IRIs for manual testing. val externalOntologyIriHostAndPort: String = @@ -312,62 +312,63 @@ class KnoraSettingsImpl(config: Config, log: LoggingAdapter) extends Extension { .getObject(featureTogglesPath) .asScala .toMap - .map { - case (featureName: String, featureConfigValue: ConfigValue) => - val featureConfig: Config = featureConfigValue match { - case configObject: ConfigObject => configObject.toConfig - case _ => throw FeatureToggleException(s"The feature toggle configuration $featureName must be an object") - } - - val description: String = featureConfig.getString(descriptionKey) - val availableVersions: Seq[Int] = - featureConfig.getIntList(availableVersionsKey).asScala.map(_.intValue).toVector - - if (availableVersions.isEmpty) { - throw FeatureToggleException(s"Feature toggle $featureName has no version numbers") - } - - for ((version: Int, index: Int) <- availableVersions.zipWithIndex) { - if (version != index + 1) { - throw FeatureToggleException( - s"The version numbers of feature toggle $featureName must be an ascending sequence of consecutive integers starting from 1") - } - } - - val defaultVersion: Int = featureConfig.getInt(defaultVersionKey) - - if (!availableVersions.contains(defaultVersion)) { + .map { case (featureName: String, featureConfigValue: ConfigValue) => + val featureConfig: Config = featureConfigValue match { + case configObject: ConfigObject => configObject.toConfig + case _ => throw FeatureToggleException(s"The feature toggle configuration $featureName must be an object") + } + + val description: String = featureConfig.getString(descriptionKey) + val availableVersions: Seq[Int] = + featureConfig.getIntList(availableVersionsKey).asScala.map(_.intValue).toVector + + if (availableVersions.isEmpty) { + throw FeatureToggleException(s"Feature toggle $featureName has no version numbers") + } + + for ((version: Int, index: Int) <- availableVersions.zipWithIndex) { + if (version != index + 1) { throw FeatureToggleException( - s"Invalid default version number $defaultVersion for feature toggle $featureName") + s"The version numbers of feature toggle $featureName must be an ascending sequence of consecutive integers starting from 1" + ) } + } - val enabledByDefault: Boolean = featureConfig.getBoolean(enabledByDefaultKey) - val overrideAllowed: Boolean = featureConfig.getBoolean(overrideAllowedKey) + val defaultVersion: Int = featureConfig.getInt(defaultVersionKey) - val expirationDate: Option[Instant] = if (featureConfig.hasPath(expirationDateKey)) { - val definedExpirationDate: Instant = Instant.parse(featureConfig.getString(expirationDateKey)) + if (!availableVersions.contains(defaultVersion)) { + throw FeatureToggleException( + s"Invalid default version number $defaultVersion for feature toggle $featureName" + ) + } + + val enabledByDefault: Boolean = featureConfig.getBoolean(enabledByDefaultKey) + val overrideAllowed: Boolean = featureConfig.getBoolean(overrideAllowedKey) - if (Instant.ofEpochMilli(System.currentTimeMillis).isAfter(definedExpirationDate)) { - log.warning(s"Feature toggle $featureName has expired") - } + val expirationDate: Option[Instant] = if (featureConfig.hasPath(expirationDateKey)) { + val definedExpirationDate: Instant = Instant.parse(featureConfig.getString(expirationDateKey)) - Some(definedExpirationDate) - } else { - None + if (Instant.ofEpochMilli(System.currentTimeMillis).isAfter(definedExpirationDate)) { + log.warning(s"Feature toggle $featureName has expired") } - val developerEmails: Set[String] = featureConfig.getStringList(developerEmailsKey).asScala.toSet - - FeatureToggleBaseConfig( - featureName = featureName, - description = description, - availableVersions = availableVersions, - defaultVersion = defaultVersion, - enabledByDefault = enabledByDefault, - overrideAllowed = overrideAllowed, - expirationDate = expirationDate, - developerEmails = developerEmails - ) + Some(definedExpirationDate) + } else { + None + } + + val developerEmails: Set[String] = featureConfig.getStringList(developerEmailsKey).asScala.toSet + + FeatureToggleBaseConfig( + featureName = featureName, + description = description, + availableVersions = availableVersions, + defaultVersion = defaultVersion, + enabledByDefault = enabledByDefault, + overrideAllowed = overrideAllowed, + expirationDate = expirationDate, + developerEmails = developerEmails + ) } .toSet } match { @@ -392,8 +393,8 @@ object KnoraSettings extends ExtensionId[KnoraSettingsImpl] with ExtensionIdProv new KnoraSettingsImpl(system.settings.config, akka.event.Logging(system, this.getClass)) /** - * Java API: retrieve the Settings extension for the given system. - */ + * Java API: retrieve the Settings extension for the given system. + */ override def get(system: ActorSystem): KnoraSettingsImpl = super.get(system) val featureTogglesPath: String = "app.feature-toggles" @@ -406,26 +407,28 @@ object KnoraSettings extends ExtensionId[KnoraSettingsImpl] with ExtensionIdProv val overrideAllowedKey: String = "override-allowed" /** - * Represents the base configuration of a feature toggle. - * - * @param featureName the name of the feature. - * @param description a description of the feature. - * @param availableVersions the available versions of the feature. - * @param defaultVersion the version of the feature that should be enabled by default. - * @param enabledByDefault `true` if the feature should be enabled by default, `false` if it should be - * disabled by default. - * @param overrideAllowed `true` if this configuration can be overridden, e.g. by per-request feature - * toggle configuration. - * @param expirationDate the expiration date of the feature. - * @param developerEmails one or more email addresses of developers who can be contacted about the feature. - */ - case class FeatureToggleBaseConfig(featureName: String, - description: String, - availableVersions: Seq[Int], - defaultVersion: Int, - enabledByDefault: Boolean, - overrideAllowed: Boolean, - expirationDate: Option[Instant], - developerEmails: Set[String]) + * Represents the base configuration of a feature toggle. + * + * @param featureName the name of the feature. + * @param description a description of the feature. + * @param availableVersions the available versions of the feature. + * @param defaultVersion the version of the feature that should be enabled by default. + * @param enabledByDefault `true` if the feature should be enabled by default, `false` if it should be + * disabled by default. + * @param overrideAllowed `true` if this configuration can be overridden, e.g. by per-request feature + * toggle configuration. + * @param expirationDate the expiration date of the feature. + * @param developerEmails one or more email addresses of developers who can be contacted about the feature. + */ + case class FeatureToggleBaseConfig( + featureName: String, + description: String, + availableVersions: Seq[Int], + defaultVersion: Int, + enabledByDefault: Boolean, + overrideAllowed: Boolean, + expirationDate: Option[Instant], + developerEmails: Set[String] + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettingsConstants.scala b/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettingsConstants.scala index d8af0c9f1f..34b34a664a 100644 --- a/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettingsConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettingsConstants.scala @@ -20,9 +20,9 @@ package org.knora.webapi.settings /** - * 'SettingsConstants' contains constants of strings, we would generally expect to find in - * the 'application.conf' file, which can be accessed by the application 'Settings' - */ + * 'SettingsConstants' contains constants of strings, we would generally expect to find in + * the 'application.conf' file, which can be accessed by the application 'Settings' + */ object TriplestoreTypes { val EmbeddedJenaTdb = "embedded-jena-tdb" @@ -35,12 +35,12 @@ object TriplestoreTypes { object KnoraDispatchers { /** - * All normal actors should run on this dispatcher (non-blocking) - */ + * All normal actors should run on this dispatcher (non-blocking) + */ val KnoraActorDispatcher = "knora-actor-dispatcher" /** - * All blocking operations should run on this dispatcher (blocking) - */ + * All blocking operations should run on this dispatcher (blocking) + */ val KnoraBlockingDispatcher = "knora-blocking-dispatcher" } diff --git a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheService.scala b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheService.scala index f339427102..b30729ced0 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheService.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheService.scala @@ -27,8 +27,8 @@ import org.knora.webapi.messages.store.cacheservicemessages.{CacheServiceFlushDB import scala.concurrent.{ExecutionContext, Future} /** - * Cache Service Interface - */ + * Cache Service Interface + */ trait CacheService { def putUserADM(value: UserADM)(implicit ec: ExecutionContext): Future[Boolean] def getUserADM(identifier: UserIdentifierADM)(implicit ec: ExecutionContext): Future[Option[UserADM]] diff --git a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheServiceManager.scala b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheServiceManager.scala index b258ab71a3..40132d4495 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheServiceManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/CacheServiceManager.scala @@ -38,13 +38,13 @@ class CacheServiceManager(cs: CacheService) with InstrumentationSupport { /** - * The Knora Akka actor system. - */ + * The Knora Akka actor system. + */ protected implicit val _system: ActorSystem = context.system /** - * The Akka actor system's execution context for futures. - */ + * The Akka actor system's execution context for futures. + */ protected implicit val ec: ExecutionContext = context.system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) def receive = { @@ -62,99 +62,99 @@ class CacheServiceManager(cs: CacheService) } /** - * Stores the user under the IRI and additionally the IRI under the keys of - * USERNAME and EMAIL: - * - * IRI -> byte array - * username -> IRI - * email -> IRI - * - * @param value the stored value - */ + * Stores the user under the IRI and additionally the IRI under the keys of + * USERNAME and EMAIL: + * + * IRI -> byte array + * username -> IRI + * email -> IRI + * + * @param value the stored value + */ private def putUserADM(value: UserADM): Future[Boolean] = tracedFuture("caches-service-write-user") { cs.putUserADM(value) } /** - * Retrieves the user stored under the identifier (either iri, username, - * or email). - * - * @param identifier the project identifier. - */ + * Retrieves the user stored under the identifier (either iri, username, + * or email). + * + * @param identifier the project identifier. + */ private def getUserADM(identifier: UserIdentifierADM): Future[Option[UserADM]] = tracedFuture("cache-service-read-user") { cs.getUserADM(identifier) } /** - * Stores the project under the IRI and additionally the IRI under the keys - * of SHORTCODE and SHORTNAME: - * - * IRI -> byte array - * shortname -> IRI - * shortcode -> IRI - * - * @param value the stored value - */ + * Stores the project under the IRI and additionally the IRI under the keys + * of SHORTCODE and SHORTNAME: + * + * IRI -> byte array + * shortname -> IRI + * shortcode -> IRI + * + * @param value the stored value + */ private def putProjectADM(value: ProjectADM)(implicit ec: ExecutionContext): Future[Boolean] = tracedFuture("cache-service-write-project") { cs.putProjectADM(value) } /** - * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). - * - * @param identifier the project identifier. - */ - private def getProjectADM(identifier: ProjectIdentifierADM)( - implicit ec: ExecutionContext): Future[Option[ProjectADM]] = + * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). + * + * @param identifier the project identifier. + */ + private def getProjectADM( + identifier: ProjectIdentifierADM + )(implicit ec: ExecutionContext): Future[Option[ProjectADM]] = tracedFuture("redis-read-project") { cs.getProjectADM(identifier) } /** - * Get value stored under the key as a string. - * - * @param maybeKey the key. - */ + * Get value stored under the key as a string. + * + * @param maybeKey the key. + */ private def getStringValue(maybeKey: Option[String]): Future[Option[String]] = tracedFuture("cache-service-get-string") { cs.getStringValue(maybeKey) } /** - * Store string or byte array value under key. - * - * @param key the key. - * @param value the value. - */ + * Store string or byte array value under key. + * + * @param key the key. + * @param value the value. + */ private def writeStringValue(key: String, value: String): Future[Boolean] = tracedFuture("cache-service-write-string") { cs.writeStringValue(key, value) } /** - * Removes values for the provided keys. Any invalid keys are ignored. - * - * @param keys the keys. - */ + * Removes values for the provided keys. Any invalid keys are ignored. + * + * @param keys the keys. + */ private def removeValues(keys: Set[String]): Future[Boolean] = tracedFuture("redis-remove-values") { cs.removeValues(keys) } /** - * Flushes (removes) all stored content from the Redis store. - */ + * Flushes (removes) all stored content from the Redis store. + */ private def flushDB(requestingUser: UserADM): Future[CacheServiceFlushDBACK] = tracedFuture("redis-flush-db") { cs.flushDB(requestingUser) } /** - * Pings the cache service to see if it is available. - */ - private def ping(): Future[CacheServiceStatusResponse] = { + * Pings the cache service to see if it is available. + */ + private def ping(): Future[CacheServiceStatusResponse] = cs.ping() - } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImpl.scala b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImpl.scala index b4621e9d55..ae48ea6c83 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImpl.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImpl.scala @@ -43,15 +43,15 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { scala.collection.mutable.Map[Any, Any]() /** - * Stores the user under the IRI and additionally the IRI under the keys of - * USERNAME and EMAIL: - * - * IRI -> byte array - * username -> IRI - * email -> IRI - * - * @param value the stored value - */ + * Stores the user under the IRI and additionally the IRI under the keys of + * USERNAME and EMAIL: + * + * IRI -> byte array + * username -> IRI + * email -> IRI + * + * @param value the stored value + */ def putUserADM(value: UserADM)(implicit ec: ExecutionContext): Future[Boolean] = { cache(value.id) = value cache(value.username) = value.id @@ -60,11 +60,11 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { } /** - * Retrieves the user stored under the identifier (either iri, username, - * or email). - * - * @param identifier the user identifier. - */ + * Retrieves the user stored under the identifier (either iri, username, + * or email). + * + * @param identifier the user identifier. + */ def getUserADM(identifier: UserIdentifierADM)(implicit ec: ExecutionContext): Future[Option[UserADM]] = { // The data is stored under the IRI key. // Additionally, the USERNAME and EMAIL keys point to the IRI key @@ -86,15 +86,15 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { } /** - * Stores the project under the IRI and additionally the IRI under the keys - * of SHORTCODE and SHORTNAME: - * - * IRI -> byte array - * shortname -> IRI - * shortcode -> IRI - * - * @param value the stored value - */ + * Stores the project under the IRI and additionally the IRI under the keys + * of SHORTCODE and SHORTNAME: + * + * IRI -> byte array + * shortname -> IRI + * shortcode -> IRI + * + * @param value the stored value + */ def putProjectADM(value: ProjectADM)(implicit ec: ExecutionContext): Future[Boolean] = { cache(value.id) = value cache(value.shortcode) = value.id @@ -103,10 +103,10 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { } /** - * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). - * - * @param identifier the project identifier. - */ + * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). + * + * @param identifier the project identifier. + */ def getProjectADM(identifier: ProjectIdentifierADM)(implicit ec: ExecutionContext): Future[Option[ProjectADM]] = { // The data is stored under the IRI key. // Additionally, the SHORTNAME and SHORTCODE keys point to the IRI key @@ -128,11 +128,11 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { } /** - * Store string or byte array value under key. - * - * @param key the key. - * @param value the value. - */ + * Store string or byte array value under key. + * + * @param key the key. + * @param value the value. + */ def writeStringValue(key: String, value: String)(implicit ec: ExecutionContext): Future[Boolean] = { if (key.isEmpty) @@ -146,24 +146,23 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { } /** - * Get value stored under the key as a string. - * - * @param maybeKey the key. - */ - def getStringValue(maybeKey: Option[String])(implicit ec: ExecutionContext): Future[Option[String]] = { + * Get value stored under the key as a string. + * + * @param maybeKey the key. + */ + def getStringValue(maybeKey: Option[String])(implicit ec: ExecutionContext): Future[Option[String]] = maybeKey match { case Some(key) => FastFuture.successful(cache.get(key).map(_.asInstanceOf[String])) case None => FastFuture.successful(None) } - } /** - * Removes values for the provided keys. Any invalid keys are ignored. - * - * @param keys the keys. - */ + * Removes values for the provided keys. Any invalid keys are ignored. + * + * @param keys the keys. + */ def removeValues(keys: Set[String])(implicit ec: ExecutionContext): Future[Boolean] = { logger.debug("removeValues - {}", keys) @@ -175,18 +174,17 @@ object CacheServiceInMemImpl extends CacheService with LazyLogging { } /** - * Flushes (removes) all stored content from the in-memory cache. - */ + * Flushes (removes) all stored content from the in-memory cache. + */ def flushDB(requestingUser: UserADM)(implicit ec: ExecutionContext): Future[CacheServiceFlushDBACK] = { cache = scala.collection.mutable.Map[Any, Any]() FastFuture.successful(CacheServiceFlushDBACK()) } /** - * Pings the in-memory cache to see if it is available. - */ - def ping()(implicit ec: ExecutionContext): Future[CacheServiceStatusResponse] = { + * Pings the in-memory cache to see if it is available. + */ + def ping()(implicit ec: ExecutionContext): Future[CacheServiceStatusResponse] = FastFuture.successful(CacheServiceStatusOK) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImpl.scala b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImpl.scala index da0e2e7b2a..da158e3dce 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImpl.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImpl.scala @@ -45,8 +45,8 @@ import scala.concurrent.{ExecutionContext, Future} class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with LazyLogging { /** - * The Redis Client Pool - */ + * The Redis Client Pool + */ val pool: JedisPool = new JedisPool(new JedisPoolConfig(), s.cacheServiceRedisHost, s.cacheServiceRedisPort, 20999) // this is needed for time measurements using 'org.knora.webapi.Timing' @@ -54,15 +54,15 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L implicit val l: Logger = logger /** - * Stores the user under the IRI and additionally the IRI under the keys of - * USERNAME and EMAIL: - * - * IRI -> byte array - * username -> IRI - * email -> IRI - * - * @param value the stored value - */ + * Stores the user under the IRI and additionally the IRI under the keys of + * USERNAME and EMAIL: + * + * IRI -> byte array + * username -> IRI + * email -> IRI + * + * @param value the stored value + */ def putUserADM(value: UserADM)(implicit ec: ExecutionContext): Future[Boolean] = { val resultFuture = for { bytes: Array[Byte] <- CacheSerialization.serialize(value) @@ -72,21 +72,20 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L _ = writeStringValue(value.email, value.id) } yield result - val recoverableResultFuture = resultFuture.recover { - case e: Exception => - logger.warn("Aborting writing 'UserADM' to Redis - {}", e.getMessage) - false + val recoverableResultFuture = resultFuture.recover { case e: Exception => + logger.warn("Aborting writing 'UserADM' to Redis - {}", e.getMessage) + false } recoverableResultFuture } /** - * Retrieves the user stored under the identifier (either iri, username, - * or email). - * - * @param identifier the user identifier. - */ + * Retrieves the user stored under the identifier (either iri, username, + * or email). + * + * @param identifier the user identifier. + */ def getUserADM(identifier: UserIdentifierADM)(implicit ec: ExecutionContext): Future[Option[UserADM]] = { // The data is stored under the IRI key. // Additionally, the USERNAME and EMAIL keys point to the IRI key @@ -121,25 +120,24 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } yield maybeUser } - val recoverableResultFuture = resultFuture.recover { - case e: Exception => - logger.warn("Aborting reading 'UserADM' from Redis - {}", e.getMessage) - None + val recoverableResultFuture = resultFuture.recover { case e: Exception => + logger.warn("Aborting reading 'UserADM' from Redis - {}", e.getMessage) + None } recoverableResultFuture } /** - * Stores the project under the IRI and additionally the IRI under the keys - * of SHORTCODE and SHORTNAME: - * - * IRI -> byte array - * shortname -> IRI - * shortcode -> IRI - * - * @param value the stored value - */ + * Stores the project under the IRI and additionally the IRI under the keys + * of SHORTCODE and SHORTNAME: + * + * IRI -> byte array + * shortname -> IRI + * shortcode -> IRI + * + * @param value the stored value + */ def putProjectADM(value: ProjectADM)(implicit ec: ExecutionContext): Future[Boolean] = { val resultFuture = for { bytes: Array[Byte] <- CacheSerialization.serialize(value) @@ -148,20 +146,19 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L _ = writeStringValue(value.shortname, value.id) } yield result - val recoverableResultFuture = resultFuture.recover { - case e: Exception => - logger.warn("Aborting writing 'ProjectADM' to Redis - {}", e.getMessage) - false + val recoverableResultFuture = resultFuture.recover { case e: Exception => + logger.warn("Aborting writing 'ProjectADM' to Redis - {}", e.getMessage) + false } recoverableResultFuture } /** - * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). - * - * @param identifier the project identifier. - */ + * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). + * + * @param identifier the project identifier. + */ def getProjectADM(identifier: ProjectIdentifierADM)(implicit ec: ExecutionContext): Future[Option[ProjectADM]] = { // The data is stored under the IRI key. @@ -195,21 +192,20 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } yield maybeProject } - val recoverableResultFuture = resultFuture.recover { - case e: Exception => - logger.warn("Aborting reading 'ProjectADM' from Redis - {}", e.getMessage) - None + val recoverableResultFuture = resultFuture.recover { case e: Exception => + logger.warn("Aborting reading 'ProjectADM' from Redis - {}", e.getMessage) + None } recoverableResultFuture } /** - * Store string or byte array value under key. - * - * @param key the key. - * @param value the value. - */ + * Store string or byte array value under key. + * + * @param key the key. + * @param value the value. + */ def writeStringValue(key: String, value: String)(implicit ec: ExecutionContext): Future[Boolean] = { if (key.isEmpty) @@ -230,21 +226,20 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - // Log any errors. - logger.warn("Writing to Redis failed - {}", e.getMessage) - false + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + // Log any errors. + logger.warn("Writing to Redis failed - {}", e.getMessage) + false } recoverableOperationFuture } /** - * Get value stored under the key as a string. - * - * @param maybeKey the key. - */ + * Get value stored under the key as a string. + * + * @param maybeKey the key. + */ def getStringValue(maybeKey: Option[String])(implicit ec: ExecutionContext): Future[Option[String]] = { val operationFuture: Future[Option[String]] = maybeKey match { @@ -261,21 +256,20 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L FastFuture.successful(None) } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - // Log any errors. - logger.warn("Reading string from Redis failed, {}", e) - None + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + // Log any errors. + logger.warn("Reading string from Redis failed, {}", e) + None } recoverableOperationFuture } /** - * Removes values for the provided keys. Any invalid keys are ignored. - * - * @param keys the keys. - */ + * Removes values for the provided keys. Any invalid keys are ignored. + * + * @param keys the keys. + */ def removeValues(keys: Set[String])(implicit ec: ExecutionContext): Future[Boolean] = { logger.debug("removeValues - {}", keys) @@ -291,19 +285,18 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - // Log any errors. - logger.warn("Removing keys from Redis failed.", e.getMessage) - false + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + // Log any errors. + logger.warn("Removing keys from Redis failed.", e.getMessage) + false } recoverableOperationFuture } /** - * Flushes (removes) all stored content from the Redis store. - */ + * Flushes (removes) all stored content from the Redis store. + */ def flushDB(requestingUser: UserADM)(implicit ec: ExecutionContext): Future[CacheServiceFlushDBACK] = { if (!requestingUser.isSystemUser) { @@ -321,19 +314,18 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - // Log any errors. - logger.warn("Flushing DB failed", e.getMessage) - throw e + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + // Log any errors. + logger.warn("Flushing DB failed", e.getMessage) + throw e } recoverableOperationFuture } /** - * Pings the Redis store to see if it is available. - */ + * Pings the Redis store to see if it is available. + */ def ping()(implicit ec: ExecutionContext): Future[CacheServiceStatusResponse] = { val operationFuture: Future[CacheServiceStatusResponse] = Future { @@ -346,20 +338,19 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - CacheServiceStatusNOK + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + CacheServiceStatusNOK } recoverableOperationFuture } /** - * Store string or byte array value under key. - * - * @param key the key. - * @param value the value. - */ + * Store string or byte array value under key. + * + * @param key the key. + * @param value the value. + */ private def writeBytesValue(key: String, value: Array[Byte])(implicit ec: ExecutionContext): Future[Boolean] = { if (key.isEmpty) @@ -378,22 +369,21 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L } } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - // Log any errors. - logger.warn("Writing to Redis failed - {}", e.getMessage) - false + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + // Log any errors. + logger.warn("Writing to Redis failed - {}", e.getMessage) + false } recoverableOperationFuture } /** - * Get value stored under the key as a byte array. If no value is found - * under the key, then a [[None]] is returned.. - * - * @param maybeKey the key. - */ + * Get value stored under the key as a byte array. If no value is found + * under the key, then a [[None]] is returned.. + * + * @param maybeKey the key. + */ private def getBytesValue(maybeKey: Option[String])(implicit ec: ExecutionContext): Future[Option[Array[Byte]]] = { val operationFuture: Future[Option[Array[Byte]]] = maybeKey match { @@ -410,11 +400,10 @@ class CacheServiceRedisImpl(s: CacheServiceSettings) extends CacheService with L FastFuture.successful(None) } - val recoverableOperationFuture = operationFuture.recover { - case e: Exception => - // Log any errors. - logger.warn("Reading byte array from Redis failed - {}", e.getMessage) - None + val recoverableOperationFuture = operationFuture.recover { case e: Exception => + // Log any errors. + logger.warn("Reading byte array from Redis failed - {}", e.getMessage) + None } recoverableOperationFuture diff --git a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerialization.scala b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerialization.scala index eec7f3238c..1ac9043b82 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerialization.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerialization.scala @@ -32,14 +32,14 @@ case class EmptyByteArray(message: String) extends CacheServiceException(message object CacheSerialization extends InstrumentationSupport { /** - * Serialize objects by using plain java serialization. Java serialization is not - * capable to serialize all our objects (e.g., UserADM) and that is why we use the - * [[MeatLocker]], which does some magic and allows our case classes to be - * serializable. - * - * @param value the value we want to serialize as a array of bytes. - * @tparam T the type parameter of our value. - */ + * Serialize objects by using plain java serialization. Java serialization is not + * capable to serialize all our objects (e.g., UserADM) and that is why we use the + * [[MeatLocker]], which does some magic and allows our case classes to be + * serializable. + * + * @param value the value we want to serialize as a array of bytes. + * @tparam T the type parameter of our value. + */ def serialize[T](value: T)(implicit ec: ExecutionContext): Future[Array[Byte]] = tracedFuture("redis-serialize") { Future { @@ -53,13 +53,13 @@ object CacheSerialization extends InstrumentationSupport { } /** - * Deserialize objects by using plain java serialization. Java serialization is not - * capable to serialize all our objects (e.g., UserADM) and that is why we use the - * [[MeatLocker]], which does some magic and allows our case classes to be - * serializable. - * - * @tparam T the type parameter of our value. - */ + * Deserialize objects by using plain java serialization. Java serialization is not + * capable to serialize all our objects (e.g., UserADM) and that is why we use the + * [[MeatLocker]], which does some magic and allows our case classes to be + * serializable. + * + * @tparam T the type parameter of our value. + */ def deserialize[T](bytes: Array[Byte])(implicit ec: ExecutionContext): Future[Option[T]] = tracedFuture("redis-deserialize") { diff --git a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/settings/CacheServiceSettings.scala b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/settings/CacheServiceSettings.scala index be1235b5a9..468ccbc759 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cacheservice/settings/CacheServiceSettings.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cacheservice/settings/CacheServiceSettings.scala @@ -23,8 +23,8 @@ package org.knora.webapi.store.cacheservice.settings import com.typesafe.config.Config /** - * Holds the Cache Service specific settings. - */ + * Holds the Cache Service specific settings. + */ class CacheServiceSettings(config: Config) { val cacheServiceEnabled: Boolean = config.getBoolean("app.cache-service.enabled") val cacheServiceRedisHost: String = config.getString("app.cache-service.redis.host") diff --git a/webapi/src/main/scala/org/knora/webapi/store/iiif/SipiConnector.scala b/webapi/src/main/scala/org/knora/webapi/store/iiif/SipiConnector.scala index 58548cf2d2..cf4073edcd 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/iiif/SipiConnector.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/iiif/SipiConnector.scala @@ -45,8 +45,8 @@ import scala.concurrent.ExecutionContext import scala.util.Try /** - * Makes requests to Sipi. - */ + * Makes requests to Sipi. + */ class SipiConnector extends Actor with ActorLogging { implicit val system: ActorSystem = context.system @@ -83,24 +83,26 @@ class SipiConnector extends Actor with ActorLogging { } /** - * Represents a response from Sipi's `knora.json` route. - * - * @param originalFilename the file's original filename, if known. - * @param originalMimeType the file's original MIME type. - * @param internalMimeType the file's internal MIME type. - * @param width the file's width in pixels, if applicable. - * @param height the file's height in pixels, if applicable. - * @param numpages the number of pages in the file, if applicable. - * @param duration the duration of the file in seconds, if applicable. - */ - case class SipiKnoraJsonResponse(originalFilename: Option[String], - originalMimeType: Option[String], - internalMimeType: String, - width: Option[Int], - height: Option[Int], - numpages: Option[Int], - duration: Option[BigDecimal], - fps: Option[BigDecimal]) { + * Represents a response from Sipi's `knora.json` route. + * + * @param originalFilename the file's original filename, if known. + * @param originalMimeType the file's original MIME type. + * @param internalMimeType the file's internal MIME type. + * @param width the file's width in pixels, if applicable. + * @param height the file's height in pixels, if applicable. + * @param numpages the number of pages in the file, if applicable. + * @param duration the duration of the file in seconds, if applicable. + */ + case class SipiKnoraJsonResponse( + originalFilename: Option[String], + originalMimeType: Option[String], + internalMimeType: String, + width: Option[Int], + height: Option[Int], + numpages: Option[Int], + duration: Option[BigDecimal], + fps: Option[BigDecimal] + ) { if (originalFilename.contains("")) { throw SipiException(s"Sipi returned an empty originalFilename") } @@ -115,11 +117,11 @@ class SipiConnector extends Actor with ActorLogging { } /** - * Asks Sipi for metadata about a file. - * - * @param getFileMetadataRequest the request. - * @return a [[GetFileMetadataResponse]] containing the requested metadata. - */ + * Asks Sipi for metadata about a file. + * + * @param getFileMetadataRequest the request. + * @return a [[GetFileMetadataResponse]] containing the requested metadata. + */ private def getFileMetadata(getFileMetadataRequest: GetFileMetadataRequest): Try[GetFileMetadataResponse] = { import SipiKnoraJsonResponseProtocol._ @@ -129,28 +131,27 @@ class SipiConnector extends Actor with ActorLogging { for { sipiResponseStr <- doSipiRequest(sipiRequest) sipiResponse: SipiKnoraJsonResponse = sipiResponseStr.parseJson.convertTo[SipiKnoraJsonResponse] - } yield - GetFileMetadataResponse( - originalFilename = sipiResponse.originalFilename, - originalMimeType = sipiResponse.originalMimeType, - internalMimeType = sipiResponse.internalMimeType, - width = sipiResponse.width, - height = sipiResponse.height, - pageCount = sipiResponse.numpages, - duration = sipiResponse.duration, - fps = sipiResponse.fps - ) + } yield GetFileMetadataResponse( + originalFilename = sipiResponse.originalFilename, + originalMimeType = sipiResponse.originalMimeType, + internalMimeType = sipiResponse.internalMimeType, + width = sipiResponse.width, + height = sipiResponse.height, + pageCount = sipiResponse.numpages, + duration = sipiResponse.duration, + fps = sipiResponse.fps + ) } /** - * Asks Sipi to move a file from temporary storage to permanent storage. - * - * @param moveTemporaryFileToPermanentStorageRequestV2 the request. - * @return a [[SuccessResponseV2]]. - */ + * Asks Sipi to move a file from temporary storage to permanent storage. + * + * @param moveTemporaryFileToPermanentStorageRequestV2 the request. + * @return a [[SuccessResponseV2]]. + */ private def moveTemporaryFileToPermanentStorage( - moveTemporaryFileToPermanentStorageRequestV2: MoveTemporaryFileToPermanentStorageRequest) - : Try[SuccessResponseV2] = { + moveTemporaryFileToPermanentStorageRequestV2: MoveTemporaryFileToPermanentStorageRequest + ): Try[SuccessResponseV2] = { val token: String = JWTHelper.createToken( userIri = moveTemporaryFileToPermanentStorageRequestV2.requestingUser.id, secret = settings.jwtSecretKey, @@ -181,11 +182,11 @@ class SipiConnector extends Actor with ActorLogging { } /** - * Asks Sipi to delete a temporary file. - * - * @param deleteTemporaryFileRequestV2 the request. - * @return a [[SuccessResponseV2]]. - */ + * Asks Sipi to delete a temporary file. + * + * @param deleteTemporaryFileRequestV2 the request. + * @return a [[SuccessResponseV2]]. + */ private def deleteTemporaryFile(deleteTemporaryFileRequestV2: DeleteTemporaryFileRequest): Try[SuccessResponseV2] = { val token: String = JWTHelper.createToken( userIri = deleteTemporaryFileRequestV2.requestingUser.id, @@ -211,10 +212,10 @@ class SipiConnector extends Actor with ActorLogging { } /** - * Asks Sipi for a text file used internally by Knora. - * - * @param textFileRequest the request message. - */ + * Asks Sipi for a text file used internally by Knora. + * + * @param textFileRequest the request message. + */ private def sipiGetTextFileRequest(textFileRequest: SipiGetTextFileRequest): Try[SipiGetTextFileResponse] = { val httpRequest = new HttpGet(textFileRequest.fileUrl) @@ -225,11 +226,13 @@ class SipiConnector extends Actor with ActorLogging { sipiResponseTry.recover { case notFoundException: NotFoundException => throw NotFoundException( - s"Unable to get file ${textFileRequest.fileUrl} from Sipi as requested by ${textFileRequest.senderName}: ${notFoundException.message}") + s"Unable to get file ${textFileRequest.fileUrl} from Sipi as requested by ${textFileRequest.senderName}: ${notFoundException.message}" + ) case badRequestException: BadRequestException => throw SipiException( - s"Unable to get file ${textFileRequest.fileUrl} from Sipi as requested by ${textFileRequest.senderName}: ${badRequestException.message}") + s"Unable to get file ${textFileRequest.fileUrl} from Sipi as requested by ${textFileRequest.senderName}: ${badRequestException.message}" + ) case sipiException: SipiException => throw SipiException( @@ -245,8 +248,8 @@ class SipiConnector extends Actor with ActorLogging { } /** - * Tries to access the IIIF Service. - */ + * Tries to access the IIIF Service. + */ private def iiifGetStatus(): Try[IIIFServiceStatusResponse] = { val request = new HttpGet(settings.internalSipiBaseUrl + "/server/test.html") @@ -259,11 +262,11 @@ class SipiConnector extends Actor with ActorLogging { } /** - * Makes an HTTP request to Sipi and returns the response. - * - * @param request the HTTP request. - * @return Sipi's response. - */ + * Makes an HTTP request to Sipi and returns the response. + * + * @param request the HTTP request. + * @return Sipi's response. + */ private def doSipiRequest(request: HttpRequest): Try[String] = { val httpContext: HttpClientContext = HttpClientContext.create var maybeResponse: Option[CloseableHttpResponse] = None diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/RdfDataObjectFactory.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/RdfDataObjectFactory.scala index a7a8205046..6432e3fb0e 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/RdfDataObjectFactory.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/RdfDataObjectFactory.scala @@ -23,8 +23,7 @@ import com.typesafe.config.Config import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject /** - * - */ + */ object RdfDataObjectFactory { def apply(conf: Config): RdfDataObject = { val path = conf.getString("path") diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala index 8c31822874..686bd07b3e 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/embedded/JenaTDBActor.scala @@ -56,14 +56,14 @@ import scala.concurrent.{ExecutionContextExecutor, Future} */ class JenaTDBActor extends Actor with ActorLogging { - private val system = context.system + private val system = context.system private implicit val executionContext: ExecutionContextExecutor = system.dispatcher - private val settings = KnoraSettings(system) + private val settings = KnoraSettings(system) - private val persist = settings.tripleStoreConfig.getBoolean("embedded-jena-tdb.persisted") + private val persist = settings.tripleStoreConfig.getBoolean("embedded-jena-tdb.persisted") private val loadExistingData = settings.tripleStoreConfig.getBoolean("embedded-jena-tdb.loadExistingData") - private val storagePath = Paths.get(settings.tripleStoreConfig.getString("embedded-jena-tdb.storage-path")) - private val tdbStoragePath = Paths.get(storagePath.toString + "/db") + private val storagePath = Paths.get(settings.tripleStoreConfig.getString("embedded-jena-tdb.storage-path")) + private val tdbStoragePath = Paths.get(storagePath.toString + "/db") private val tsType = settings.triplestoreType @@ -155,9 +155,9 @@ class JenaTDBActor extends Actor with ActorLogging { //println("# Content of dataset at beginning of query") //SSE.write(this.dataset) - val query: Query = QueryFactory.create(queryString) + val query: Query = QueryFactory.create(queryString) val qExec: QueryExecution = QueryExecutionFactory.create(query, this.dataset) - val resultSet: ResultSet = qExec.execSelect() + val resultSet: ResultSet = qExec.execSelect() /* Attention: the ResultSet can be only used once, i.e. it is not there anymore after use! @@ -399,7 +399,7 @@ class JenaTDBActor extends Actor with ActorLogging { try { // get index. val dgt: DatasetGraphText = dataset.asDatasetGraph().asInstanceOf[DatasetGraphText] - val textIndex = dgt.getTextIndex + val textIndex = dgt.getTextIndex // get entity definitions from index val entityDefinition = textIndex.getDocDef @@ -412,7 +412,7 @@ class JenaTDBActor extends Actor with ActorLogging { for (prop: Node <- entityDefinition.getPredicates(field).asScala) { val quadIter: Iterator[Quad] = dgt.find(Node.ANY, Node.ANY, prop, Node.ANY).asScala while (quadIter.hasNext) { - val quad: Quad = quadIter.next() + val quad: Quad = quadIter.next() val entity: Entity = TextQueryFuncs.entityFromQuad(entityDefinition, quad) if (entity != null) { textIndex.addEntity(entity) @@ -443,8 +443,8 @@ class JenaTDBActor extends Actor with ActorLogging { // Define which fields should be indexed by lucene val knoraBase = "http://www.knora.org/ontology/knora-base#" - val rdfs = "http://www.w3.org/2000/01/rdf-schema#" - val entDef = new EntityDefinition("uri", "text", ResourceFactory.createProperty(knoraBase, "valueHasString")) + val rdfs = "http://www.w3.org/2000/01/rdf-schema#" + val entDef = new EntityDefinition("uri", "text", ResourceFactory.createProperty(knoraBase, "valueHasString")) entDef.setPrimaryPredicate(ResourceFactory.createProperty(rdfs, "label")) entDef.setPrimaryPredicate(ResourceFactory.createProperty(knoraBase, "valueHasComment")) diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala index de581846d0..44aa55d0b0 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/http/HttpTriplestoreConnector.scala @@ -67,16 +67,16 @@ import scala.util.{Failure, Success, Try} class HttpTriplestoreConnector extends Actor with ActorLogging with InstrumentationSupport { // MIME type constants. - private val mimeTypeApplicationJson = "application/json" + private val mimeTypeApplicationJson = "application/json" private val mimeTypeApplicationSparqlResultsJson = "application/sparql-results+json" - private val mimeTypeTextTurtle = "text/turtle" - private val mimeTypeApplicationSparqlUpdate = "application/sparql-update" - private val mimeTypeApplicationNQuads = "application/n-quads" + private val mimeTypeTextTurtle = "text/turtle" + private val mimeTypeApplicationSparqlUpdate = "application/sparql-update" + private val mimeTypeApplicationNQuads = "application/n-quads" - private implicit val system: ActorSystem = context.system - private val settings = KnoraSettings(system) + private implicit val system: ActorSystem = context.system + private val settings = KnoraSettings(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraBlockingDispatcher) - override val log: LoggingAdapter = akka.event.Logging(system, this.getClass.getName) + override val log: LoggingAdapter = akka.event.Logging(system, this.getClass.getName) private val triplestoreType = settings.triplestoreType @@ -282,19 +282,20 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat for { // Are we using the fake triplestore? - resultStr <- if (settings.useFakeTriplestore) { - // Yes: get the response from it. - Try(FakeTriplestore.data(sparql)) - } else { - // No: get the response from the real triplestore over HTTP. - getSparqlHttpResponse(sparql, isUpdate = false, simulateTimeout = simulateTimeout) - } + resultStr <- + if (settings.useFakeTriplestore) { + // Yes: get the response from it. + Try(FakeTriplestore.data(sparql)) + } else { + // No: get the response from the real triplestore over HTTP. + getSparqlHttpResponse(sparql, isUpdate = false, simulateTimeout = simulateTimeout) + } // Are we preparing a fake triplestore? _ = if (settings.prepareFakeTriplestore) { - // Yes: add the query and the response to it. - FakeTriplestore.add(sparql, resultStr, log) - } + // Yes: add the query and the response to it. + FakeTriplestore.add(sparql, resultStr, log) + } // _ = println(s"SPARQL: $logDelimiter$sparql") // _ = println(s"Result: $logDelimiter$resultStr") @@ -321,13 +322,13 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat rdfFormatUtil: RdfFormatUtil ): Try[SparqlConstructResponse] = { val parseTry = Try { - val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtleStr, rdfFormat = Turtle) + val rdfModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtleStr, rdfFormat = Turtle) val statementMap: mutable.Map[IRI, Seq[(IRI, String)]] = mutable.Map.empty for (st: Statement <- rdfModel) { - val subjectIri = st.subj.stringValue + val subjectIri = st.subj.stringValue val predicateIri = st.pred.stringValue - val objectIri = st.obj.stringValue + val objectIri = st.obj.stringValue val currentStatementsForSubject: Seq[(IRI, String)] = statementMap.getOrElse(subjectIri, Vector.empty[(IRI, String)]) statementMap += (subjectIri -> (currentStatementsForSubject :+ (predicateIri, objectIri))) @@ -352,10 +353,10 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat getSparqlHttpResponse(sparqlConstructRequest.sparql, isUpdate = false, acceptMimeType = mimeTypeTextTurtle) response <- parseTurtleResponse( - sparql = sparqlConstructRequest.sparql, - turtleStr = turtleStr, - rdfFormatUtil = rdfFormatUtil - ) + sparql = sparqlConstructRequest.sparql, + turtleStr = turtleStr, + rdfFormatUtil = rdfFormatUtil + ) } yield response } @@ -381,11 +382,11 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat turtleStr <- getSparqlHttpResponse(sparql, isUpdate = false, acceptMimeType = mimeTypeTextTurtle) _ = rdfFormatUtil.turtleToQuadsFile( - rdfSource = RdfStringSource(turtleStr), - graphIri = graphIri, - outputFile = outputFile, - outputFormat = outputFormat - ) + rdfSource = RdfStringSource(turtleStr), + graphIri = graphIri, + outputFile = outputFile, + outputFormat = outputFormat + ) } yield FileWrittenResponse() } @@ -404,16 +405,16 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val parseTry = for { turtleStr <- getSparqlHttpResponse( - sparqlExtendedConstructRequest.sparql, - isUpdate = false, - acceptMimeType = mimeTypeTextTurtle - ) + sparqlExtendedConstructRequest.sparql, + isUpdate = false, + acceptMimeType = mimeTypeTextTurtle + ) response <- SparqlExtendedConstructResponse.parseTurtleResponse( - turtleStr = turtleStr, - rdfFormatUtil = rdfFormatUtil, - log = log - ) + turtleStr = turtleStr, + rdfFormatUtil = rdfFormatUtil, + log = log + ) } yield response parseTry match { @@ -474,7 +475,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat def sparqlHttpAsk(sparql: String): Try[SparqlAskResponse] = for { resultString <- getSparqlHttpResponse(sparql, isUpdate = false) - _ = log.debug("sparqlHttpAsk - resultString: {}", resultString) + _ = log.debug("sparqlHttpAsk - resultString: {}", resultString) result: Boolean = resultString.parseJson.asJsObject.getFields("boolean").head.convertTo[Boolean] } yield SparqlAskResponse(result) @@ -510,7 +511,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val response: Try[DropAllRepositoryContentACK] = for { result: String <- getSparqlHttpResponse(dropAllSparqlString, isUpdate = true) - _ = log.debug(s"==>> Drop All Data End, Result: $result") + _ = log.debug(s"==>> Drop All Data End, Result: $result") } yield DropAllRepositoryContentACK() response.recover { case t: Exception => @@ -642,7 +643,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat responseTry.get } - val nameShouldBe = settings.triplestoreDatabaseName + val nameShouldBe = settings.triplestoreDatabaseName val fusekiServer: FusekiServer = JsonParser(responseStr).convertTo[FusekiServer] val neededDataset: Option[FusekiDataset] = fusekiServer.datasets.find(dataset => dataset.dsName == s"/$nameShouldBe" && dataset.dsState) @@ -718,8 +719,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat } val httpContext: HttpClientContext = makeHttpContext - val httpPost: HttpPost = new HttpPost("/$/datasets") - val stringEntity = new StringEntity(triplestoreConfig, ContentType.create(mimeTypeTextTurtle)) + val httpPost: HttpPost = new HttpPost("/$/datasets") + val stringEntity = new StringEntity(triplestoreConfig, ContentType.create(mimeTypeTextTurtle)) httpPost.setEntity(stringEntity) doHttpRequest( @@ -768,8 +769,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val repositories: Seq[GraphDBRepository] = jsonArr.elements.map(_.convertTo[GraphDBRepository]) - val idShouldBe: String = settings.triplestoreDatabaseName - val sesameTypeForSEShouldBe = "owlim:MonitorRepository" + val idShouldBe: String = settings.triplestoreDatabaseName + val sesameTypeForSEShouldBe = "owlim:MonitorRepository" val sesameTypeForFreeShouldBe = "graphdb:FreeSailRepository" val neededRepo: Option[GraphDBRepository] = repositories.find(repo => @@ -842,7 +843,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat featureFactoryConfig: FeatureFactoryConfig ): Try[FileWrittenResponse] = { val httpContext: HttpClientContext = makeHttpContext - val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) + val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) httpGet.addHeader("Accept", mimeTypeTextTurtle) val makeResponse: CloseableHttpResponse => FileWrittenResponse = writeResponseFile( @@ -867,7 +868,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat */ private def sparqlHttpGraphData(graphIri: IRI): Try[NamedGraphDataResponse] = { val httpContext: HttpClientContext = makeHttpContext - val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) + val httpGet = new HttpGet(makeNamedGraphDownloadUri(graphIri)) httpGet.addHeader("Accept", mimeTypeTextTurtle) val makeResponse: CloseableHttpResponse => NamedGraphDataResponse = returnGraphDataAsTurtle(graphIri) @@ -899,7 +900,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat val (httpClient: CloseableHttpClient, httpPost: HttpPost) = if (isUpdate) { // Send updates as application/sparql-update (as per SPARQL 1.1 Protocol §3.2.2, "UPDATE using POST directly"). - val requestEntity = new StringEntity(sparql, ContentType.create(mimeTypeApplicationSparqlUpdate, "UTF-8")) + val requestEntity = new StringEntity(sparql, ContentType.create(mimeTypeApplicationSparqlUpdate, "UTF-8")) val updateHttpPost = new HttpPost(sparqlUpdatePath) updateHttpPost.setEntity(requestEntity) (updateHttpClient, updateHttpPost) @@ -915,7 +916,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat formParams.add(new BasicNameValuePair("query", sparql)) val requestEntity: UrlEncodedFormEntity = new UrlEncodedFormEntity(formParams, Consts.UTF_8) - val queryHttpPost: HttpPost = new HttpPost(queryPath) + val queryHttpPost: HttpPost = new HttpPost(queryPath) queryHttpPost.setEntity(requestEntity) queryHttpPost.addHeader("Accept", acceptMimeType) (queryHttpClient, queryHttpPost) @@ -990,8 +991,8 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat */ private def uploadRepository(inputFile: Path): Try[RepositoryUploadedResponse] = { val httpContext: HttpClientContext = makeHttpContext - val httpPost: HttpPost = new HttpPost(repositoryUploadPath) - val fileEntity = new FileEntity(inputFile.toFile, ContentType.create(mimeTypeApplicationNQuads, "UTF-8")) + val httpPost: HttpPost = new HttpPost(repositoryUploadPath) + val fileEntity = new FileEntity(inputFile.toFile, ContentType.create(mimeTypeApplicationNQuads, "UTF-8")) httpPost.setEntity(fileEntity) doHttpRequest( @@ -1010,10 +1011,10 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat */ private def insertDataGraphRequest(graphContent: String, graphName: String): Try[InsertGraphDataContentResponse] = { val httpContext: HttpClientContext = makeHttpContext - val uriBuilder: URIBuilder = new URIBuilder(dataInsertPath) + val uriBuilder: URIBuilder = new URIBuilder(dataInsertPath) uriBuilder.addParameter("graph", graphName) val httpPut: HttpPut = new HttpPut(uriBuilder.build()) - val requestEntity = new StringEntity(graphContent, ContentType.create(mimeTypeTextTurtle, "UTF-8")) + val requestEntity = new StringEntity(graphContent, ContentType.create(mimeTypeTextTurtle, "UTF-8")) httpPut.setEntity(requestEntity) val makeResponse: CloseableHttpResponse => InsertGraphDataContentResponse = returnInsertGraphDataResponse(graphName) @@ -1031,7 +1032,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat * @return httpContext with credentials and authorization */ private def makeHttpContext: HttpClientContext = { - val authCache: AuthCache = new BasicAuthCache + val authCache: AuthCache = new BasicAuthCache val basicAuth: BasicScheme = new BasicScheme authCache.put(targetHost, basicAuth) @@ -1069,7 +1070,7 @@ class HttpTriplestoreConnector extends Actor with ActorLogging with Instrumentat throw new java.net.SocketTimeoutException("Simulated read timeout") } - val start = System.currentTimeMillis() + val start = System.currentTimeMillis() val response = client.execute(targetHost, request, context) maybeResponse = Some(response) val statusCode: Int = response.getStatusLine.getStatusCode diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala index 2c6f0e3277..fade4726e9 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala @@ -5,29 +5,37 @@ import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.store.triplestore.upgrade.plugins._ /** - * The plan for updating a repository to work with the current version of Knora. - */ + * The plan for updating a repository to work with the current version of Knora. + */ object RepositoryUpdatePlan { /** - * Constructs list of all repository update plugins in chronological order. - * - * @param featureFactoryConfig the feature factor configuration. - */ + * Constructs list of all repository update plugins in chronological order. + * + * @param featureFactoryConfig the feature factor configuration. + */ def makePluginsForVersions(featureFactoryConfig: FeatureFactoryConfig, log: Logger): Seq[PluginForKnoraBaseVersion] = Seq( - PluginForKnoraBaseVersion(versionNumber = 1, - plugin = new UpgradePluginPR1307(featureFactoryConfig), - prBasedVersionString = Some("PR 1307")), - PluginForKnoraBaseVersion(versionNumber = 2, - plugin = new UpgradePluginPR1322(featureFactoryConfig), - prBasedVersionString = Some("PR 1322")), - PluginForKnoraBaseVersion(versionNumber = 3, - plugin = new UpgradePluginPR1367(featureFactoryConfig), - prBasedVersionString = Some("PR 1367")), - PluginForKnoraBaseVersion(versionNumber = 4, - plugin = new UpgradePluginPR1372(featureFactoryConfig), - prBasedVersionString = Some("PR 1372")), + PluginForKnoraBaseVersion( + versionNumber = 1, + plugin = new UpgradePluginPR1307(featureFactoryConfig), + prBasedVersionString = Some("PR 1307") + ), + PluginForKnoraBaseVersion( + versionNumber = 2, + plugin = new UpgradePluginPR1322(featureFactoryConfig), + prBasedVersionString = Some("PR 1322") + ), + PluginForKnoraBaseVersion( + versionNumber = 3, + plugin = new UpgradePluginPR1367(featureFactoryConfig), + prBasedVersionString = Some("PR 1367") + ), + PluginForKnoraBaseVersion( + versionNumber = 4, + plugin = new UpgradePluginPR1372(featureFactoryConfig), + prBasedVersionString = Some("PR 1372") + ), PluginForKnoraBaseVersion(versionNumber = 5, plugin = new NoopPlugin, prBasedVersionString = Some("PR 1440")), PluginForKnoraBaseVersion(versionNumber = 6, plugin = new NoopPlugin), // PR 1206 PluginForKnoraBaseVersion(versionNumber = 7, plugin = new NoopPlugin), // PR 1403 @@ -39,8 +47,8 @@ object RepositoryUpdatePlan { ) /** - * The built-in named graphs that are always updated when there is a new version of knora-base. - */ + * The built-in named graphs that are always updated when there is a new version of knora-base. + */ val builtInNamedGraphs: Set[BuiltInNamedGraph] = Set( BuiltInNamedGraph( filename = "knora-ontologies/knora-admin.ttl", @@ -65,15 +73,17 @@ object RepositoryUpdatePlan { ) /** - * Represents an update plugin with its knora-base version number and version string. - * - * @param versionNumber the knora-base version number that the plugin's transformation produces. - * @param plugin the plugin. - * @param prBasedVersionString the plugin's PR-based version string (not used for new plugins). - */ - case class PluginForKnoraBaseVersion(versionNumber: Int, - plugin: UpgradePlugin, - prBasedVersionString: Option[String] = None) { + * Represents an update plugin with its knora-base version number and version string. + * + * @param versionNumber the knora-base version number that the plugin's transformation produces. + * @param plugin the plugin. + * @param prBasedVersionString the plugin's PR-based version string (not used for new plugins). + */ + case class PluginForKnoraBaseVersion( + versionNumber: Int, + plugin: UpgradePlugin, + prBasedVersionString: Option[String] = None + ) { lazy val versionString: String = { prBasedVersionString match { case Some(str) => str @@ -83,11 +93,11 @@ object RepositoryUpdatePlan { } /** - * Represents a Knora built-in named graph. - * - * @param filename the filename containing the named graph. - * @param iri the IRI of the named graph. - */ + * Represents a Knora built-in named graph. + * + * @param filename the filename containing the named graph. + * @param iri the IRI of the named graph. + */ case class BuiltInNamedGraph(filename: String, iri: String) } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala index 3a88477f2f..c350b4b5be 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala @@ -20,116 +20,118 @@ import org.knora.webapi.util.FileUtil import scala.concurrent.{ExecutionContext, Future} /** - * Updates a Knora repository to work with the current version of Knora. - * - * @param system the Akka [[ActorSystem]]. - * @param appActor a reference to the main application actor. - * @param featureFactoryConfig the feature factory configuration. - * @param settings the Knora application settings. - */ -class RepositoryUpdater(system: ActorSystem, - appActor: ActorRef, - featureFactoryConfig: FeatureFactoryConfig, - settings: KnoraSettingsImpl) - extends LazyLogging { + * Updates a Knora repository to work with the current version of Knora. + * + * @param system the Akka [[ActorSystem]]. + * @param appActor a reference to the main application actor. + * @param featureFactoryConfig the feature factory configuration. + * @param settings the Knora application settings. + */ +class RepositoryUpdater( + system: ActorSystem, + appActor: ActorRef, + featureFactoryConfig: FeatureFactoryConfig, + settings: KnoraSettingsImpl +) extends LazyLogging { private val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) // A SPARQL query to find out the knora-base version in a repository. private val knoraBaseVersionQuery = """PREFIX knora-base: - | - |SELECT ?knoraBaseVersion WHERE { - | knora-base:ontologyVersion ?knoraBaseVersion . - |}""".stripMargin + | + |SELECT ?knoraBaseVersion WHERE { + | knora-base:ontologyVersion ?knoraBaseVersion . + |}""".stripMargin /** - * The execution context for futures created in Knora actors. - */ + * The execution context for futures created in Knora actors. + */ private implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) /** - * A string formatter. - */ + * A string formatter. + */ private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * The application's default timeout for `ask` messages. - */ + * The application's default timeout for `ask` messages. + */ private implicit val timeout: Timeout = settings.defaultTimeout /** - * Provides logging. - */ + * Provides logging. + */ private val log: Logger = logger /** - * A list of available plugins. - */ + * A list of available plugins. + */ private val plugins: Seq[PluginForKnoraBaseVersion] = RepositoryUpdatePlan.makePluginsForVersions( featureFactoryConfig = featureFactoryConfig, log = log ) /** - * Updates the repository, if necessary, to work with the current version of Knora. - * - * @return a response indicating what was done. - */ - def maybeUpdateRepository: Future[RepositoryUpdatedResponse] = { + * Updates the repository, if necessary, to work with the current version of Knora. + * + * @return a response indicating what was done. + */ + def maybeUpdateRepository: Future[RepositoryUpdatedResponse] = for { foundRepositoryVersion: Option[String] <- getRepositoryVersion requiredRepositoryVersion = org.knora.webapi.KnoraBaseVersion // Is the repository up to date? repositoryUpToData = foundRepositoryVersion.contains(requiredRepositoryVersion) - repositoryUpdatedResponse: RepositoryUpdatedResponse <- if (repositoryUpToData) { - // Yes. Nothing more to do. - FastFuture.successful(RepositoryUpdatedResponse(s"Repository is up to date at $requiredRepositoryVersion")) - } else { - // No. Construct the list of updates that it needs. - - log.info( - s"Repository not up-to-date. Found: ${foundRepositoryVersion.getOrElse("None")}, Required: $requiredRepositoryVersion" - ) - - val selectedPlugins: Seq[PluginForKnoraBaseVersion] = selectPluginsForNeededUpdates(foundRepositoryVersion) - log.info(s"Updating repository with transformations: ${selectedPlugins.map(_.versionString).mkString(", ")}") - - // Update it with those plugins. - updateRepositoryWithSelectedPlugins(selectedPlugins) - } + repositoryUpdatedResponse: RepositoryUpdatedResponse <- + if (repositoryUpToData) { + // Yes. Nothing more to do. + FastFuture.successful(RepositoryUpdatedResponse(s"Repository is up to date at $requiredRepositoryVersion")) + } else { + // No. Construct the list of updates that it needs. + + log.info( + s"Repository not up-to-date. Found: ${foundRepositoryVersion.getOrElse("None")}, Required: $requiredRepositoryVersion" + ) + + val selectedPlugins: Seq[PluginForKnoraBaseVersion] = selectPluginsForNeededUpdates(foundRepositoryVersion) + log.info(s"Updating repository with transformations: ${selectedPlugins.map(_.versionString).mkString(", ")}") + + // Update it with those plugins. + updateRepositoryWithSelectedPlugins(selectedPlugins) + } } yield repositoryUpdatedResponse - } /** - * Determines the `knora-base` version in the repository. - * - * @return the `knora-base` version string, if any, in the repository. - */ - private def getRepositoryVersion: Future[Option[String]] = { + * Determines the `knora-base` version in the repository. + * + * @return the `knora-base` version string, if any, in the repository. + */ + private def getRepositoryVersion: Future[Option[String]] = for { repositoryVersionResponse: SparqlSelectResult <- (appActor ? SparqlSelectRequest(knoraBaseVersionQuery)) .mapTo[SparqlSelectResult] bindings = repositoryVersionResponse.results.bindings - versionString = if (bindings.nonEmpty) { - Some(bindings.head.rowMap("knoraBaseVersion")) - } else { - None - } + versionString = + if (bindings.nonEmpty) { + Some(bindings.head.rowMap("knoraBaseVersion")) + } else { + None + } } yield versionString - } /** - * Constructs a list of update plugins that need to be run to update the repository. - * - * @param maybeRepositoryVersionString the `knora-base` version string, if any, in the repository. - * @return the plugins needed to update the repository. - */ + * Constructs a list of update plugins that need to be run to update the repository. + * + * @param maybeRepositoryVersionString the `knora-base` version string, if any, in the repository. + * @return the plugins needed to update the repository. + */ private def selectPluginsForNeededUpdates( - maybeRepositoryVersionString: Option[String]): Seq[PluginForKnoraBaseVersion] = { + maybeRepositoryVersionString: Option[String] + ): Seq[PluginForKnoraBaseVersion] = maybeRepositoryVersionString match { case Some(repositoryVersion) => // The repository has a version string. Get the plugins for all subsequent versions. @@ -152,16 +154,16 @@ class RepositoryUpdater(system: ActorSystem, // The repository has no version string. Include all updates. plugins } - } /** - * Updates the repository with the specified list of plugins. - * - * @param pluginsForNeededUpdates the plugins needed to update the repository. - * @return a [[RepositoryUpdatedResponse]] indicating what was done. - */ + * Updates the repository with the specified list of plugins. + * + * @param pluginsForNeededUpdates the plugins needed to update the repository. + * @return a [[RepositoryUpdatedResponse]] indicating what was done. + */ private def updateRepositoryWithSelectedPlugins( - pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion]): Future[RepositoryUpdatedResponse] = { + pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion] + ): Future[RepositoryUpdatedResponse] = { // Was a download directory specified in the application settings? val downloadDir: Path = settings.upgradeDownloadDir match { case Some(configuredDir) => @@ -207,22 +209,23 @@ class RepositoryUpdater(system: ActorSystem, // Upload the transformed repository. _: RepositoryUploadedResponse <- (appActor ? UploadRepositoryRequest(transformedRepositoryFile)) .mapTo[RepositoryUploadedResponse] - } yield - RepositoryUpdatedResponse( - message = s"Updated repository to ${org.knora.webapi.KnoraBaseVersion}" - ) + } yield RepositoryUpdatedResponse( + message = s"Updated repository to ${org.knora.webapi.KnoraBaseVersion}" + ) } /** - * Transforms a file containing a downloaded repository. - * - * @param downloadedRepositoryFile the downloaded repository. - * @param transformedRepositoryFile the transformed file. - * @param pluginsForNeededUpdates the plugins needed to update the repository. - */ - private def doTransformations(downloadedRepositoryFile: Path, - transformedRepositoryFile: Path, - pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion]): Unit = { + * Transforms a file containing a downloaded repository. + * + * @param downloadedRepositoryFile the downloaded repository. + * @param transformedRepositoryFile the transformed file. + * @param pluginsForNeededUpdates the plugins needed to update the repository. + */ + private def doTransformations( + downloadedRepositoryFile: Path, + transformedRepositoryFile: Path, + pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion] + ): Unit = { // Parse the input file. log.info("Reading repository file...") val model = rdfFormatUtil.fileToRdfModel(file = downloadedRepositoryFile, rdfFormat = NQuads) @@ -248,11 +251,11 @@ class RepositoryUpdater(system: ActorSystem, } /** - * Adds Knora's built-in named graphs to an [[RdfModel]]. - * - * @param model the [[RdfModel]]. - */ - private def addBuiltInNamedGraphsToModel(model: RdfModel): Unit = { + * Adds Knora's built-in named graphs to an [[RdfModel]]. + * + * @param model the [[RdfModel]]. + */ + private def addBuiltInNamedGraphsToModel(model: RdfModel): Unit = // Add each built-in named graph to the model. for (builtInNamedGraph <- RepositoryUpdatePlan.builtInNamedGraphs) { val context: IRI = builtInNamedGraph.iri @@ -278,15 +281,14 @@ class RepositoryUpdater(system: ActorSystem, ) } } - } /** - * Reads a file from the CLASSPATH into an [[RdfModel]]. - * - * @param filename the filename. - * @param rdfFormat the file format. - * @return an [[RdfModel]] representing the contents of the file. - */ + * Reads a file from the CLASSPATH into an [[RdfModel]]. + * + * @param filename the filename. + * @param rdfFormat the file format. + * @return an [[RdfModel]] representing the contents of the file. + */ def readResourceIntoModel(filename: String, rdfFormat: NonJsonLD): RdfModel = { val fileContent: String = FileUtil.readTextResource(filename) rdfFormatUtil.parseToRdfModel(fileContent, rdfFormat) diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/UpgradePlugin.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/UpgradePlugin.scala index c2cfd26e58..1b41ef6199 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/UpgradePlugin.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/UpgradePlugin.scala @@ -3,14 +3,14 @@ package org.knora.webapi.store.triplestore.upgrade import org.knora.webapi.messages.util.rdf.RdfModel /** - * A trait for plugins that update a repository. - */ + * A trait for plugins that update a repository. + */ trait UpgradePlugin { /** - * Transforms a repository. - * - * @param model an [[RdfModel]] containing the repository data. - */ + * Transforms a repository. + * + * @param model an [[RdfModel]] containing the repository data. + */ def transform(model: RdfModel): Unit } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/NoopPlugin.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/NoopPlugin.scala index 50a798d8ee..fd70bc166f 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/NoopPlugin.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/NoopPlugin.scala @@ -23,8 +23,8 @@ import org.knora.webapi.messages.util.rdf.RdfModel import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * An update plugin that does nothing. Used for updates in which only the built-in named graphs have changed. - */ + * An update plugin that does nothing. Used for updates in which only the built-in named graphs have changed. + */ class NoopPlugin extends UpgradePlugin { override def transform(model: RdfModel): Unit = {} } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1307.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1307.scala index 98a43a5f86..46e710093e 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1307.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1307.scala @@ -27,8 +27,8 @@ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * Transforms a repository for Knora PR 1307. - */ + * Transforms a repository for Knora PR 1307. + */ class UpgradePluginPR1307(featureFactoryConfig: FeatureFactoryConfig) extends UpgradePlugin { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(featureFactoryConfig) @@ -46,19 +46,20 @@ class UpgradePluginPR1307(featureFactoryConfig: FeatureFactoryConfig) extends Up nodeFactory.makeIriNode(OntologyConstants.KnoraBase.ValueHasMaxStandoffStartIndex) /** - * Represents a standoff tag to be transformed. - * - * @param oldIri the tag's old IRI. - * @param statements the statements about the tag. - */ + * Represents a standoff tag to be transformed. + * + * @param oldIri the tag's old IRI. + * @param statements the statements about the tag. + */ case class StandoffRdf(oldIri: IriNode, statements: Set[Statement]) { def notFound = throw InconsistentRepositoryDataException( - s"$oldIri does not have knora-base:standoffTagHasStartIndex with an integer object") + s"$oldIri does not have knora-base:standoffTagHasStartIndex with an integer object" + ) /** - * The value of knora-base:standoffTagHasStartIndex. - */ + * The value of knora-base:standoffTagHasStartIndex. + */ val startIndex: Int = statements.find { statement => statement.subj == oldIri && statement.pred == StandoffTagHasStartIndexIri } match { @@ -72,15 +73,15 @@ class UpgradePluginPR1307(featureFactoryConfig: FeatureFactoryConfig) extends Up } /** - * The tag's new IRI. - */ + * The tag's new IRI. + */ lazy val newIri: IriNode = { val oldSubjStr: String = oldIri.stringValue val slashPos: Int = oldSubjStr.lastIndexOf('/') nodeFactory.makeIriNode(oldSubjStr.substring(0, slashPos + 1) + startIndex.toString) } - def transform(model: RdfModel, standoff: Map[IriNode, StandoffRdf]): Unit = { + def transform(model: RdfModel, standoff: Map[IriNode, StandoffRdf]): Unit = for (statement: Statement <- statements) { // Change statements with knora-base:standoffTagHasStartParent and knora-base:standoffTagHasEndParent to point // to the new IRIs of those tags. @@ -103,23 +104,24 @@ class UpgradePluginPR1307(featureFactoryConfig: FeatureFactoryConfig) extends Up context = statement.context ) } - } } /** - * Represents a `knora-base:TextValue` to be transformed. - * - * @param iri the text value's IRI. - * @param context the text value's context. - * @param valueHasStandoffStatements the statements whose subject is the text value and whose predicate is - * `knora-base:valueHasStandoff`. - * @param standoff the standoff tags attached to this text value, as a map of old standoff tag IRIs to - * [[StandoffRdf]] objects. - */ - case class TextValueRdf(iri: IriNode, - context: Option[IRI], - valueHasStandoffStatements: Set[Statement], - standoff: Map[IriNode, StandoffRdf]) { + * Represents a `knora-base:TextValue` to be transformed. + * + * @param iri the text value's IRI. + * @param context the text value's context. + * @param valueHasStandoffStatements the statements whose subject is the text value and whose predicate is + * `knora-base:valueHasStandoff`. + * @param standoff the standoff tags attached to this text value, as a map of old standoff tag IRIs to + * [[StandoffRdf]] objects. + */ + case class TextValueRdf( + iri: IriNode, + context: Option[IRI], + valueHasStandoffStatements: Set[Statement], + standoff: Map[IriNode, StandoffRdf] + ) { def transform(model: RdfModel): Unit = { // Transform the text value's standoff tags. for (standoffTag <- standoff.values) { @@ -148,23 +150,22 @@ class UpgradePluginPR1307(featureFactoryConfig: FeatureFactoryConfig) extends Up model.add( subj = iri, pred = ValueHasMaxStandoffStartIndexIri, - obj = nodeFactory.makeDatatypeLiteral(standoff.values.map(_.startIndex).max.toString, - OntologyConstants.Xsd.Integer), + obj = nodeFactory + .makeDatatypeLiteral(standoff.values.map(_.startIndex).max.toString, OntologyConstants.Xsd.Integer), context = context ) } } } - override def transform(model: RdfModel): Unit = { + override def transform(model: RdfModel): Unit = for (textValue <- collectTextValues(model)) { textValue.transform(model) } - } /** - * Collects the text values and standoff tags in the repository. - */ + * Collects the text values and standoff tags in the repository. + */ private def collectTextValues(model: RdfModel): Vector[TextValueRdf] = { // A map of text value IRIs to their contexts. @@ -179,52 +180,52 @@ class UpgradePluginPR1307(featureFactoryConfig: FeatureFactoryConfig) extends Up } .toMap - textValueSubjectsAndContexts.map { - case (textValueSubj: IriNode, textValueContext: Option[IRI]) => - // Get the statements about the text value. - val textValueStatements: Set[Statement] = model - .find( - subj = Some(textValueSubj), - pred = None, - obj = None - ) - .toSet + textValueSubjectsAndContexts.map { case (textValueSubj: IriNode, textValueContext: Option[IRI]) => + // Get the statements about the text value. + val textValueStatements: Set[Statement] = model + .find( + subj = Some(textValueSubj), + pred = None, + obj = None + ) + .toSet - // Get the statements whose subject is the text value and whose predicate is knora-base:valueHasStandoff. - val valueHasStandoffStatements: Set[Statement] = textValueStatements.filter { statement => - statement.pred == ValueHasStandoffIri - } + // Get the statements whose subject is the text value and whose predicate is knora-base:valueHasStandoff. + val valueHasStandoffStatements: Set[Statement] = textValueStatements.filter { statement => + statement.pred == ValueHasStandoffIri + } - // Get the IRIs of the text value's standoff tags. - val standoffSubjects: Set[IriNode] = valueHasStandoffStatements.map { statement => - statement.obj match { - case iriNode: IriNode => iriNode - case other => - throw InconsistentRepositoryDataException( - s"Unexpected object for $textValueSubj $ValueHasStandoffIri: $other") - } + // Get the IRIs of the text value's standoff tags. + val standoffSubjects: Set[IriNode] = valueHasStandoffStatements.map { statement => + statement.obj match { + case iriNode: IriNode => iriNode + case other => + throw InconsistentRepositoryDataException( + s"Unexpected object for $textValueSubj $ValueHasStandoffIri: $other" + ) } + } - // Make a map of standoff IRIs to StandoffRdf objects. - val standoff: Map[IriNode, StandoffRdf] = standoffSubjects.map { standoffSubj: IriNode => - standoffSubj -> StandoffRdf( - oldIri = standoffSubj, - statements = model - .find( - subj = Some(standoffSubj), - pred = None, - obj = None - ) - .toSet - ) - }.toMap - - TextValueRdf( - iri = textValueSubj, - context = textValueContext, - valueHasStandoffStatements = valueHasStandoffStatements, - standoff = standoff + // Make a map of standoff IRIs to StandoffRdf objects. + val standoff: Map[IriNode, StandoffRdf] = standoffSubjects.map { standoffSubj: IriNode => + standoffSubj -> StandoffRdf( + oldIri = standoffSubj, + statements = model + .find( + subj = Some(standoffSubj), + pred = None, + obj = None + ) + .toSet ) + }.toMap + + TextValueRdf( + iri = textValueSubj, + context = textValueContext, + valueHasStandoffStatements = valueHasStandoffStatements, + standoff = standoff + ) }.toVector } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322.scala index 35c84f8d66..419a3aaa32 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322.scala @@ -26,8 +26,8 @@ import org.knora.webapi.messages.{OntologyConstants, StringFormatter} import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * Transforms a repository for Knora PR 1322. - */ + * Transforms a repository for Knora PR 1322. + */ class UpgradePluginPR1322(featureFactoryConfig: FeatureFactoryConfig) extends UpgradePlugin { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(featureFactoryConfig) private implicit val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies @@ -37,7 +37,7 @@ class UpgradePluginPR1322(featureFactoryConfig: FeatureFactoryConfig) extends Up private val ValueCreationDateIri: IriNode = nodeFactory.makeIriNode(OntologyConstants.KnoraBase.ValueCreationDate) private val PreviousValueIri: IriNode = nodeFactory.makeIriNode(OntologyConstants.KnoraBase.PreviousValue) - override def transform(model: RdfModel): Unit = { + override def transform(model: RdfModel): Unit = // Add a random UUID to each current value version. for (valueIri: IriNode <- collectCurrentValueIris(model)) { model.add( @@ -46,12 +46,11 @@ class UpgradePluginPR1322(featureFactoryConfig: FeatureFactoryConfig) extends Up obj = nodeFactory.makeStringLiteral(stringFormatter.makeRandomBase64EncodedUuid) ) } - } /** - * Collects the IRIs of all values that are current value versions. - */ - private def collectCurrentValueIris(model: RdfModel): Iterator[IriNode] = { + * Collects the IRIs of all values that are current value versions. + */ + private def collectCurrentValueIris(model: RdfModel): Iterator[IriNode] = model .find(None, Some(ValueCreationDateIri), None) .map(_.subj) @@ -68,5 +67,4 @@ class UpgradePluginPR1322(featureFactoryConfig: FeatureFactoryConfig) extends Up case iriNode: IriNode => iriNode case other => throw InconsistentRepositoryDataException(s"Unexpected subject for $ValueCreationDateIri: $other") } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1367.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1367.scala index 174962e783..77ebf8581f 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1367.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1367.scala @@ -25,8 +25,8 @@ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * Transforms a repository for Knora PR 1367. - */ + * Transforms a repository for Knora PR 1367. + */ class UpgradePluginPR1367(featureFactoryConfig: FeatureFactoryConfig) extends UpgradePlugin { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(featureFactoryConfig) diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1372.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1372.scala index 56d0f19c54..7e3e3bdcac 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1372.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1372.scala @@ -26,8 +26,8 @@ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * Transforms a repository for Knora PR 1372. - */ + * Transforms a repository for Knora PR 1372. + */ class UpgradePluginPR1372(featureFactoryConfig: FeatureFactoryConfig) extends UpgradePlugin { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(featureFactoryConfig) @@ -36,7 +36,7 @@ class UpgradePluginPR1372(featureFactoryConfig: FeatureFactoryConfig) extends Up private val PreviousValueIri: IriNode = nodeFactory.makeIriNode(OntologyConstants.KnoraBase.PreviousValue) private val HasPermissionsIri: IriNode = nodeFactory.makeIriNode(OntologyConstants.KnoraBase.HasPermissions) - override def transform(model: RdfModel): Unit = { + override def transform(model: RdfModel): Unit = // Remove knora-base:hasPermissions from all past value versions. for (valueIri: IriNode <- collectPastValueIris(model)) { model.remove( @@ -45,12 +45,11 @@ class UpgradePluginPR1372(featureFactoryConfig: FeatureFactoryConfig) extends Up obj = None ) } - } /** - * Collects the IRIs of all values that are past value versions. - */ - private def collectPastValueIris(model: RdfModel): Iterator[IriNode] = { + * Collects the IRIs of all values that are past value versions. + */ + private def collectPastValueIris(model: RdfModel): Iterator[IriNode] = model .find(None, Some(ValueCreationDateIri), None) .map(_.subj) @@ -67,5 +66,4 @@ class UpgradePluginPR1372(featureFactoryConfig: FeatureFactoryConfig) extends Up case iriNode: IriNode => iriNode case other => throw InconsistentRepositoryDataException(s"Unexpected subject for $ValueCreationDateIri: $other") } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1615.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1615.scala index d20a116881..82e8dea586 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1615.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1615.scala @@ -24,20 +24,19 @@ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * Transforms a repository for Knora PR 1615. - */ + * Transforms a repository for Knora PR 1615. + */ class UpgradePluginPR1615(featureFactoryConfig: FeatureFactoryConfig) extends UpgradePlugin { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(featureFactoryConfig) // IRI objects representing the IRIs used in this transformation. private val ForbiddenResourceIri: IriNode = nodeFactory.makeIriNode("http://rdfh.ch/0000/forbiddenResource") - override def transform(model: RdfModel): Unit = { + override def transform(model: RdfModel): Unit = // Remove the singleton instance of knora-base:ForbiddenResource. model.remove( subj = Some(ForbiddenResourceIri), pred = None, obj = None ) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746.scala index b87a22379f..92848cb8c4 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746.scala @@ -26,8 +26,8 @@ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin /** - * Transforms a repository for Knora PR 1746. - */ + * Transforms a repository for Knora PR 1746. + */ class UpgradePluginPR1746(featureFactoryConfig: FeatureFactoryConfig, log: Logger) extends UpgradePlugin { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(featureFactoryConfig) diff --git a/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala index 173ed32305..ed849fe86c 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala @@ -37,37 +37,38 @@ import scala.util.{Failure, Success, Try} object ActorUtil { /** - * A convenience function that simplifies and centralises error-handling in the `receive` method of supervised Akka - * actors that expect to receive messages sent using the `ask` pattern. - * - * Such an actor should handle errors by returning a [[Status.Failure]] message to the sender. If this is not done, - * the sender's `ask` times out, and the sender has no way of finding out why. This function ensures that a - * [[Status.Failure]] is sent. - * - * If an error occurs that isn't the client's fault, the actor will also want to report it to the actor's supervisor, - * so the supervisor can carry out its own error-handling policy. - * - * It is also useful to give the sender the original exception object so that a stack trace can be - * logged. However, some exception classes are not serializable and therefore cannot be sent as Akka messages. This - * function ensures that stack traces are logged in those cases. - * - * The function takes as arguments the sender of the `ask` request, and a [[Future]] representing the result - * of processing the request. It first converts the `Future` to a reply message, which it sends back to the sender. - * If the `Future` succeeded, the reply message is the object it contains. If the `Future` failed, the reply message - * is a [[Status.Failure]]. It will contain the original exception if it is serializable; otherwise, the original - * exception is logged, and a [[WrapperException]] is sent instead. The [[WrapperException]] will contain the result - * of calling `toString` on the original exception. - * - * After the reply message is sent, if the `Future` succeeded, or contained a [[RequestRejectedException]], - * nothing more is done. If it contained any other exception, the function triggers the supervisor's error-handling - * policy by throwing whatever exception was returned to the sender. - * - * @param sender the actor that made the request in the `ask` pattern. - * @param future a [[Future]] that will provide the result of the sender's request. - * @param log a [[LoggingAdapter]] for logging non-serializable exceptions. - */ - def future2Message[ReplyT](sender: ActorRef, future: Future[ReplyT], log: LoggingAdapter)( - implicit executionContext: ExecutionContext): Unit = { + * A convenience function that simplifies and centralises error-handling in the `receive` method of supervised Akka + * actors that expect to receive messages sent using the `ask` pattern. + * + * Such an actor should handle errors by returning a [[Status.Failure]] message to the sender. If this is not done, + * the sender's `ask` times out, and the sender has no way of finding out why. This function ensures that a + * [[Status.Failure]] is sent. + * + * If an error occurs that isn't the client's fault, the actor will also want to report it to the actor's supervisor, + * so the supervisor can carry out its own error-handling policy. + * + * It is also useful to give the sender the original exception object so that a stack trace can be + * logged. However, some exception classes are not serializable and therefore cannot be sent as Akka messages. This + * function ensures that stack traces are logged in those cases. + * + * The function takes as arguments the sender of the `ask` request, and a [[Future]] representing the result + * of processing the request. It first converts the `Future` to a reply message, which it sends back to the sender. + * If the `Future` succeeded, the reply message is the object it contains. If the `Future` failed, the reply message + * is a [[Status.Failure]]. It will contain the original exception if it is serializable; otherwise, the original + * exception is logged, and a [[WrapperException]] is sent instead. The [[WrapperException]] will contain the result + * of calling `toString` on the original exception. + * + * After the reply message is sent, if the `Future` succeeded, or contained a [[RequestRejectedException]], + * nothing more is done. If it contained any other exception, the function triggers the supervisor's error-handling + * policy by throwing whatever exception was returned to the sender. + * + * @param sender the actor that made the request in the `ask` pattern. + * @param future a [[Future]] that will provide the result of the sender's request. + * @param log a [[LoggingAdapter]] for logging non-serializable exceptions. + */ + def future2Message[ReplyT](sender: ActorRef, future: Future[ReplyT], log: LoggingAdapter)(implicit + executionContext: ExecutionContext + ): Unit = future.onComplete { tryObj: Try[ReplyT] => try2Message( sender = sender, @@ -75,17 +76,17 @@ object ActorUtil { log = log ) } - } /** - * Like `future2Message`, but takes a `Try` instead of a `Future`. - * - * @param sender the actor that made the request in the `ask` pattern. - * @param tryObj a [[Try]] that will provide the result of the sender's request. - * @param log a [[LoggingAdapter]] for logging non-serializable exceptions. - */ - def try2Message[ReplyT](sender: ActorRef, tryObj: Try[ReplyT], log: LoggingAdapter)( - implicit executionContext: ExecutionContext): Unit = { + * Like `future2Message`, but takes a `Try` instead of a `Future`. + * + * @param sender the actor that made the request in the `ask` pattern. + * @param tryObj a [[Try]] that will provide the result of the sender's request. + * @param log a [[LoggingAdapter]] for logging non-serializable exceptions. + */ + def try2Message[ReplyT](sender: ActorRef, tryObj: Try[ReplyT], log: LoggingAdapter)(implicit + executionContext: ExecutionContext + ): Unit = tryObj match { case Success(result) => sender ! result @@ -109,75 +110,70 @@ object ActorUtil { throw otherThrowable } } - } /** - * An actor that expects to receive messages sent using the `ask` pattern can use this method to handle - * unexpected request messages in a consistent way. - * - * @param sender the actor that made the request in the `ask` pattern. - * @param message the message that was received. - * @param log a [[LoggingAdapter]]. - */ - def handleUnexpectedMessage(sender: ActorRef, message: Any, log: LoggingAdapter, who: String)( - implicit executionContext: ExecutionContext): Unit = { + * An actor that expects to receive messages sent using the `ask` pattern can use this method to handle + * unexpected request messages in a consistent way. + * + * @param sender the actor that made the request in the `ask` pattern. + * @param message the message that was received. + * @param log a [[LoggingAdapter]]. + */ + def handleUnexpectedMessage(sender: ActorRef, message: Any, log: LoggingAdapter, who: String)(implicit + executionContext: ExecutionContext + ): Unit = { val unexpectedMessageException = UnexpectedMessageException( - s"$who received an unexpected message $message of type ${message.getClass.getCanonicalName}") + s"$who received an unexpected message $message of type ${message.getClass.getCanonicalName}" + ) sender ! akka.actor.Status.Failure(unexpectedMessageException) } /** - * Converts a [[Map]] containing futures of values into a future containing a [[Map]] of values. - * - * @param mapToSequence the [[Map]] to be converted. - * @return a future that will provide the results of the futures that were in the [[Map]]. - */ - def sequenceFuturesInMap[KeyT: ClassTag, ValueT](mapToSequence: Map[KeyT, Future[ValueT]])( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Map[KeyT, ValueT]] = { - Future - .sequence { - mapToSequence.map { - case (key: KeyT, futureValue: Future[ValueT]) => - futureValue.map { value => - key -> value - } + * Converts a [[Map]] containing futures of values into a future containing a [[Map]] of values. + * + * @param mapToSequence the [[Map]] to be converted. + * @return a future that will provide the results of the futures that were in the [[Map]]. + */ + def sequenceFuturesInMap[KeyT: ClassTag, ValueT]( + mapToSequence: Map[KeyT, Future[ValueT]] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Map[KeyT, ValueT]] = + Future.sequence { + mapToSequence.map { case (key: KeyT, futureValue: Future[ValueT]) => + futureValue.map { value => + key -> value } } + } .map(_.toMap) - } /** - * Converts a [[Map]] containing futures of sequences into a future containing a [[Map]] containing sequences. - * - * @param mapToSequence the [[Map]] to be converted. - * @return a future that will provide the results of the futures that were in the [[Map]]. - */ - def sequenceFutureSeqsInMap[KeyT: ClassTag, ElemT](mapToSequence: Map[KeyT, Future[Seq[ElemT]]])( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Map[KeyT, Seq[ElemT]]] = { + * Converts a [[Map]] containing futures of sequences into a future containing a [[Map]] containing sequences. + * + * @param mapToSequence the [[Map]] to be converted. + * @return a future that will provide the results of the futures that were in the [[Map]]. + */ + def sequenceFutureSeqsInMap[KeyT: ClassTag, ElemT]( + mapToSequence: Map[KeyT, Future[Seq[ElemT]]] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Map[KeyT, Seq[ElemT]]] = // See http://stackoverflow.com/a/17479415 - Future - .sequence { - mapToSequence.map { - case (key: KeyT, futureSeq: Future[Seq[ElemT]]) => - futureSeq.map { elements: Seq[ElemT] => - (key, elements) - } + Future.sequence { + mapToSequence.map { case (key: KeyT, futureSeq: Future[Seq[ElemT]]) => + futureSeq.map { elements: Seq[ElemT] => + (key, elements) } } + } .map(_.toMap) - } /** - * Converts a [[Map]] containing sequences of futures into a future containing a [[Map]] containing sequences. - * - * @param mapToSequence the [[Map]] to be converted. - * @return a future that will provide the results of the futures that were in the [[Map]]. - */ - def sequenceSeqFuturesInMap[KeyT: ClassTag, ElemT](mapToSequence: Map[KeyT, Seq[Future[ElemT]]])( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[Map[KeyT, Seq[ElemT]]] = { + * Converts a [[Map]] containing sequences of futures into a future containing a [[Map]] containing sequences. + * + * @param mapToSequence the [[Map]] to be converted. + * @return a future that will provide the results of the futures that were in the [[Map]]. + */ + def sequenceSeqFuturesInMap[KeyT: ClassTag, ElemT]( + mapToSequence: Map[KeyT, Seq[Future[ElemT]]] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[Map[KeyT, Seq[ElemT]]] = { val transformedMap: Map[KeyT, Future[Seq[ElemT]]] = mapToSequence.map { case (key: KeyT, seqFuture: Seq[Future[ElemT]]) => key -> Future.sequence(seqFuture) } @@ -186,44 +182,44 @@ object ActorUtil { } /** - * Converts an option containing a future to a future containing an option. - * - * @param optionFuture an option containing a future. - * @return a future containing an option. - */ - def optionFuture2FutureOption[A](optionFuture: Option[Future[A]])( - implicit executionContext: ExecutionContext): Future[Option[A]] = { + * Converts an option containing a future to a future containing an option. + * + * @param optionFuture an option containing a future. + * @return a future containing an option. + */ + def optionFuture2FutureOption[A]( + optionFuture: Option[Future[A]] + )(implicit executionContext: ExecutionContext): Future[Option[A]] = optionFuture match { case Some(f) => f.map(Some(_)) case None => Future.successful(None) } - } /** - * Runs a sequence of tasks. - * - * @param firstTask the first task in the sequence. - * @tparam T the type of the underlying task result. - * @return the result of the last task in the sequence. - */ - def runTasks[T](firstTask: Task[T])(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TaskResult[T]] = { + * Runs a sequence of tasks. + * + * @param firstTask the first task in the sequence. + * @tparam T the type of the underlying task result. + * @return the result of the last task in the sequence. + */ + def runTasks[T]( + firstTask: Task[T] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TaskResult[T]] = runTasksRec(previousResult = None, nextTask = firstTask) - } /** - * Recursively runs a sequence of tasks. - * - * @param previousResult the previous result or `None` if this is the first task in the sequence. - * @param nextTask the next task to be run. - * @tparam T the type of the underlying task result. - * @return the result of the last task in the sequence. - */ - private def runTasksRec[T](previousResult: Option[TaskResult[T]], nextTask: Task[T])( - implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TaskResult[T]] = { + * Recursively runs a sequence of tasks. + * + * @param previousResult the previous result or `None` if this is the first task in the sequence. + * @param nextTask the next task to be run. + * @tparam T the type of the underlying task result. + * @return the result of the last task in the sequence. + */ + private def runTasksRec[T](previousResult: Option[TaskResult[T]], nextTask: Task[T])(implicit + timeout: Timeout, + executionContext: ExecutionContext + ): Future[TaskResult[T]] = // This function doesn't need to be tail recursive: https://stackoverflow.com/a/16986416 - for { taskResult: TaskResult[T] <- nextTask.runTask(previousResult) @@ -232,40 +228,40 @@ object ActorUtil { case None => FastFuture.successful(taskResult) } } yield recResult - } } /** - * Represents the result of a task in a sequence of tasks. - * - * @tparam T the type of the underlying task result. - */ + * Represents the result of a task in a sequence of tasks. + * + * @tparam T the type of the underlying task result. + */ trait TaskResult[T] { /** - * Returns the underlying result of this task. - */ + * Returns the underlying result of this task. + */ def underlyingResult: T /** - * Returns the next task, or `None` if this was the last task. - */ + * Returns the next task, or `None` if this was the last task. + */ def nextTask: Option[Task[T]] } /** - * Represents a task in a sequence of tasks. - * - * @tparam T the type of the underlying task result. - */ + * Represents a task in a sequence of tasks. + * + * @tparam T the type of the underlying task result. + */ trait Task[T] { /** - * Runs the task. - * - * @param previousResult the result of the previous task, or `None` if this is the first task in the sequence. - * @return the result of this task. - */ - def runTask(previousResult: Option[TaskResult[T]])(implicit timeout: Timeout, - executionContext: ExecutionContext): Future[TaskResult[T]] + * Runs the task. + * + * @param previousResult the result of the previous task, or `None` if this is the first task in the sequence. + * @return the result of this task. + */ + def runTask( + previousResult: Option[TaskResult[T]] + )(implicit timeout: Timeout, executionContext: ExecutionContext): Future[TaskResult[T]] } diff --git a/webapi/src/main/scala/org/knora/webapi/util/ApacheLuceneSupport.scala b/webapi/src/main/scala/org/knora/webapi/util/ApacheLuceneSupport.scala index 32e10590b8..7c45cdd274 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/ApacheLuceneSupport.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/ApacheLuceneSupport.scala @@ -22,8 +22,8 @@ package org.knora.webapi.util import scala.util.matching.Regex /** - * Provides some functionality to pre-process a given search string so it supports the Apache Lucene Query Parser syntax. - */ + * Provides some functionality to pre-process a given search string so it supports the Apache Lucene Query Parser syntax. + */ object ApacheLuceneSupport { private val wildcard = "*" @@ -37,11 +37,11 @@ object ApacheLuceneSupport { val separateTermsAndPhrasesRegex = new Regex("([^\\s]*\".*?\"[^\\s]*)|([^\\s]+)") /** - * Searches for a resource by its rdfs:label as the user is typing. - * - * @param terms the terms to search for. - * @param lastTerm the last term the user is entering at the moment. - */ + * Searches for a resource by its rdfs:label as the user is typing. + * + * @param terms the terms to search for. + * @param lastTerm the last term the user is entering at the moment. + */ case class MatchStringWhileTyping(private val terms: Seq[String], lastTerm: String) { // @@ -53,71 +53,61 @@ object ApacheLuceneSupport { // /** - * Generates a string literal to be used as an object in a statement with the Apache Lucene predicate. - * It combines all terms with a logical AND and adds the last term with a wildcard at the end, not taking into account the given sequence of the elements. - * - * This matches all the strings that contain all of the terms in any order and the term with the wildcard. - * - * @return a string literal. - */ - def generateLiteralForLuceneIndexWithoutExactSequence: String = { - + * Generates a string literal to be used as an object in a statement with the Apache Lucene predicate. + * It combines all terms with a logical AND and adds the last term with a wildcard at the end, not taking into account the given sequence of the elements. + * + * This matches all the strings that contain all of the terms in any order and the term with the wildcard. + * + * @return a string literal. + */ + def generateLiteralForLuceneIndexWithoutExactSequence: String = // Combine all terms with a logical AND and add a wildcard to the last term. // Finds all the strings that contain all of the terms in any order and the last term with the wildcard at the end. - if (terms.nonEmpty) { s"${this.terms.mkString(s" $logicalAnd ")} $logicalAnd $lastTerm$wildcard" } else { s"$lastTerm$wildcard" } - } /** - * Generates a string literal to be used as an object in a statement with the Apache Lucene predicate. - * It generates one string from the given terms to be enclosed with double quotes and appends the last term to it with a wildcard at the end. - * - * This matches all the strings that contain the exact phrase and the term with the wildcard. - * Example: "Reise ins" "Heili" -> "Reise ins" AND Heili* - * - * @return a string literal. - */ - def generateLiteralForLuceneIndexWithExactSequence: String = { - + * Generates a string literal to be used as an object in a statement with the Apache Lucene predicate. + * It generates one string from the given terms to be enclosed with double quotes and appends the last term to it with a wildcard at the end. + * + * This matches all the strings that contain the exact phrase and the term with the wildcard. + * Example: "Reise ins" "Heili" -> "Reise ins" AND Heili* + * + * @return a string literal. + */ + def generateLiteralForLuceneIndexWithExactSequence: String = // Combine all the terms to a phrase (to be enclosed by double quotes) and add a wildcard to the last term. // This finds all the strings that contain the exact phrase and the term with the wildcard at the end. // Example: "Reise ins" "Heili" -> "Reise ins" AND Heili* - if (terms.nonEmpty) { s""""${this.terms.mkString(" ")}" $logicalAnd $lastTerm$wildcard""" } else { s"$lastTerm$wildcard" } - } /** - * Generates a Filter regex that makes sure that phrase and the last term occur exactly in the given order. - * - * @param labelVarName the name of the variable representing the string to check. - * @return a FILTER regex statement, if phrase is given. - */ - def generateRegexFilterStatementForExactSequenceMatch(labelVarName: String): String = { - + * Generates a Filter regex that makes sure that phrase and the last term occur exactly in the given order. + * + * @param labelVarName the name of the variable representing the string to check. + * @return a FILTER regex statement, if phrase is given. + */ + def generateRegexFilterStatementForExactSequenceMatch(labelVarName: String): String = // Apply filter statement containing a regex to make sure that lastTerm directly follows the phrase // (only necessary if we have several terms to search for). - if (terms.nonEmpty) { s"FILTER regex(?$labelVarName, '${this.terms.mkString(" ")} $lastTerm$wildcard', 'i')" } else { "" } - } - } /** - * Companion object providing constructor. - */ + * Companion object providing constructor. + */ object MatchStringWhileTyping { def apply(searchString: String): MatchStringWhileTyping = { @@ -137,29 +127,27 @@ object ApacheLuceneSupport { } /** - * Handles Boolean logic for given search terms. - * - * @param queryString given search terms. - */ + * Handles Boolean logic for given search terms. + * + * @param queryString given search terms. + */ case class LuceneQueryString(private val queryString: String) { /** - * Returns the query string. - * - * @return query string. - */ - def getQueryString: String = { + * Returns the query string. + * + * @return query string. + */ + def getQueryString: String = queryString - } /** - * Returns the terms contained in a Lucene query string. - * - * @return the terms contained in a Lucene query. - */ - def getSingleTerms: Seq[String] = { + * Returns the terms contained in a Lucene query string. + * + * @return the terms contained in a Lucene query. + */ + def getSingleTerms: Seq[String] = queryString.split(space).toSeq - } } diff --git a/webapi/src/main/scala/org/knora/webapi/util/Base64UrlCheckDigit.scala b/webapi/src/main/scala/org/knora/webapi/util/Base64UrlCheckDigit.scala index 46c1a6f69c..36f68f901c 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/Base64UrlCheckDigit.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/Base64UrlCheckDigit.scala @@ -22,12 +22,11 @@ package org.knora.webapi.util import org.apache.commons.validator.routines.checkdigit.{CheckDigitException, ModulusCheckDigit} /** - * Calculates and validates check digits for base64url-encoded strings. - */ + * Calculates and validates check digits for base64url-encoded strings. + */ class Base64UrlCheckDigit extends ModulusCheckDigit(Base64UrlCheckDigit.Alphabet.length) { - override def weightedValue(charValue: Int, leftPos: Int, rightPos: Int): Int = { + override def weightedValue(charValue: Int, leftPos: Int, rightPos: Int): Int = charValue * rightPos - } override def toInt(character: Char, leftPos: Int, rightPos: Int): Int = { val charValue = Base64UrlCheckDigit.Alphabet.indexOf(character) @@ -49,8 +48,8 @@ class Base64UrlCheckDigit extends ModulusCheckDigit(Base64UrlCheckDigit.Alphabet } /** - * Contains constants for [[Base64UrlCheckDigit]]. - */ + * Contains constants for [[Base64UrlCheckDigit]]. + */ object Base64UrlCheckDigit { // The base64url alphabet (without padding) from RFC 4648, Table 2. val Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" diff --git a/webapi/src/main/scala/org/knora/webapi/util/Debug.scala b/webapi/src/main/scala/org/knora/webapi/util/Debug.scala index 346fd07e87..94ec5e07c8 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/Debug.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/Debug.scala @@ -1,13 +1,13 @@ package org.knora.webapi.util /** - * Contains methods useful for debugging - */ + * Contains methods useful for debugging + */ object Debug { /** - * prints out the classpath - */ + * prints out the classpath + */ def printClasspath(): Unit = { // debug classpath def urls(cl: ClassLoader): Array[java.net.URL] = Option(cl) match { @@ -21,10 +21,10 @@ object Debug { } /** - * Prints out the file paths for the resources - * - * @param resources a sequence of resource. - */ + * Prints out the file paths for the resources + * + * @param resources a sequence of resource. + */ def printResources(resources: Seq[String]): Unit = { println(s"printing resources: $resources") resources.foreach { res => diff --git a/webapi/src/main/scala/org/knora/webapi/util/FileUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/FileUtil.scala index 32477c821d..5cc4c7765c 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/FileUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/FileUtil.scala @@ -32,26 +32,25 @@ import scala.io.{BufferedSource, Codec, Source} import scala.util.{Failure, Success, Try} /** - * Functions for reading and writing files. - */ + * Functions for reading and writing files. + */ object FileUtil { /** - * Writes a string to a file. - * - * @param file the destination file. - * @param content the string to write. - */ - def writeTextFile(file: Path, content: String): Unit = { + * Writes a string to a file. + * + * @param file the destination file. + * @param content the string to write. + */ + def writeTextFile(file: Path, content: String): Unit = writeBinaryFile(file, content.getBytes(StandardCharsets.UTF_8)) - } /** - * Reads a text file into a string. - * - * @param file the source file. - * @return the contents of the file. - */ + * Reads a text file into a string. + * + * @param file the source file. + * @return the contents of the file. + */ def readTextFile(file: Path): String = { // TODO: provide apt error handling @@ -65,11 +64,11 @@ object FileUtil { } /** - * Reads a file from the classpath into a string. - * - * @param filename the name of the file. - * @return the contents of the file. - */ + * Reads a file from the classpath into a string. + * + * @param filename the name of the file. + * @return the contents of the file. + */ def readTextResource(filename: String): String = { // https://alvinalexander.com/scala/scala-exception-handling-try-catch-finally#toc_0 @@ -95,33 +94,31 @@ object FileUtil { } /** - * Writes a byte array to a file. - * - * @param file the destination file. - * @param content the binary data to write. - */ - def writeBinaryFile(file: Path, content: Array[Byte]): Unit = { + * Writes a byte array to a file. + * + * @param file the destination file. + * @param content the binary data to write. + */ + def writeBinaryFile(file: Path, content: Array[Byte]): Unit = Files.write(file, content) - } /** - * Generates a byte array representing a Zip file containing the specified data. The Zip file data is - * generated in memory only; no disk access is performed. - * - * @param contents a map of file names to byte arrays representing file contents. - * @return a byte array containing the Zip file data. - */ + * Generates a byte array representing a Zip file containing the specified data. The Zip file data is + * generated in memory only; no disk access is performed. + * + * @param contents a map of file names to byte arrays representing file contents. + * @return a byte array containing the Zip file data. + */ def createZipFileBytes(contents: Map[String, Array[Byte]]): Array[Byte] = { val byteArrayOutputStream = new ByteArrayOutputStream() val zipOutputStream = new ZipOutputStream(byteArrayOutputStream) val bytesTry = Try { - contents.foreach { - case (filename: String, content: Array[Byte]) => - val entry: ZipEntry = new ZipEntry(filename) - zipOutputStream.putNextEntry(entry) - zipOutputStream.write(content) - zipOutputStream.closeEntry() + contents.foreach { case (filename: String, content: Array[Byte]) => + val entry: ZipEntry = new ZipEntry(filename) + zipOutputStream.putNextEntry(entry) + zipOutputStream.write(content) + zipOutputStream.closeEntry() } } @@ -134,12 +131,12 @@ object FileUtil { } /** - * Saves data to a temporary file. - * - * @param settings Knora application settings. - * @param binaryData the binary file data to be saved. - * @return the location where the file has been written to. - */ + * Saves data to a temporary file. + * + * @param settings Knora application settings. + * @param binaryData the binary file data to be saved. + * @return the location where the file has been written to. + */ def saveFileToTmpLocation(settings: KnoraSettingsImpl, binaryData: Array[Byte]): Path = { val file = createTempFile(settings) Files.write(file, binaryData) @@ -147,12 +144,12 @@ object FileUtil { } /** - * Creates an empty file in the default temporary-file directory specified in Knora's application settings. - * - * @param settings Knora's application settings. - * @param fileExtension the extension to be used for the temporary file name, if any, - * @return the location where the file has been written to. - */ + * Creates an empty file in the default temporary-file directory specified in Knora's application settings. + * + * @param settings Knora's application settings. + * @param fileExtension the extension to be used for the temporary file name, if any, + * @return the location where the file has been written to. + */ def createTempFile(settings: KnoraSettingsImpl, fileExtension: Option[String] = None): Path = { val tempDataDir = Paths.get(settings.tmpDataDir) @@ -173,12 +170,12 @@ object FileUtil { } /** - * Deletes a temporary file. - * - * @param file the file to be deleted. - * @param log a logging adapter. - * @return `true` if the file was deleted by this method. - */ + * Deletes a temporary file. + * + * @param file the file to be deleted. + * @param log a logging adapter. + * @return `true` if the file was deleted by this method. + */ def deleteFileFromTmpLocation(file: Path, log: Logger): Boolean = { if (!Files.isWritable(file)) { diff --git a/webapi/src/main/scala/org/knora/webapi/util/JavaUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/JavaUtil.scala index 38f133113d..18ce9586ea 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/JavaUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/JavaUtil.scala @@ -24,31 +24,31 @@ import java.util.function.{BiFunction, Function} import scala.language.implicitConversions /** - * Utility functions for working with Java libraries. - */ + * Utility functions for working with Java libraries. + */ object JavaUtil { /** - * Converts a 1-argument Scala function into a Java [[Function]]. - * - * @param f the Scala function. - * @return a [[Function]] that calls the Scala function. - */ + * Converts a 1-argument Scala function into a Java [[Function]]. + * + * @param f the Scala function. + * @return a [[Function]] that calls the Scala function. + */ def function[A, B](f: A => B): Function[A, B] = (a: A) => f(a) /** - * Converts a 2-argument Scala function into a Java [[BiFunction]]. - * - * @param f the Scala function. - * @return a [[BiFunction]] that calls the Scala function. - */ + * Converts a 2-argument Scala function into a Java [[BiFunction]]. + * + * @param f the Scala function. + * @return a [[BiFunction]] that calls the Scala function. + */ def biFunction[A, B, C](f: (A, B) => C): BiFunction[A, B, C] = (a: A, b: B) => f(a, b) /** - * Wraps a Java `Optional` and converts it to a Scala [[Option]]. - */ + * Wraps a Java `Optional` and converts it to a Scala [[Option]]. + */ implicit class ConvertibleJavaOptional[T](val self: java.util.Optional[T]) extends AnyVal { def toOption: Option[T] = if (self.isPresent) Some(self.get) else None } diff --git a/webapi/src/main/scala/org/knora/webapi/util/SipiUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/SipiUtil.scala index 5b7654b15d..38a84b00c5 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/SipiUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/SipiUtil.scala @@ -24,17 +24,17 @@ import spray.json._ import scala.util.{Failure, Success, Try} /** - * Utility functions for communicating with Sipi. - */ + * Utility functions for communicating with Sipi. + */ object SipiUtil { /** - * Tries to extract an error message from a Sipi HTTP response. - * - * @param sipiResponseStr the response received from Sipi. - * @return the error message contained in the response, or the same string if it could not be parsed. - */ - def getSipiErrorMessage(sipiResponseStr: String): String = { + * Tries to extract an error message from a Sipi HTTP response. + * + * @param sipiResponseStr the response received from Sipi. + * @return the error message contained in the response, or the same string if it could not be parsed. + */ + def getSipiErrorMessage(sipiResponseStr: String): String = if (sipiResponseStr.isEmpty) { sipiResponseStr } else { @@ -53,5 +53,4 @@ object SipiUtil { case Failure(_) => sipiResponseStr } } - } } diff --git a/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala index a368cc217e..1133cdcede 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/cache/CacheUtil.scala @@ -26,37 +26,39 @@ import org.knora.webapi.exceptions.ApplicationCacheException import org.slf4j.LoggerFactory /** - * Maintains in-memory caches, and caches values, using Ehcache (http://ehcache.org/). Each cache is accessible - * by name from any function running in the same JVM. - */ + * Maintains in-memory caches, and caches values, using Ehcache (http://ehcache.org/). Each cache is accessible + * by name from any function running in the same JVM. + */ object CacheUtil { val log: Logger = Logger(LoggerFactory.getLogger("org.knora.webapi.util.cache")) /** - * Represents the configuration of a Knora application cache. - * - * @param cacheName the name of the cache. - * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit). - * @param overflowToDisk whether to use the disk store. - * @param eternal whether the elements in the cache are eternal, i.e. never expire. - * @param timeToLiveSeconds the default amount of time to live for an element from its creation date. - * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date. - */ - case class KnoraCacheConfig(cacheName: String, - maxElementsInMemory: Int, - overflowToDisk: Boolean, - eternal: Boolean, - timeToLiveSeconds: Int, - timeToIdleSeconds: Int) + * Represents the configuration of a Knora application cache. + * + * @param cacheName the name of the cache. + * @param maxElementsInMemory the maximum number of elements in memory, before they are evicted (0 == no limit). + * @param overflowToDisk whether to use the disk store. + * @param eternal whether the elements in the cache are eternal, i.e. never expire. + * @param timeToLiveSeconds the default amount of time to live for an element from its creation date. + * @param timeToIdleSeconds the default amount of time to live for an element from its last accessed or modified date. + */ + case class KnoraCacheConfig( + cacheName: String, + maxElementsInMemory: Int, + overflowToDisk: Boolean, + eternal: Boolean, + timeToLiveSeconds: Int, + timeToIdleSeconds: Int + ) /** - * Creates application caches. - * - * @param cacheConfigs Maps containing the keys `cacheName`, `maxElementsInMemory`, - * `overflowToDisk`, `eternal`, `timeToLiveSeconds`, and `timeToIdleSeconds`, - * representing configuration options for Ehcache caches. - */ + * Creates application caches. + * + * @param cacheConfigs Maps containing the keys `cacheName`, `maxElementsInMemory`, + * `overflowToDisk`, `eternal`, `timeToLiveSeconds`, and `timeToIdleSeconds`, + * representing configuration options for Ehcache caches. + */ def createCaches(cacheConfigs: Seq[KnoraCacheConfig]): Unit = { val cacheManager = CacheManager.getInstance() cacheConfigs.foreach { cacheConfig => @@ -75,8 +77,8 @@ object CacheUtil { } /** - * Removes all caches. - */ + * Removes all caches. + */ def removeAllCaches(): Unit = { val cacheManager = CacheManager.getInstance() cacheManager.removeAllCaches() @@ -84,10 +86,10 @@ object CacheUtil { } /** - * Clears a cache. - * - * @param cacheName the name of the cache to be cleared. - */ + * Clears a cache. + * + * @param cacheName the name of the cache to be cleared. + */ def clearCache(cacheName: String): Unit = { val cacheManager = CacheManager.getInstance() Option(cacheManager.getCache(cacheName)) match { @@ -100,13 +102,13 @@ object CacheUtil { } /** - * Adds a value to a cache. - * - * @param cacheName the name of the cache. - * @param key the cache key as a [[String]]. - * @param value the value we want to cache. - * @tparam V the type of the value we want to cache. - */ + * Adds a value to a cache. + * + * @param cacheName the name of the cache. + * @param key the cache key as a [[String]]. + * @param value the value we want to cache. + * @tparam V the type of the value we want to cache. + */ def put[V](cacheName: String, key: String, value: V): Unit = { val cacheManager = CacheManager.getInstance() val cacheOption = Option(cacheManager.getCache(cacheName)) @@ -120,13 +122,13 @@ object CacheUtil { } /** - * Tries to ge a value from a cache. - * - * @param cacheName the name of the cache. - * @param key the cache key as a [[String]]. - * @tparam V the type of the item we try to get from the cache. - * @return an [[Option[V]]]. - */ + * Tries to ge a value from a cache. + * + * @param cacheName the name of the cache. + * @param key the cache key as a [[String]]. + * @tparam V the type of the item we try to get from the cache. + * @return an [[Option[V]]]. + */ def get[V](cacheName: String, key: String): Option[V] = { val cacheManager = CacheManager.getInstance() val cacheOption = Option(cacheManager.getCache(cacheName)) @@ -143,17 +145,18 @@ object CacheUtil { } case None => throw ApplicationCacheException( - s"Can't find application cache '$cacheName'. Please check configuration of 'app.caches' in 'application.conf'") + s"Can't find application cache '$cacheName'. Please check configuration of 'app.caches' in 'application.conf'" + ) } } /** - * Tries to remove a value from a cache. - * - * @param cacheName the name of the cache. - * @param key the cache key as a [[String]] - */ + * Tries to remove a value from a cache. + * + * @param cacheName the name of the cache. + * @param key the cache key as a [[String]] + */ def remove(cacheName: String, key: String): () = { val cacheManager = CacheManager.getInstance() val cacheOption = Option(cacheManager.getCache(cacheName)) @@ -169,29 +172,23 @@ object CacheUtil { class LoggingCacheEventListener(log: Logger) extends CacheEventListener { - def notifyElementRemoved(cache: Ehcache, element: Element): Unit = { + def notifyElementRemoved(cache: Ehcache, element: Element): Unit = log.debug("notifyElementRemoved " + cache.getName + element.toString) - } - def notifyElementPut(cache: Ehcache, element: Element): Unit = { + def notifyElementPut(cache: Ehcache, element: Element): Unit = log.debug("notifyElementPut " + cache.getName + element.toString) - } - def notifyElementUpdated(cache: Ehcache, element: Element): Unit = { + def notifyElementUpdated(cache: Ehcache, element: Element): Unit = log.debug("notifyElementUpdated " + cache.getName + element.toString) - } - def notifyElementExpired(cache: Ehcache, element: Element): Unit = { + def notifyElementExpired(cache: Ehcache, element: Element): Unit = log.debug("notifyElementExpired " + cache.getName + element.toString) - } - def notifyElementEvicted(cache: Ehcache, element: Element): Unit = { + def notifyElementEvicted(cache: Ehcache, element: Element): Unit = log.debug("notifyElementEvicted " + cache.getName + element.toString) - } - def notifyRemoveAll(cache: Ehcache): Unit = { + def notifyRemoveAll(cache: Ehcache): Unit = log.debug("notifyRemoveAll " + cache.getName) - } def dispose(): Unit = {} diff --git a/webapi/src/test/scala/org/knora/webapi/AsyncCoreSpec.scala b/webapi/src/test/scala/org/knora/webapi/AsyncCoreSpec.scala index 592c2f941d..98d091c5b2 100644 --- a/webapi/src/test/scala/org/knora/webapi/AsyncCoreSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/AsyncCoreSpec.scala @@ -86,7 +86,7 @@ abstract class AsyncCoreSpec(_system: ActorSystem) /* needed by the core trait */ implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) override implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) @@ -104,7 +104,7 @@ abstract class AsyncCoreSpec(_system: ActorSystem) // The main application actor forwards messages to the responder manager and the store manager. val responderManager: ActorRef = appActor - val storeManager: ActorRef = appActor + val storeManager: ActorRef = appActor val responderData: ResponderData = ResponderData( system = system, diff --git a/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala b/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala index 9f623938c6..1abf3ea917 100644 --- a/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala @@ -108,8 +108,8 @@ abstract class CoreSpec(_system: ActorSystem) ) /* needed by the core trait */ - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // can be overridden in individual spec @@ -126,7 +126,7 @@ abstract class CoreSpec(_system: ActorSystem) // The main application actor forwards messages to the responder manager and the store manager. val responderManager: ActorRef = appActor - val storeManager: ActorRef = appActor + val storeManager: ActorRef = appActor val responderData: ResponderData = ResponderData( system = system, diff --git a/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala index bcc5f68af7..8d73b3fed0 100644 --- a/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala @@ -82,9 +82,9 @@ class E2ESpec(_system: ActorSystem) /* needed by the core trait */ - implicit lazy val system: ActorSystem = _system - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val system: ActorSystem = _system + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // can be overridden in individual spec @@ -148,7 +148,7 @@ class E2ESpec(_system: ActorSystem) protected def responseToJsonLDDocument(httpResponse: HttpResponse): JsonLDDocument = { val responseBodyFuture: Future[String] = httpResponse.entity.toStrict(10.seconds).map(_.data.decodeString("UTF-8")) - val responseBodyStr = Await.result(responseBodyFuture, 10.seconds) + val responseBodyStr = Await.result(responseBodyFuture, 10.seconds) JsonLDUtil.parseJsonLD(responseBodyStr) } @@ -158,7 +158,7 @@ class E2ESpec(_system: ActorSystem) } protected def doGetRequest(urlPath: String): String = { - val request = Get(s"$baseApiUrl$urlPath") + val request = Get(s"$baseApiUrl$urlPath") val response: HttpResponse = singleAwaitingRequest(request) responseToString(response) } @@ -198,7 +198,7 @@ class E2ESpec(_system: ActorSystem) // Per default only read access is allowed in the bazel sandbox. // This workaround allows to save test output. val testOutputDir: Path = Paths.get(sys.env("TEST_UNDECLARED_OUTPUTS_DIR")) - val newOutputFile = testOutputDir.resolve(file) + val newOutputFile = testOutputDir.resolve(file) Files.createDirectories(newOutputFile.getParent) FileUtil.writeTextFile( newOutputFile, diff --git a/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala b/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala index a0910a0c8d..c463f93288 100644 --- a/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/ITKnoraFakeSpec.scala @@ -75,9 +75,9 @@ class ITKnoraFakeSpec(_system: ActorSystem) this(ActorSystem("IntegrationTests", TestContainersAll.PortConfig.withFallback(ITKnoraFakeSpec.defaultConfig))) /* needed by the core trait */ - implicit lazy val system: ActorSystem = _system - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val system: ActorSystem = _system + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) /* Needs to be initialized before any responders */ @@ -86,7 +86,7 @@ class ITKnoraFakeSpec(_system: ActorSystem) val log: LoggingAdapter = akka.event.Logging(system, this.getClass) - protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl + protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl protected val baseInternalSipiUrl: String = settings.internalSipiBaseUrl protected val baseExternalSipiUrl: String = settings.externalSipiBaseUrl @@ -108,7 +108,7 @@ class ITKnoraFakeSpec(_system: ActorSystem) } protected def getResponseString(request: HttpRequest): String = { - val response = singleAwaitingRequest(request) + val response = singleAwaitingRequest(request) val responseBodyStr = Await.result(Unmarshal(response.entity).to[String], 6.seconds) assert(response.status === StatusCodes.OK, s",\n REQUEST: $request,\n RESPONSE: $responseBodyStr") responseBodyStr diff --git a/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala index ac5cbc22f1..d42a9c3feb 100644 --- a/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala @@ -85,9 +85,9 @@ class ITKnoraLiveSpec(_system: ActorSystem) this(ActorSystem("IntegrationTests", TestContainersAll.PortConfig.withFallback(ITKnoraLiveSpec.defaultConfig))) /* needed by the core trait (represents the KnoraTestCore trait)*/ - implicit lazy val system: ActorSystem = _system - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit lazy val system: ActorSystem = _system + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) // can be overridden in individual spec @@ -102,7 +102,7 @@ class ITKnoraLiveSpec(_system: ActorSystem) lazy val appActor: ActorRef = system.actorOf(Props(new ApplicationActor with LiveManagers), name = APPLICATION_MANAGER_ACTOR_NAME) - protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl + protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl protected val baseInternalSipiUrl: String = settings.internalSipiBaseUrl protected val baseExternalSipiUrl: String = settings.externalSipiBaseUrl @@ -130,7 +130,7 @@ class ITKnoraLiveSpec(_system: ActorSystem) protected def checkIfSipiIsRunning(): Unit = { // This requires that (1) fileserver.docroot is set in Sipi's config file and (2) it contains a file test.html. - val request = Get(baseInternalSipiUrl + "/server/test.html") + val request = Get(baseInternalSipiUrl + "/server/test.html") val response = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, s"Sipi is probably not running: ${response.status}") if (response.status.isSuccess()) logger.info("Sipi is running.") diff --git a/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala b/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala index cc4849e775..0c50f96ac5 100644 --- a/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala @@ -98,22 +98,23 @@ abstract class IntegrationSpec(_config: Config) implicit val ctx: MessageDispatcher = system.dispatchers.lookup(KnoraDispatchers.KnoraBlockingDispatcher) val checkTriplestore: ZIO[Any, Throwable, Unit] = for { checkResult <- ZIO.fromTry( - Try( - Await - .result(actorRef ? CheckTriplestoreRequest(), 1.second.asScala) - .asInstanceOf[CheckTriplestoreResponse] - ) - ) - - value <- if (checkResult.triplestoreStatus == TriplestoreStatus.ServiceAvailable) { - ZIO.effectTotal(logger.info("... triplestore is ready.")) - } else { - ZIO.fail( - new Exception( - s"Triplestore not ready: ${checkResult.triplestoreStatus}" - ) - ) - } + Try( + Await + .result(actorRef ? CheckTriplestoreRequest(), 1.second.asScala) + .asInstanceOf[CheckTriplestoreResponse] + ) + ) + + value <- + if (checkResult.triplestoreStatus == TriplestoreStatus.ServiceAvailable) { + ZIO.effectTotal(logger.info("... triplestore is ready.")) + } else { + ZIO.fail( + new Exception( + s"Triplestore not ready: ${checkResult.triplestoreStatus}" + ) + ) + } } yield value implicit val rt: Runtime[Clock with Console] = Runtime.default diff --git a/webapi/src/test/scala/org/knora/webapi/KnoraFakeCore.scala b/webapi/src/test/scala/org/knora/webapi/KnoraFakeCore.scala index 7aa0c72916..c2a746df1b 100644 --- a/webapi/src/test/scala/org/knora/webapi/KnoraFakeCore.scala +++ b/webapi/src/test/scala/org/knora/webapi/KnoraFakeCore.scala @@ -30,8 +30,8 @@ import org.knora.webapi.settings.APPLICATION_MANAGER_ACTOR_NAME import scala.concurrent.duration.FiniteDuration /** - * A fake Knora service that Sipi can use to get file permissions. - */ + * A fake Knora service that Sipi can use to get file permissions. + */ trait KnoraFakeCore { this: Core => @@ -40,13 +40,13 @@ trait KnoraFakeCore { system.actorOf(Props(new ApplicationActor with LiveManagers), name = APPLICATION_MANAGER_ACTOR_NAME) /** - * Timeout definition (need to be high enough to allow reloading of data so that checkActorSystem doesn't timeout) - */ + * Timeout definition (need to be high enough to allow reloading of data so that checkActorSystem doesn't timeout) + */ implicit private val timeout: FiniteDuration = settings.defaultTimeout /** - * Faked `webapi` routes - */ + * Faked `webapi` routes + */ private val apiRoutes = { path("admin" / "files" / Segments(2)) { projectIDAndFile => get { @@ -63,19 +63,19 @@ trait KnoraFakeCore { } /** - * Starts the Faked Knora API server. - */ + * Starts the Faked Knora API server. + */ def startService(): Unit = { Http().newServerAt(settings.internalKnoraApiHost, settings.internalKnoraApiPort).bindFlow(Route.toFlow(apiRoutes)) println( - s"Faked Knora API Server started at http://${settings.internalKnoraApiHost}:${settings.internalKnoraApiPort}.") + s"Faked Knora API Server started at http://${settings.internalKnoraApiHost}:${settings.internalKnoraApiPort}." + ) } /** - * Stops Knora. - */ - def stopService(): Unit = { + * Stops Knora. + */ + def stopService(): Unit = system.terminate() - } } diff --git a/webapi/src/test/scala/org/knora/webapi/ManagersWithMockedSipi.scala b/webapi/src/test/scala/org/knora/webapi/ManagersWithMockedSipi.scala index 664afd4f0a..1891ddffd5 100644 --- a/webapi/src/test/scala/org/knora/webapi/ManagersWithMockedSipi.scala +++ b/webapi/src/test/scala/org/knora/webapi/ManagersWithMockedSipi.scala @@ -31,19 +31,20 @@ import org.knora.webapi.store.cacheservice.settings.CacheServiceSettings import org.knora.webapi.store.iiif.MockSipiConnector /** - * Mixin trait for running the application with mocked Sipi - */ + * Mixin trait for running the application with mocked Sipi + */ trait ManagersWithMockedSipi extends Managers { this: Actor => lazy val mockStoreConnectors: Map[String, ActorRef] = Map( - SipiConnectorActorName -> context.actorOf(Props(new MockSipiConnector))) + SipiConnectorActorName -> context.actorOf(Props(new MockSipiConnector)) + ) lazy val mockResponders: Map[String, ActorRef] = Map.empty[String, ActorRef] lazy val storeManager: ActorRef = context.actorOf( Props( new MockableStoreManager(mockStoreConnectors = mockStoreConnectors, appActor = self, cs = CacheServiceInMemImpl) - with LiveActorMaker + with LiveActorMaker ), name = StoreManagerActorName ) @@ -53,11 +54,14 @@ trait ManagersWithMockedSipi extends Managers { new MockableResponderManager( mockRespondersOrStoreConnectors = mockResponders, appActor = self, - responderData = ResponderData(system, - self, - knoraSettings = KnoraSettings(system), - cacheServiceSettings = new CacheServiceSettings(system.settings.config)) - )), + responderData = ResponderData( + system, + self, + knoraSettings = KnoraSettings(system), + cacheServiceSettings = new CacheServiceSettings(system.settings.config) + ) + ) + ), name = RESPONDER_MANAGER_ACTOR_NAME ) } diff --git a/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala index 695d9c6688..c023ae6453 100644 --- a/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala @@ -100,7 +100,7 @@ class R2RSpec // The main application actor forwards messages to the responder manager and the store manager. val responderManager: ActorRef = appActor - val storeManager: ActorRef = appActor + val storeManager: ActorRef = appActor val routeData: KnoraRouteData = KnoraRouteData( system = system, @@ -130,7 +130,7 @@ class R2RSpec protected def responseToJsonLDDocument(httpResponse: HttpResponse): JsonLDDocument = { val responseBodyFuture: Future[String] = httpResponse.entity.toStrict(5.seconds).map(_.data.decodeString("UTF-8")) - val responseBodyStr = Await.result(responseBodyFuture, 5.seconds) + val responseBodyStr = Await.result(responseBodyFuture, 5.seconds) JsonLDUtil.parseJsonLD(responseBodyStr) } @@ -172,7 +172,7 @@ class R2RSpec // Per default only read access is allowed in the bazel sandbox. // This workaround allows to save test output. val testOutputDir: Path = Paths.get(sys.env("TEST_UNDECLARED_OUTPUTS_DIR")) - val newOutputFile = testOutputDir.resolve(file) + val newOutputFile = testOutputDir.resolve(file) Files.createDirectories(newOutputFile.getParent) FileUtil.writeTextFile( newOutputFile, diff --git a/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala b/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala index 1a10ac52e2..af43e1e4ac 100644 --- a/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestContainerFuseki.scala @@ -31,7 +31,7 @@ import scala.jdk.CollectionConverters._ object TestContainerFuseki { val FusekiImageName: DockerImageName = DockerImageName.parse("bazel/docker/knora-jena-fuseki:image") - val FusekiContainer = new GenericContainer(FusekiImageName) + val FusekiContainer = new GenericContainer(FusekiImageName) FusekiContainer.withExposedPorts(3030) FusekiContainer.withEnv("ADMIN_PASSWORD", "test") diff --git a/webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala b/webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala index bcef49339c..e927575b66 100644 --- a/webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestContainerRedis.scala @@ -31,7 +31,7 @@ import scala.jdk.CollectionConverters._ object TestContainerRedis { val RedisImageName: DockerImageName = DockerImageName.parse("redis:5") - val RedisContainer = new GenericContainer(RedisImageName) + val RedisContainer = new GenericContainer(RedisImageName) RedisContainer.withExposedPorts(6379) RedisContainer.start() diff --git a/webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala b/webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala index 3fbb8ec1a5..4ff750d8a0 100644 --- a/webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestContainersAll.scala @@ -40,14 +40,14 @@ object TestContainersAll { .getOrElse(throw new UnknownHostException("No suitable network interface found")) val FusekiImageName: DockerImageName = DockerImageName.parse("bazel/docker/knora-jena-fuseki:image") - val FusekiContainer = new GenericContainer(FusekiImageName) + val FusekiContainer = new GenericContainer(FusekiImageName) FusekiContainer.withExposedPorts(3030) FusekiContainer.withEnv("ADMIN_PASSWORD", "test") FusekiContainer.withEnv("JVM_ARGS", "-Xmx3G") FusekiContainer.start() val SipiImageName: DockerImageName = DockerImageName.parse("bazel/docker/knora-sipi:image") - val SipiContainer = new GenericContainer(SipiImageName) + val SipiContainer = new GenericContainer(SipiImageName) SipiContainer.withExposedPorts(1024) SipiContainer.withEnv("SIPI_EXTERNAL_PROTOCOL", "http") SipiContainer.withEnv("SIPI_EXTERNAL_HOSTNAME", "0.0.0.0") @@ -64,7 +64,7 @@ object TestContainersAll { // Container needs to be started to get the random IP val sipiIp: String = SipiContainer.getHost - val sipiPort: Int = SipiContainer.getFirstMappedPort + val sipiPort: Int = SipiContainer.getFirstMappedPort // The new default is the inmem cache implementation, so no need // for a container @@ -76,8 +76,8 @@ object TestContainersAll { private val portMap = Map( "app.triplestore.fuseki.port" -> FusekiContainer.getFirstMappedPort, - "app.sipi.internal-host" -> sipiIp, - "app.sipi.internal-port" -> sipiPort + "app.sipi.internal-host" -> sipiIp, + "app.sipi.internal-port" -> sipiPort // "app.cache-service.redis.port" -> RedisContainer.getFirstMappedPort ).asJava diff --git a/webapi/src/test/scala/org/knora/webapi/TestProbeMaker.scala b/webapi/src/test/scala/org/knora/webapi/TestProbeMaker.scala index 9cd6ff936d..2aa771b36a 100644 --- a/webapi/src/test/scala/org/knora/webapi/TestProbeMaker.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestProbeMaker.scala @@ -24,9 +24,9 @@ import akka.testkit.TestProbe import org.knora.webapi.core.ActorMaker /** - * This trait is part of the cake pattern used in the creation of actors. This trait provides an implementation of the - * makeActor method that creates an actor as a [[TestProbe]], which can than be used in testing. - */ + * This trait is part of the cake pattern used in the creation of actors. This trait provides an implementation of the + * makeActor method that creates an actor as a [[TestProbe]], which can than be used in testing. + */ trait TestProbeMaker extends ActorMaker { this: Actor with ActorLogging => @@ -36,12 +36,15 @@ trait TestProbeMaker extends ActorMaker { val probe = new TestProbe(context.system) probes(name) = probe log.debug(s"created test-probe named: $name") - context.actorOf(Props(new Actor { - def receive = { - case msg => { - probe.ref forward msg + context.actorOf( + Props(new Actor { + def receive = { + case msg => { + probe.ref forward msg + } } - } - }), name) + }), + name + ) } } diff --git a/webapi/src/test/scala/org/knora/webapi/UnitSpec.scala b/webapi/src/test/scala/org/knora/webapi/UnitSpec.scala index c8c66ba929..5e5d3ec325 100644 --- a/webapi/src/test/scala/org/knora/webapi/UnitSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/UnitSpec.scala @@ -45,7 +45,7 @@ object UnitSpec { .dropWhile(_ matches "(java.lang.Thread|.*UnitSpec.?$)") val reduced = s.lastIndexWhere(_ == clazz.getName) match { case -1 => s - case z => s drop (z + 1) + case z => s drop (z + 1) } reduced.head.replaceFirst(""".*\.""", "").replaceAll("[^a-zA-Z_0-9]", "_") } diff --git a/webapi/src/test/scala/org/knora/webapi/contributors/GenerateContributorsFileSpec.scala b/webapi/src/test/scala/org/knora/webapi/contributors/GenerateContributorsFileSpec.scala index 5413372f4d..4e21e1bb27 100644 --- a/webapi/src/test/scala/org/knora/webapi/contributors/GenerateContributorsFileSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/contributors/GenerateContributorsFileSpec.scala @@ -25,8 +25,8 @@ import org.knora.webapi.CoreSpec import org.knora.webapi.util.FileUtil /** - * Tests [[GenerateContributorsFile]]. - */ + * Tests [[GenerateContributorsFile]]. + */ class GenerateContributorsFileSpec extends CoreSpec() { "The GenerateContributorsFile utility" should { "generate a contributors file" ignore { // GitHub returns an HTTP 403 (Forbidden) error when this is run on Travis without a GitHub API key. diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala index 8df6c7313b..97cd3f8467 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala @@ -87,7 +87,7 @@ class CORSSupportE2ESpec extends E2ESpec(CORSSupportE2ESpec.config) { } "send `Access-Control-Allow-Origin` header when the api endpoint route is NOT found " in { - val request = Get(baseApiUrl + "/NotFound") ~> Origin(exampleOrigin) + val request = Get(baseApiUrl + "/NotFound") ~> Origin(exampleOrigin) val response = singleAwaitingRequest(request) response.status should equal(StatusCodes.NotFound) response.headers should contain allElementsOf Seq( diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/ClientTestDataCollector.scala b/webapi/src/test/scala/org/knora/webapi/e2e/ClientTestDataCollector.scala index c69c694a71..e66622251e 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/ClientTestDataCollector.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/ClientTestDataCollector.scala @@ -24,10 +24,10 @@ import org.knora.webapi.settings.KnoraSettingsImpl import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig} /** - * Collects E2E test requests and responses for use as client test data. - * - * @param settings the application settings. - */ + * Collects E2E test requests and responses for use as client test data. + * + * @param settings the application settings. + */ class ClientTestDataCollector(settings: KnoraSettingsImpl) { private val redisHashName: String = "client-test-data" @@ -36,20 +36,22 @@ class ClientTestDataCollector(settings: KnoraSettingsImpl) { // Yes. Make a connection pool for connecting to Redis. val redisHost: String = settings.clientTestDataRedisHost.getOrElse( - throw TestConfigurationException(s"No Redis host configured for client test data")) + throw TestConfigurationException(s"No Redis host configured for client test data") + ) val redisPort: Int = settings.clientTestDataRedisPort.getOrElse( - throw TestConfigurationException(s"No Redis port configured for client test data")) + throw TestConfigurationException(s"No Redis port configured for client test data") + ) Some(new JedisPool(new JedisPoolConfig(), redisHost, redisPort, 30999)) } else { None } /** - * Stores a client test data file. - * - * @param fileContent the content of the file to be stored. - */ - def addFile(fileContent: TestDataFileContent): Unit = { + * Stores a client test data file. + * + * @param fileContent the content of the file to be stored. + */ + def addFile(fileContent: TestDataFileContent): Unit = // Are we configured to collect client test data? maybeJedisPool match { case Some(jedisPool) => @@ -67,27 +69,25 @@ class ClientTestDataCollector(settings: KnoraSettingsImpl) { // No. Do nothing. () } - } } /** - * Represents a file containing generated client API test data. - * - * @param filePath the file path in which the test data should be saved. - * @param text the source code. - */ + * Represents a file containing generated client API test data. + * + * @param filePath the file path in which the test data should be saved. + * @param text the source code. + */ case class TestDataFileContent(filePath: TestDataFilePath, text: String) /** - * Represents the filesystem path of a file containing generated test data. - * - * @param directoryPath the path of the directory containing the file, - * relative to the root directory of the source tree. - * @param filename the filename, without the file extension. - * @param fileExtension the file extension. - */ + * Represents the filesystem path of a file containing generated test data. + * + * @param directoryPath the path of the directory containing the file, + * relative to the root directory of the source tree. + * @param filename the filename, without the file extension. + * @param fileExtension the file extension. + */ case class TestDataFilePath(directoryPath: Seq[String], filename: String, fileExtension: String) { - override def toString: String = { + override def toString: String = (directoryPath :+ filename + "." + fileExtension).mkString("/") - } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/ExceptionHandlerR2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/ExceptionHandlerR2RSpec.scala index 0aa1614fc9..846734a356 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/ExceptionHandlerR2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/ExceptionHandlerR2RSpec.scala @@ -23,8 +23,8 @@ import org.knora.webapi.exceptions._ import org.knora.webapi.http.handler /** - * Route (R2R) test specification for testing exception handling. - */ + * Route (R2R) test specification for testing exception handling. + */ class ExceptionHandlerR2RSpec extends R2RSpec { private val nfe = path(("v1" | "v2" | "admin") / "nfe") { @@ -93,7 +93,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.NotFound) responseAs[String] should be( - "{\"status\":9,\"error\":\"org.knora.webapi.exceptions.NotFoundException: not found\"}") + "{\"status\":9,\"error\":\"org.knora.webapi.exceptions.NotFoundException: not found\"}" + ) } Get("/v2/nfe") ~> route ~> check { @@ -101,7 +102,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.NotFound) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.NotFoundException: not found\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.NotFoundException: not found\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/nfe") ~> route ~> check { @@ -118,7 +120,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Forbidden) responseAs[String] should be( - "{\"status\":3,\"error\":\"org.knora.webapi.exceptions.ForbiddenException: forbidden\",\"access\":\"NO_ACCESS\"}") + "{\"status\":3,\"error\":\"org.knora.webapi.exceptions.ForbiddenException: forbidden\",\"access\":\"NO_ACCESS\"}" + ) } Get("/v2/fe") ~> route ~> check { @@ -126,7 +129,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Forbidden) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.ForbiddenException: forbidden\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.ForbiddenException: forbidden\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/fe") ~> route ~> check { @@ -143,7 +147,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"status\":28,\"error\":\"org.knora.webapi.exceptions.DuplicateValueException: duplicate value\"}") + "{\"status\":28,\"error\":\"org.knora.webapi.exceptions.DuplicateValueException: duplicate value\"}" + ) } Get("/v2/dve") ~> route ~> check { @@ -151,7 +156,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.DuplicateValueException: duplicate value\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.DuplicateValueException: duplicate value\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/dve") ~> route ~> check { @@ -159,7 +165,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"error\":\"org.knora.webapi.exceptions.DuplicateValueException: duplicate value\"}") + "{\"error\":\"org.knora.webapi.exceptions.DuplicateValueException: duplicate value\"}" + ) } } @@ -169,7 +176,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"status\":29,\"error\":\"org.knora.webapi.exceptions.OntologyConstraintException: ontology constraint\"}") + "{\"status\":29,\"error\":\"org.knora.webapi.exceptions.OntologyConstraintException: ontology constraint\"}" + ) } Get("/v2/oce") ~> route ~> check { @@ -177,7 +185,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.OntologyConstraintException: ontology constraint\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.OntologyConstraintException: ontology constraint\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/oce") ~> route ~> check { @@ -185,7 +194,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"error\":\"org.knora.webapi.exceptions.OntologyConstraintException: ontology constraint\"}") + "{\"error\":\"org.knora.webapi.exceptions.OntologyConstraintException: ontology constraint\"}" + ) } } @@ -195,7 +205,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Conflict) responseAs[String] should be( - "{\"status\":27,\"error\":\"org.knora.webapi.exceptions.EditConflictException: edit conflict\"}") + "{\"status\":27,\"error\":\"org.knora.webapi.exceptions.EditConflictException: edit conflict\"}" + ) } Get("/v2/ece") ~> route ~> check { @@ -203,7 +214,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Conflict) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.EditConflictException: edit conflict\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.EditConflictException: edit conflict\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/ece") ~> route ~> check { @@ -220,7 +232,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"status\":11,\"error\":\"org.knora.webapi.exceptions.GravsearchException: sparql search\"}") + "{\"status\":11,\"error\":\"org.knora.webapi.exceptions.GravsearchException: sparql search\"}" + ) } Get("/v2/sse") ~> route ~> check { @@ -228,7 +241,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.BadRequest) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.GravsearchException: sparql search\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.GravsearchException: sparql search\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/sse") ~> route ~> check { @@ -245,7 +259,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Conflict) responseAs[String] should be( - "{\"status\":27,\"error\":\"org.knora.webapi.exceptions.UpdateNotPerformedException: update not performed\"}") + "{\"status\":27,\"error\":\"org.knora.webapi.exceptions.UpdateNotPerformedException: update not performed\"}" + ) } Get("/v2/unpe") ~> route ~> check { @@ -253,7 +268,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Conflict) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.UpdateNotPerformedException: update not performed\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.UpdateNotPerformedException: update not performed\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/unpe") ~> route ~> check { @@ -261,7 +277,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.Conflict) responseAs[String] should be( - "{\"error\":\"org.knora.webapi.exceptions.UpdateNotPerformedException: update not performed\"}") + "{\"error\":\"org.knora.webapi.exceptions.UpdateNotPerformedException: update not performed\"}" + ) } } @@ -271,7 +288,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.InternalServerError) responseAs[String] should be( - "{\"status\":4,\"error\":\"org.knora.webapi.exceptions.AuthenticationException: authentication exception\"}") + "{\"status\":4,\"error\":\"org.knora.webapi.exceptions.AuthenticationException: authentication exception\"}" + ) } Get("/v2/ae") ~> route ~> check { @@ -279,7 +297,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.InternalServerError) responseAs[String] should be( - "{\"knora-api:error\":\"org.knora.webapi.exceptions.AuthenticationException: authentication exception\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}") + "{\"knora-api:error\":\"org.knora.webapi.exceptions.AuthenticationException: authentication exception\",\"@context\":{\"knora-api\":\"http://api.knora.org/ontology/knora-api/v2#\"}}" + ) } Get("/admin/ae") ~> route ~> check { @@ -287,7 +306,8 @@ class ExceptionHandlerR2RSpec extends R2RSpec { status should be(StatusCodes.InternalServerError) responseAs[String] should be( - "{\"error\":\"org.knora.webapi.exceptions.AuthenticationException: authentication exception\"}") + "{\"error\":\"org.knora.webapi.exceptions.AuthenticationException: authentication exception\"}" + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/FeatureToggleR2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/FeatureToggleR2RSpec.scala index 791989fa65..aa036be407 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/FeatureToggleR2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/FeatureToggleR2RSpec.scala @@ -34,62 +34,62 @@ import org.knora.webapi.routing.{KnoraRoute, KnoraRouteData, KnoraRouteFactory} import scala.concurrent.ExecutionContextExecutor /** - * Tests feature toggles that replace implementations of API routes. - */ + * Tests feature toggles that replace implementations of API routes. + */ class FeatureToggleR2RSpec extends R2RSpec { // Some feature toggles for testing. override def testConfigSource: String = """app { - | feature-toggles { - | FeatureToggleR2RSpec-new-foo { - | description = "Replace the old foo routes with new ones." - | - | available-versions = [ 1, 2 ] - | default-version = 1 - | enabled-by-default = yes - | override-allowed = yes - | - | developer-emails = [ - | "Benjamin Geer " - | ] - | } - | - | FeatureToggleR2RSpec-new-bar { - | description = "Replace the old bar routes with new ones." - | - | available-versions = [ 1 ] - | default-version = 1 - | enabled-by-default = yes - | override-allowed = yes - | - | developer-emails = [ - | "Benjamin Geer " - | ] - | } - | - | FeatureToggleR2RSpec-new-baz { - | description = "Replace the old baz routes with new ones." - | - | available-versions = [ 1 ] - | default-version = 1 - | enabled-by-default = no - | override-allowed = no - | - | developer-emails = [ - | "Benjamin Geer " - | ] - | } - | } - |} + | feature-toggles { + | FeatureToggleR2RSpec-new-foo { + | description = "Replace the old foo routes with new ones." + | + | available-versions = [ 1, 2 ] + | default-version = 1 + | enabled-by-default = yes + | override-allowed = yes + | + | developer-emails = [ + | "Benjamin Geer " + | ] + | } + | + | FeatureToggleR2RSpec-new-bar { + | description = "Replace the old bar routes with new ones." + | + | available-versions = [ 1 ] + | default-version = 1 + | enabled-by-default = yes + | override-allowed = yes + | + | developer-emails = [ + | "Benjamin Geer " + | ] + | } + | + | FeatureToggleR2RSpec-new-baz { + | description = "Replace the old baz routes with new ones." + | + | available-versions = [ 1 ] + | default-version = 1 + | enabled-by-default = no + | override-allowed = no + | + | developer-emails = [ + | "Benjamin Geer " + | ] + | } + | } + |} """.stripMargin /** - * A test implementation of a route feature that handles HTTP GET requests. - * - * @param pathStr the route path. - * @param featureName the name of the feature. - * @param routeData a [[KnoraRouteData]] providing access to the application. - */ + * A test implementation of a route feature that handles HTTP GET requests. + * + * @param pathStr the route path. + * @param featureName the name of the feature. + * @param routeData a [[KnoraRouteData]] providing access to the application. + */ class TestRouteFeature(pathStr: String, featureName: String, routeData: KnoraRouteData) extends KnoraRoute(routeData) with Feature { @@ -114,8 +114,8 @@ class FeatureToggleR2RSpec extends R2RSpec { } /** - * A feature factory that constructs implementations of [[FooRoute]]. - */ + * A feature factory that constructs implementations of [[FooRoute]]. + */ class FooRouteFeatureFactory(routeData: KnoraRouteData) extends KnoraRouteFactory(routeData) with FeatureFactory { // A trait for version numbers of the new 'foo' feature. @@ -139,12 +139,12 @@ class FeatureToggleR2RSpec extends R2RSpec { new TestRouteFeature(pathStr = "foo", featureName = "the new foo, version 2", routeData = routeData) /** - * Constructs an implementation of the 'foo' route according to the feature factory - * configuration. - * - * @param featureFactoryConfig the per-request feature factory configuration. - * @return a route configured with the features enabled by the feature factory configuration. - */ + * Constructs an implementation of the 'foo' route according to the feature factory + * configuration. + * + * @param featureFactoryConfig the per-request feature factory configuration. + * @return a route configured with the features enabled by the feature factory configuration. + */ def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { // Get the 'new-foo' feature toggle. val fooToggle: FeatureToggle = featureFactoryConfig.getToggle("FeatureToggleR2RSpec-new-foo") @@ -162,8 +162,8 @@ class FeatureToggleR2RSpec extends R2RSpec { } /** - * A feature factory that constructs implementations of [[BarRoute]]. - */ + * A feature factory that constructs implementations of [[BarRoute]]. + */ class BarRouteFeatureFactory(routeData: KnoraRouteData) extends KnoraRouteFactory(routeData) with FeatureFactory { // The old 'bar' feature implementation. @@ -188,8 +188,8 @@ class FeatureToggleR2RSpec extends R2RSpec { } /** - * A feature factory that constructs implementations of [[BazRoute]]. - */ + * A feature factory that constructs implementations of [[BazRoute]]. + */ class BazRouteFeatureFactory(routeData: KnoraRouteData) extends KnoraRouteFactory(routeData) with FeatureFactory { // The old 'baz' feature implementation. @@ -213,53 +213,50 @@ class FeatureToggleR2RSpec extends R2RSpec { } /** - * A façade route that uses implementations constructed by [[FooRouteFeatureFactory]]. - */ + * A façade route that uses implementations constructed by [[FooRouteFeatureFactory]]. + */ class FooRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) { private val featureFactory = new FooRouteFeatureFactory(routeData) - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = featureFactory.makeRoute(featureFactoryConfig) - } } /** - * A façade route that uses implementations constructed by [[BarRouteFeatureFactory]]. - */ + * A façade route that uses implementations constructed by [[BarRouteFeatureFactory]]. + */ class BarRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) { private val featureFactory = new BarRouteFeatureFactory(routeData) - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = featureFactory.makeRoute(featureFactoryConfig) - } } /** - * A façade route that uses implementations constructed by [[BazRouteFeatureFactory]]. - */ + * A façade route that uses implementations constructed by [[BazRouteFeatureFactory]]. + */ class BazRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) { private val featureFactory = new BazRouteFeatureFactory(routeData) - override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = { + override def makeRoute(featureFactoryConfig: FeatureFactoryConfig): Route = featureFactory.makeRoute(featureFactoryConfig) - } } // The façade route instances that we are going to test. - private val fooRoute = DSPApiDirectives.handleErrors(system) { new FooRoute(routeData).knoraApiPath } - private val bazRoute = DSPApiDirectives.handleErrors(system) { new BazRoute(routeData).knoraApiPath } + private val fooRoute = DSPApiDirectives.handleErrors(system)(new FooRoute(routeData).knoraApiPath) + private val bazRoute = DSPApiDirectives.handleErrors(system)(new BazRoute(routeData).knoraApiPath) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) implicit val ec: ExecutionContextExecutor = system.dispatcher /** - * Parses the HTTP response header that lists the configured feature toggles. - * - * @param response the HTTP response. - * @return a string per toggle. - */ - private def parseResponseHeader(response: HttpResponse): Set[String] = { + * Parses the HTTP response header that lists the configured feature toggles. + * + * @param response the HTTP response. + * @return a string per toggle. + */ + private def parseResponseHeader(response: HttpResponse): Set[String] = response.headers.find(_.lowercaseName == FeatureToggle.RESPONSE_HEADER_LOWERCASE) match { case Some(header) => header.value.split(',').toSet.filter { toggleStr => @@ -268,7 +265,6 @@ class FeatureToggleR2RSpec extends R2RSpec { case None => Set.empty } - } "The feature toggle framework" should { "use default toggles" in { @@ -281,7 +277,8 @@ class FeatureToggleR2RSpec extends R2RSpec { "FeatureToggleR2RSpec-new-foo:1=on", "FeatureToggleR2RSpec-new-bar:1=on", "FeatureToggleR2RSpec-new-baz=off" - )) + ) + ) } } @@ -296,7 +293,8 @@ class FeatureToggleR2RSpec extends R2RSpec { "FeatureToggleR2RSpec-new-foo=off", "FeatureToggleR2RSpec-new-bar:1=on", "FeatureToggleR2RSpec-new-baz=off" - )) + ) + ) } } @@ -311,7 +309,8 @@ class FeatureToggleR2RSpec extends R2RSpec { "FeatureToggleR2RSpec-new-foo:2=on", "FeatureToggleR2RSpec-new-bar:1=on", "FeatureToggleR2RSpec-new-baz=off" - )) + ) + ) } } @@ -322,7 +321,9 @@ class FeatureToggleR2RSpec extends R2RSpec { assert(status == StatusCodes.BadRequest, responseStr) assert( responseStr.contains( - "You must specify a version number to enable feature toggle FeatureToggleR2RSpec-new-foo")) + "You must specify a version number to enable feature toggle FeatureToggleR2RSpec-new-foo" + ) + ) } } @@ -342,7 +343,9 @@ class FeatureToggleR2RSpec extends R2RSpec { assert(status == StatusCodes.BadRequest, responseStr) assert( responseStr.contains( - "You cannot specify a version number when disabling feature toggle FeatureToggleR2RSpec-new-foo")) + "You cannot specify a version number when disabling feature toggle FeatureToggleR2RSpec-new-foo" + ) + ) } } @@ -356,9 +359,9 @@ class FeatureToggleR2RSpec extends R2RSpec { } "not accept two settings for the same toggle" in { - Get(s"/baz").addHeader(RawHeader( - FeatureToggle.REQUEST_HEADER, - "FeatureToggleR2RSpec-new-foo=off,FeatureToggleR2RSpec-new-foo:2=on")) ~> bazRoute ~> check { + Get(s"/baz").addHeader( + RawHeader(FeatureToggle.REQUEST_HEADER, "FeatureToggleR2RSpec-new-foo=off,FeatureToggleR2RSpec-new-foo:2=on") + ) ~> bazRoute ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.BadRequest, responseStr) assert(responseStr.contains("You cannot set the same feature toggle more than once per request")) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala index 571be69ab8..8772b98bdc 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala @@ -30,8 +30,8 @@ object HealthRouteE2ESpec { } /** - * End-to-End (E2E) test specification for testing route rejections. - */ + * End-to-End (E2E) test specification for testing route rejections. + */ class HealthRouteE2ESpec extends E2ESpec(HealthRouteE2ESpec.config) { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala index 7ad443e13f..f2beef2509 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceChecker.scala @@ -34,8 +34,8 @@ import org.knora.webapi.messages.{OntologyConstants, SmartIri, StringFormatter} import scala.collection.mutable /** - * A factory that constructs [[InstanceChecker]] instances for different Knora response formats. - */ + * A factory that constructs [[InstanceChecker]] instances for different Knora response formats. + */ object InstanceChecker { // A cache of class definitions. private val classDefCache: mutable.Map[SmartIri, ClassInfoContentV2] = mutable.Map.empty @@ -44,28 +44,28 @@ object InstanceChecker { private val propertyDefCache: mutable.Map[SmartIri, PropertyInfoContentV2] = mutable.Map.empty /** - * Returns an [[InstanceChecker]] for Knora responses in JSON-LD format. - */ + * Returns an [[InstanceChecker]] for Knora responses in JSON-LD format. + */ def getJsonLDChecker: InstanceChecker = new InstanceChecker(new JsonLDInstanceInspector) } /** - * A test utility for checking whether an instance of an RDF class returned in a Knora response corresponds to the - * the class definition. - * - * @param instanceInspector an [[InstanceInspector]] for working with instances in a particular format. - */ + * A test utility for checking whether an instance of an RDF class returned in a Knora response corresponds to the + * the class definition. + * + * @param instanceInspector an [[InstanceInspector]] for working with instances in a particular format. + */ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * Checks that an instance of an RDF class returned in a Knora response corresponds to the class definition. - * - * @param instanceResponse a Knora response containing the instance to be checked. - * @param expectedClassIri the IRI of the expected class. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - */ + * Checks that an instance of an RDF class returned in a Knora response corresponds to the class definition. + * + * @param instanceResponse a Knora response containing the instance to be checked. + * @param expectedClassIri the IRI of the expected class. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + */ def check(instanceResponse: String, expectedClassIri: SmartIri, knoraRouteGet: String => String): Unit = { val instanceElement: InstanceElement = instanceInspector.toElement(instanceResponse) val definitions: Definitions = getDefinitions(classIri = expectedClassIri, knoraRouteGet = knoraRouteGet) @@ -90,28 +90,33 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } /** - * Logs an error message at DEBUG level and throws an [[AssertionException]] containing the message. - * - * @param msg the error message. - */ + * Logs an error message at DEBUG level and throws an [[AssertionException]] containing the message. + * + * @param msg the error message. + */ private def throwAndLogAssertionException(msg: String): Nothing = { logger.debug(msg) throw AssertionException(msg) } /** - * Recursively checks whether instance elements and their property objects correspond to their class definitions. - * - * @param instanceElement the instance to be checked. - * @param classIri the class IRI. - * @param definitions definitions of the class and any other relevant classes and properties. - */ + * Recursively checks whether instance elements and their property objects correspond to their class definitions. + * + * @param instanceElement the instance to be checked. + * @param classIri the class IRI. + * @param definitions definitions of the class and any other relevant classes and properties. + */ private def checkRec(instanceElement: InstanceElement, classIri: SmartIri, definitions: Definitions): Unit = { - if (!instanceInspector.elementHasCompatibleType(element = instanceElement, - expectedType = classIri, - definitions = definitions)) { + if ( + !instanceInspector.elementHasCompatibleType( + element = instanceElement, + expectedType = classIri, + definitions = definitions + ) + ) { throwAndLogAssertionException( - s"Instance type ${instanceElement.elementType} is not compatible with expected class IRI $classIri") + s"Instance type ${instanceElement.elementType} is not compatible with expected class IRI $classIri" + ) } val classDef: ClassInfoContentV2 = definitions.getClassDef(classIri, throwAndLogAssertionException) @@ -129,102 +134,111 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging if (extraInstancePropertyNames.nonEmpty) { throwAndLogAssertionException( - s"One or more instance properties are not allowed by cardinalities: ${extraInstancePropertyNames.mkString(", ")}") + s"One or more instance properties are not allowed by cardinalities: ${extraInstancePropertyNames.mkString(", ")}" + ) } // Check that cardinalities are respected. - propertyIrisToInstancePropertyNames.foreach { - case (propertyIri, instancePropertyName) => - val cardinality: Cardinality = classDef.directCardinalities(propertyIri).cardinality - val objectsOfProp: Vector[InstanceElement] = - instanceElement.propertyObjects.getOrElse(instancePropertyName, Vector.empty) - val numberOfObjects = objectsOfProp.size - - if ((cardinality == MustHaveOne && numberOfObjects != 1) || - (cardinality == MayHaveOne && numberOfObjects > 1) || - (cardinality == MustHaveSome && numberOfObjects == 0)) { - throwAndLogAssertionException( - s"Property $instancePropertyName has $numberOfObjects objects, but its cardinality is $cardinality") - } + propertyIrisToInstancePropertyNames.foreach { case (propertyIri, instancePropertyName) => + val cardinality: Cardinality = classDef.directCardinalities(propertyIri).cardinality + val objectsOfProp: Vector[InstanceElement] = + instanceElement.propertyObjects.getOrElse(instancePropertyName, Vector.empty) + val numberOfObjects = objectsOfProp.size + + if ( + (cardinality == MustHaveOne && numberOfObjects != 1) || + (cardinality == MayHaveOne && numberOfObjects > 1) || + (cardinality == MustHaveSome && numberOfObjects == 0) + ) { + throwAndLogAssertionException( + s"Property $instancePropertyName has $numberOfObjects objects, but its cardinality is $cardinality" + ) + } } // Check the objects of the instance's properties. - propertyIrisToInstancePropertyNames.foreach { - case (propertyIri, instancePropertyName) => - // Get the expected type of the property's objects. - val objectType: SmartIri = if (propertyIri.isKnoraApiV2EntityIri) { - val propertyDef = definitions.getPropertyDef(propertyIri, throwAndLogAssertionException) - getObjectType(propertyDef).getOrElse( - throwAndLogAssertionException(s"Property $propertyIri has no knora-api:objectType")) - } else { - OntologyConstants.Xsd.String.toSmartIri + propertyIrisToInstancePropertyNames.foreach { case (propertyIri, instancePropertyName) => + // Get the expected type of the property's objects. + val objectType: SmartIri = if (propertyIri.isKnoraApiV2EntityIri) { + val propertyDef = definitions.getPropertyDef(propertyIri, throwAndLogAssertionException) + getObjectType(propertyDef).getOrElse( + throwAndLogAssertionException(s"Property $propertyIri has no knora-api:objectType") + ) + } else { + OntologyConstants.Xsd.String.toSmartIri + } + + val objectTypeStr = objectType.toString + val objectTypeIsKnoraDefinedClass: Boolean = isKnoraDefinedClass(objectType) + val objectTypeIsKnoraDatatype: Boolean = + OntologyConstants.KnoraApiV2Simple.KnoraDatatypes.contains(objectTypeStr) + val objectsOfProp: Vector[InstanceElement] = + instanceElement.propertyObjects.getOrElse(instancePropertyName, Vector.empty) + + // Iterate over the objects of the property. + for (obj: InstanceElement <- objectsOfProp) { + val literalContentMsg = obj.literalContent match { + case Some(literalContent) => s" with literal content '$literalContent'" + case None => "" } - val objectTypeStr = objectType.toString - val objectTypeIsKnoraDefinedClass: Boolean = isKnoraDefinedClass(objectType) - val objectTypeIsKnoraDatatype: Boolean = - OntologyConstants.KnoraApiV2Simple.KnoraDatatypes.contains(objectTypeStr) - val objectsOfProp: Vector[InstanceElement] = - instanceElement.propertyObjects.getOrElse(instancePropertyName, Vector.empty) - - // Iterate over the objects of the property. - for (obj: InstanceElement <- objectsOfProp) { - val literalContentMsg = obj.literalContent match { - case Some(literalContent) => s" with literal content '$literalContent'" - case None => "" - } + val errorMsg = + s"Property $instancePropertyName has an object of type ${obj.elementType}$literalContentMsg, but type $objectType was expected" - val errorMsg = - s"Property $instancePropertyName has an object of type ${obj.elementType}$literalContentMsg, but type $objectType was expected" - - // Are we expecting an instance of a Knora class that isn't a datatype? - if (objectType.isKnoraApiV2EntityIri && !objectTypeIsKnoraDatatype) { - // Yes. Is it a class that Knora serves? - if (objectTypeIsKnoraDefinedClass) { - // Yes. If we got an IRI, accept it. Otherwise, recurse. - if (!instanceInspector.elementIsIri(obj)) { - checkRec(instanceElement = obj, classIri = objectType, definitions = definitions) - } - } else if (!instanceInspector.elementIsIri(obj)) { - // It's a class that Knora doesn't serve. Accept the object only if it's an IRI. - throwAndLogAssertionException( - s"Property $propertyIri requires an IRI referring to an instance of $objectType, but object content was received instead") - } - } else { - // We're expecting a literal. Ask the element inspector if the object is compatible with - // the expected type. - if (!instanceInspector.elementHasCompatibleType(element = obj, - expectedType = objectType, - definitions = definitions)) { - throwAndLogAssertionException(errorMsg) + // Are we expecting an instance of a Knora class that isn't a datatype? + if (objectType.isKnoraApiV2EntityIri && !objectTypeIsKnoraDatatype) { + // Yes. Is it a class that Knora serves? + if (objectTypeIsKnoraDefinedClass) { + // Yes. If we got an IRI, accept it. Otherwise, recurse. + if (!instanceInspector.elementIsIri(obj)) { + checkRec(instanceElement = obj, classIri = objectType, definitions = definitions) } + } else if (!instanceInspector.elementIsIri(obj)) { + // It's a class that Knora doesn't serve. Accept the object only if it's an IRI. + throwAndLogAssertionException( + s"Property $propertyIri requires an IRI referring to an instance of $objectType, but object content was received instead" + ) + } + } else { + // We're expecting a literal. Ask the element inspector if the object is compatible with + // the expected type. + if ( + !instanceInspector.elementHasCompatibleType( + element = obj, + expectedType = objectType, + definitions = definitions + ) + ) { + throwAndLogAssertionException(errorMsg) } } + } } } /** - * Gets all the definitions needed to check an instance of the specified class. - * - * @param classIri the class IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @return a [[Definitions]] instance containing the relevant definitions. - */ - private def getDefinitions(classIri: SmartIri, knoraRouteGet: String => String): Definitions = { + * Gets all the definitions needed to check an instance of the specified class. + * + * @param classIri the class IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @return a [[Definitions]] instance containing the relevant definitions. + */ + private def getDefinitions(classIri: SmartIri, knoraRouteGet: String => String): Definitions = getDefinitionsRec(classIri = classIri, knoraRouteGet = knoraRouteGet, definitions = Definitions()) - } /** - * Recursively gets definitions that are needed to check an instance of the specified class. - * - * @param classIri the class IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @param definitions the definitions collected so far. - * @return a [[Definitions]] instance containing the relevant definitions. - */ - private def getDefinitionsRec(classIri: SmartIri, - knoraRouteGet: String => String, - definitions: Definitions): Definitions = { + * Recursively gets definitions that are needed to check an instance of the specified class. + * + * @param classIri the class IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @param definitions the definitions collected so far. + * @return a [[Definitions]] instance containing the relevant definitions. + */ + private def getDefinitionsRec( + classIri: SmartIri, + knoraRouteGet: String => String, + definitions: Definitions + ): Definitions = { // Get the definition of classIri. val classDef: ClassInfoContentV2 = getClassDef(classIri = classIri, knoraRouteGet = knoraRouteGet) @@ -266,28 +280,27 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging val classIrisForRecursion = baseClassIris ++ classIrisFromObjectTypes -- definitions.classDefs.keySet - classIri // Recursively add the definitions of base classes and classes that we identified as object types. - classIrisForRecursion.foldLeft(updatedDefs) { - case (acc, classIriFromObjectType) => - val recDefinitions: Definitions = getDefinitionsRec( - classIri = classIriFromObjectType, - knoraRouteGet = knoraRouteGet, - definitions = acc - ) - - acc.copy( - classDefs = acc.classDefs ++ recDefinitions.classDefs, - propertyDefs = acc.propertyDefs ++ recDefinitions.propertyDefs - ) + classIrisForRecursion.foldLeft(updatedDefs) { case (acc, classIriFromObjectType) => + val recDefinitions: Definitions = getDefinitionsRec( + classIri = classIriFromObjectType, + knoraRouteGet = knoraRouteGet, + definitions = acc + ) + + acc.copy( + classDefs = acc.classDefs ++ recDefinitions.classDefs, + propertyDefs = acc.propertyDefs ++ recDefinitions.propertyDefs + ) } } /** - * Determines whether Knora should have the definition of a class. - * - * @param classIri the class IRI. - * @return `true` if Knora should have the definition of the class. - */ - private def isKnoraDefinedClass(classIri: SmartIri): Boolean = { + * Determines whether Knora should have the definition of a class. + * + * @param classIri the class IRI. + * @return `true` if Knora should have the definition of the class. + */ + private def isKnoraDefinedClass(classIri: SmartIri): Boolean = // There are some Knora classes whose definitions we refer to but don't actually serve, e.g. // knora-api:XMLToStandoffMapping. The ontology transformation rules list the internal classes that // aren't available in each external schema. But these lists include knora-base classes @@ -323,16 +336,15 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging } else { false } - } /** - * Gets a class definition from Knora. - * - * @param classIri the class IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @return the class definition. - */ - private def getClassDef(classIri: SmartIri, knoraRouteGet: String => String): ClassInfoContentV2 = { + * Gets a class definition from Knora. + * + * @param classIri the class IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @return the class definition. + */ + private def getClassDef(classIri: SmartIri, knoraRouteGet: String => String): ClassInfoContentV2 = InstanceChecker.classDefCache.get(classIri) match { case Some(classDef) => classDef @@ -352,16 +364,15 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging InstanceChecker.classDefCache.put(classIri, classDef) classDef } - } /** - * Gets a property definition from Knora. - * - * @param propertyIri the property IRI. - * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. - * @return the property definition. - */ - private def getPropertyDef(propertyIri: SmartIri, knoraRouteGet: String => String): PropertyInfoContentV2 = { + * Gets a property definition from Knora. + * + * @param propertyIri the property IRI. + * @param knoraRouteGet a function that takes a Knora API URL path and returns a response from Knora. + * @return the property definition. + */ + private def getPropertyDef(propertyIri: SmartIri, knoraRouteGet: String => String): PropertyInfoContentV2 = InstanceChecker.propertyDefCache.get(propertyIri) match { case Some(propertyDef) => propertyDef @@ -381,71 +392,70 @@ class InstanceChecker(instanceInspector: InstanceInspector) extends LazyLogging InstanceChecker.propertyDefCache.put(propertyIri, propertyDef) propertyDef } - } /** - * Gets the `knora-api:objectType` from a property definition in an external schema. - * - * @param propertyDef the property definition. - * @return the property's `knora-api:objectType`, if it has one. - */ - private def getObjectType(propertyDef: PropertyInfoContentV2): Option[SmartIri] = { + * Gets the `knora-api:objectType` from a property definition in an external schema. + * + * @param propertyDef the property definition. + * @return the property's `knora-api:objectType`, if it has one. + */ + private def getObjectType(propertyDef: PropertyInfoContentV2): Option[SmartIri] = propertyDef .getPredicateIriObject(OntologyConstants.KnoraApiV2Complex.ObjectType.toSmartIri) .orElse(propertyDef.getPredicateIriObject(OntologyConstants.KnoraApiV2Simple.ObjectType.toSmartIri)) - } } /** - * Represents an instance of an RDF class, or an element of such an instance. - * - * @param elementType the element type. This is an opaque string that only the [[InstanceInspector]] needs - * to understand. - * @param literalContent the literal content of the element (if available), for debugging. - * @param propertyObjects a map of property names to their objects. - */ -case class InstanceElement(elementType: String, - literalContent: Option[String] = None, - propertyObjects: Map[String, Vector[InstanceElement]] = Map.empty) + * Represents an instance of an RDF class, or an element of such an instance. + * + * @param elementType the element type. This is an opaque string that only the [[InstanceInspector]] needs + * to understand. + * @param literalContent the literal content of the element (if available), for debugging. + * @param propertyObjects a map of property names to their objects. + */ +case class InstanceElement( + elementType: String, + literalContent: Option[String] = None, + propertyObjects: Map[String, Vector[InstanceElement]] = Map.empty +) /** - * A trait for classes that help [[InstanceChecker]] check instances in different formats. - */ + * A trait for classes that help [[InstanceChecker]] check instances in different formats. + */ trait InstanceInspector { /** - * Converts a Knora response representing a class instance to an [[InstanceElement]]. - */ + * Converts a Knora response representing a class instance to an [[InstanceElement]]. + */ def toElement(instanceResponse: String): InstanceElement /** - * Converts an RDF property IRI to the property name used in instances. - */ + * Converts an RDF property IRI to the property name used in instances. + */ def propertyIriToInstancePropertyName(classIri: SmartIri, propertyIri: SmartIri): String /** - * Returns `true` if the specified instance element is an IRI pointing to an object, as opposed to - * the object itself. - */ + * Returns `true` if the specified instance element is an IRI pointing to an object, as opposed to + * the object itself. + */ def elementIsIri(element: InstanceElement): Boolean /** - * Checks, as far as possible, whether the type of an instance is compatible with the type IRI of its class. - */ + * Checks, as far as possible, whether the type of an instance is compatible with the type IRI of its class. + */ def elementHasCompatibleType(element: InstanceElement, expectedType: SmartIri, definitions: Definitions): Boolean } /** - * An [[InstanceInspector]] that works with Knora responses in JSON-LD format. - */ + * An [[InstanceInspector]] that works with Knora responses in JSON-LD format. + */ class JsonLDInstanceInspector extends InstanceInspector { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - override def toElement(response: String): InstanceElement = { + override def toElement(response: String): InstanceElement = jsonLDObjectToElement(JsonLDUtil.parseJsonLD(response).body) - } - private def jsonLDValueToElements(jsonLDValue: JsonLDValue): Vector[InstanceElement] = { + private def jsonLDValueToElements(jsonLDValue: JsonLDValue): Vector[InstanceElement] = jsonLDValue match { case jsonLDObject: JsonLDObject => if (jsonLDObject.isStringWithLang) { @@ -483,10 +493,12 @@ class JsonLDInstanceInspector extends InstanceInspector { case jsonLDBoolean: JsonLDBoolean => Vector( - InstanceElement(elementType = OntologyConstants.Xsd.Boolean, - literalContent = Some(jsonLDBoolean.value.toString))) + InstanceElement( + elementType = OntologyConstants.Xsd.Boolean, + literalContent = Some(jsonLDBoolean.value.toString) + ) + ) } - } private def jsonLDObjectToElement(jsonLDObject: JsonLDObject): InstanceElement = { val elementType = jsonLDObject.requireString(JsonLDKeywords.TYPE) @@ -498,17 +510,17 @@ class JsonLDInstanceInspector extends InstanceInspector { InstanceElement(elementType = elementType, propertyObjects = propertyObjects) } - override def propertyIriToInstancePropertyName(classIri: SmartIri, propertyIri: SmartIri): String = { + override def propertyIriToInstancePropertyName(classIri: SmartIri, propertyIri: SmartIri): String = propertyIri.toString - } - override def elementIsIri(element: InstanceElement): Boolean = { + override def elementIsIri(element: InstanceElement): Boolean = element.elementType == OntologyConstants.Xsd.Uri - } - override def elementHasCompatibleType(element: InstanceElement, - expectedType: SmartIri, - definitions: Definitions): Boolean = { + override def elementHasCompatibleType( + element: InstanceElement, + expectedType: SmartIri, + definitions: Definitions + ): Boolean = { val expectedTypeStr: IRI = expectedType.toString // Is the element's type a Knora class? @@ -525,25 +537,24 @@ class JsonLDInstanceInspector extends InstanceInspector { } /** - * Represents Knora ontology definitions used to check class instances against their definitions. - * - * @param classDefs relevant class definitions. - * @param propertyDefs relevant property definitions. - * @param subClassOf a map in which each class IRI points to the full set of its base classes. A class is also - * a subclass of itself. - */ -case class Definitions(classDefs: Map[SmartIri, ClassInfoContentV2] = Map.empty, - propertyDefs: Map[SmartIri, PropertyInfoContentV2] = Map.empty, - subClassOf: Map[SmartIri, Seq[SmartIri]] = Map.empty) { - def getClassDef(classIri: SmartIri, errorFun: String => Nothing): ClassInfoContentV2 = { + * Represents Knora ontology definitions used to check class instances against their definitions. + * + * @param classDefs relevant class definitions. + * @param propertyDefs relevant property definitions. + * @param subClassOf a map in which each class IRI points to the full set of its base classes. A class is also + * a subclass of itself. + */ +case class Definitions( + classDefs: Map[SmartIri, ClassInfoContentV2] = Map.empty, + propertyDefs: Map[SmartIri, PropertyInfoContentV2] = Map.empty, + subClassOf: Map[SmartIri, Seq[SmartIri]] = Map.empty +) { + def getClassDef(classIri: SmartIri, errorFun: String => Nothing): ClassInfoContentV2 = classDefs.getOrElse(classIri, errorFun(s"No definition for class $classIri")) - } - def getPropertyDef(propertyIri: SmartIri, errorFun: String => Nothing): PropertyInfoContentV2 = { + def getPropertyDef(propertyIri: SmartIri, errorFun: String => Nothing): PropertyInfoContentV2 = propertyDefs.getOrElse(propertyIri, errorFun(s"No definition for property $propertyIri")) - } - def getBaseClasses(classIri: SmartIri): Seq[SmartIri] = { + def getBaseClasses(classIri: SmartIri): Seq[SmartIri] = subClassOf.getOrElse(classIri, Seq.empty) - } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala index a1af51140e..db3397633d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala @@ -33,8 +33,8 @@ import org.knora.webapi.util.FileUtil import scala.concurrent.ExecutionContextExecutor /** - * Tests [[InstanceChecker]]. - */ + * Tests [[InstanceChecker]]. + */ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -65,7 +65,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "One or more instance properties are not allowed by cardinalities: http://0.0.0.0:3333/ontology/0001/anything/v2#hasExtraProperty") + exception.getMessage == "One or more instance properties are not allowed by cardinalities: http://0.0.0.0:3333/ontology/0001/anything/v2#hasExtraProperty" + ) } "reject a JSON-LD instance of anything:Thing (in the complex schema) with an extra property object" in { @@ -78,7 +79,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "Property http://0.0.0.0:3333/ontology/0001/anything/v2#hasBoolean has 2 objects, but its cardinality is 0-1") + exception.getMessage == "Property http://0.0.0.0:3333/ontology/0001/anything/v2#hasBoolean has 2 objects, but its cardinality is 0-1" + ) } "reject a JSON-LD instance of anything:Thing (in the complex schema) with an invalid literal type" in { @@ -91,7 +93,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "Property http://api.knora.org/ontology/knora-api/v2#booleanValueAsBoolean has an object of type http://www.w3.org/2001/XMLSchema#string with literal content 'invalid literal', but type http://www.w3.org/2001/XMLSchema#boolean was expected") + exception.getMessage == "Property http://api.knora.org/ontology/knora-api/v2#booleanValueAsBoolean has an object of type http://www.w3.org/2001/XMLSchema#string with literal content 'invalid literal', but type http://www.w3.org/2001/XMLSchema#boolean was expected" + ) } "reject a JSON-LD instance of anything:Thing (in the complex schema) with an invalid object type" in { @@ -104,7 +107,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "Instance type http://api.knora.org/ontology/knora-api/v2#DateValue is not compatible with expected class IRI http://api.knora.org/ontology/knora-api/v2#BooleanValue") + exception.getMessage == "Instance type http://api.knora.org/ontology/knora-api/v2#DateValue is not compatible with expected class IRI http://api.knora.org/ontology/knora-api/v2#BooleanValue" + ) } "reject a JSON-LD instance of anything:Thing (in the complex schema) with object content where an IRI is required" in { @@ -117,7 +121,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "Property http://api.knora.org/ontology/knora-api/v2#textValueHasMapping requires an IRI referring to an instance of http://api.knora.org/ontology/knora-api/v2#XMLToStandoffMapping, but object content was received instead") + exception.getMessage == "Property http://api.knora.org/ontology/knora-api/v2#textValueHasMapping requires an IRI referring to an instance of http://api.knora.org/ontology/knora-api/v2#XMLToStandoffMapping, but object content was received instead" + ) } "reject a JSON-LD instance of anything:Thing (in the simple schema) with an invalid datatype" in { @@ -130,7 +135,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "Property http://0.0.0.0:3333/ontology/0001/anything/simple/v2#hasDecimal has an object of type http://api.knora.org/ontology/knora-api/simple/v2#Date with literal content 'GREGORIAN:1489 CE', but type http://www.w3.org/2001/XMLSchema#decimal was expected") + exception.getMessage == "Property http://0.0.0.0:3333/ontology/0001/anything/simple/v2#hasDecimal has an object of type http://api.knora.org/ontology/knora-api/simple/v2#Date with literal content 'GREGORIAN:1489 CE', but type http://www.w3.org/2001/XMLSchema#decimal was expected" + ) } "reject a JSON-LD instance of anything:Thing (in the simple schema) without an rdfs:label" in { @@ -143,7 +149,8 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } assert( - exception.getMessage == "Property http://www.w3.org/2000/01/rdf-schema#label has 0 objects, but its cardinality is 1") + exception.getMessage == "Property http://www.w3.org/2000/01/rdf-schema#label has 0 objects, but its cardinality is 1" + ) } } } @@ -156,512 +163,512 @@ object InstanceCheckerSpec { val complexThingWithExtraProperty: String = """{ - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", - | "@type" : "anything:Thing", - | "anything:hasExtraProperty" : { - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", - | "@type" : "knora-api:BooleanValue", - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:booleanValueAsBoolean" : true, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | } - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | }, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", + | "@type" : "anything:Thing", + | "anything:hasExtraProperty" : { + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", + | "@type" : "knora-api:BooleanValue", + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:booleanValueAsBoolean" : true, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | } + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | }, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val complexThingWithExtraPropertyObject: String = """{ - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", - | "@type" : "anything:Thing", - | "anything:hasBoolean" : [ { - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", - | "@type" : "knora-api:BooleanValue", - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:booleanValueAsBoolean" : true, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | } - | }, { - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", - | "@type" : "knora-api:BooleanValue", - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:booleanValueAsBoolean" : false, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | } - | } ], - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | }, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", + | "@type" : "anything:Thing", + | "anything:hasBoolean" : [ { + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", + | "@type" : "knora-api:BooleanValue", + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:booleanValueAsBoolean" : true, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | } + | }, { + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", + | "@type" : "knora-api:BooleanValue", + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:booleanValueAsBoolean" : false, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | } + | } ], + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | }, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val complexThingWithInvalidLiteralType: String = """{ - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", - | "@type" : "anything:Thing", - | "anything:hasBoolean" : { - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", - | "@type" : "knora-api:BooleanValue", - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/o=j0jdxMQvanmAdpAIOcFA" - | }, - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/o=j0jdxMQvanmAdpAIOcFA.20190410T084145353992Z" - | }, - | "knora-api:valueHasUUID" : "o-j0jdxMQvanmAdpAIOcFA", - | "knora-api:booleanValueAsBoolean" : "invalid literal", - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | } - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | }, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", + | "@type" : "anything:Thing", + | "anything:hasBoolean" : { + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/o-j0jdxMQvanmAdpAIOcFA", + | "@type" : "knora-api:BooleanValue", + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/o=j0jdxMQvanmAdpAIOcFA" + | }, + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/o=j0jdxMQvanmAdpAIOcFA.20190410T084145353992Z" + | }, + | "knora-api:valueHasUUID" : "o-j0jdxMQvanmAdpAIOcFA", + | "knora-api:booleanValueAsBoolean" : "invalid literal", + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | } + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | }, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val complexThingWithInvalidObjectType: String = """{ - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", - | "@type" : "anything:Thing", - | "anything:hasBoolean" : { - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/lj35qx3vRUa6s1Q8s5Z5SA", - | "@type" : "knora-api:DateValue", - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:dateValueHasCalendar" : "GREGORIAN", - | "knora-api:dateValueHasEndEra" : "CE", - | "knora-api:dateValueHasEndYear" : 1489, - | "knora-api:dateValueHasStartEra" : "CE", - | "knora-api:dateValueHasStartYear" : 1489, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:valueAsString" : "GREGORIAN:1489 CE", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | } - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | }, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", + | "@type" : "anything:Thing", + | "anything:hasBoolean" : { + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/lj35qx3vRUa6s1Q8s5Z5SA", + | "@type" : "knora-api:DateValue", + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:dateValueHasCalendar" : "GREGORIAN", + | "knora-api:dateValueHasEndEra" : "CE", + | "knora-api:dateValueHasEndYear" : 1489, + | "knora-api:dateValueHasStartEra" : "CE", + | "knora-api:dateValueHasStartYear" : 1489, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:valueAsString" : "GREGORIAN:1489 CE", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | } + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | }, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val complexThingWithInvalidUseOfObjectInsteadOfIri: String = """{ - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", - | "@type" : "anything:Thing", - | "anything:hasRichtext" : { - | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/VY4XodOeSaOdttZ6rEkFPg", - | "@type" : "knora-api:TextValue", - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:textValueAsXml" : "\n

this is text

with standoff
", - | "knora-api:textValueHasMapping" : { - | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping", - | "@type" : "knora-api:XMLToStandoffMapping", - | "knora-api:hasMappingElement" : { - | "@type" : "knora-base:MappingElement", - | "knora-api:mappingHasXMLTagname" : "p" - | } - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/VY4XodOeSaOdttZ6rEkFPg" - | }, - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/VY4XodOeSaOdttZ6rEkFPg.20190410T084145353992Z" - | }, - | "knora-api:valueHasUUID" : "VY4XodOeSaOdttZ6rEkFPg", - | "knora-api:userHasPermission" : "CR", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | } - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "knora-api:attachedToUser" : { - | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" - | }, - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2019-04-10T08:41:45.353992Z" - | }, - | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - | "knora-api:userHasPermission" : "CR", - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg", + | "@type" : "anything:Thing", + | "anything:hasRichtext" : { + | "@id" : "http://rdfh.ch/0001/cUnhrC1DT821lwVWQSwEgg/values/VY4XodOeSaOdttZ6rEkFPg", + | "@type" : "knora-api:TextValue", + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:textValueAsXml" : "\n

this is text

with standoff
", + | "knora-api:textValueHasMapping" : { + | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping", + | "@type" : "knora-api:XMLToStandoffMapping", + | "knora-api:hasMappingElement" : { + | "@type" : "knora-base:MappingElement", + | "knora-api:mappingHasXMLTagname" : "p" + | } + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/VY4XodOeSaOdttZ6rEkFPg" + | }, + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0/VY4XodOeSaOdttZ6rEkFPg.20190410T084145353992Z" + | }, + | "knora-api:valueHasUUID" : "VY4XodOeSaOdttZ6rEkFPg", + | "knora-api:userHasPermission" : "CR", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | } + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "knora-api:attachedToUser" : { + | "@id" : "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" + | }, + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2019-04-10T08:41:45.353992Z" + | }, + | "knora-api:hasPermissions" : "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + | "knora-api:userHasPermission" : "CR", + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/cUnhrC1DT821lwVWQSwEgg0.20190410T084145353992Z" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val simpleThingWithInvalidDatatype: String = """ - |{ - | "@id" : "http://rdfh.ch/0001/oGI65x9pQkK6JhsoqavTGA", - | "@type" : "anything:Thing", - | "anything:hasDecimal" : { - | "@type" : "knora-api:Date", - | "@value" : "GREGORIAN:1489 CE" - | }, - | "knora-api:arkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/oGI65x9pQkK6JhsoqavTGAE" - | }, - | "knora-api:versionArkUrl" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/oGI65x9pQkK6JhsoqavTGAE.20190410T124515840198Z" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" - | } - |} + |{ + | "@id" : "http://rdfh.ch/0001/oGI65x9pQkK6JhsoqavTGA", + | "@type" : "anything:Thing", + | "anything:hasDecimal" : { + | "@type" : "knora-api:Date", + | "@value" : "GREGORIAN:1489 CE" + | }, + | "knora-api:arkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/oGI65x9pQkK6JhsoqavTGAE" + | }, + | "knora-api:versionArkUrl" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://0.0.0.0:3336/ark:/72163/1/0001/oGI65x9pQkK6JhsoqavTGAE.20190410T124515840198Z" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" + | } + |} """.stripMargin val simpleThingWithMissingLabel: String = """ - |{ - | "@id" : "http://rdfh.ch/0001/oGI65x9pQkK6JhsoqavTGA", - | "@type" : "anything:Thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" - | } - |} + |{ + | "@id" : "http://rdfh.ch/0001/oGI65x9pQkK6JhsoqavTGA", + | "@type" : "anything:Thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" + | } + |} """.stripMargin val correctUser: String = """{ - | "email": "anything.user01@example.org", - | "familyName": "User01", - | "givenName": "Anything", - | "groups": [ - | { - | "description": "A group for thing searchers.", - | "id": "http://rdfh.ch/groups/0001/thing-searcher", - | "name": "Thing searcher", - | "project": { - | "description": [ - | { - | "value": "Anything Project" - | } - | ], - | "id": "http://rdfh.ch/projects/0001", - | "keywords": [], - | "logo": null, - | "longname": "Anything Project", - | "ontologies": [ - | "http://www.knora.org/ontology/0001/anything", - | "http://www.knora.org/ontology/0001/something" - | ], - | "selfjoin": false, - | "shortcode": "0001", - | "shortname": "anything", - | "status": true - | }, - | "selfjoin": true, - | "status": true - | } - | ], - | "id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - | "lang": "de", - | "password": null, - | "permissions": { - | "administrativePermissionsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | { - | "additionalInformation": null, - | "name": "ProjectResourceCreateAllPermission", - | "permissionCode": null - | } - | ] - | }, - | "groupsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | "http://rdfh.ch/groups/0001/thing-searcher", - | "http://www.knora.org/ontology/knora-admin#ProjectMember" - | ] - | } - | }, - | "projects": [ - | { - | "description": [ - | { - | "value": "Anything Project" - | } - | ], - | "id": "http://rdfh.ch/projects/0001", - | "keywords": [], - | "logo": null, - | "longname": "Anything Project", - | "ontologies": [ - | "http://www.knora.org/ontology/0001/anything", - | "http://www.knora.org/ontology/0001/something" - | ], - | "selfjoin": false, - | "shortcode": "0001", - | "shortname": "anything", - | "status": true - | } - | ], - | "sessionId": null, - | "status": true, - | "token": null, - | "username": "anything.user01" - |}""".stripMargin + | "email": "anything.user01@example.org", + | "familyName": "User01", + | "givenName": "Anything", + | "groups": [ + | { + | "description": "A group for thing searchers.", + | "id": "http://rdfh.ch/groups/0001/thing-searcher", + | "name": "Thing searcher", + | "project": { + | "description": [ + | { + | "value": "Anything Project" + | } + | ], + | "id": "http://rdfh.ch/projects/0001", + | "keywords": [], + | "logo": null, + | "longname": "Anything Project", + | "ontologies": [ + | "http://www.knora.org/ontology/0001/anything", + | "http://www.knora.org/ontology/0001/something" + | ], + | "selfjoin": false, + | "shortcode": "0001", + | "shortname": "anything", + | "status": true + | }, + | "selfjoin": true, + | "status": true + | } + | ], + | "id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + | "lang": "de", + | "password": null, + | "permissions": { + | "administrativePermissionsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | { + | "additionalInformation": null, + | "name": "ProjectResourceCreateAllPermission", + | "permissionCode": null + | } + | ] + | }, + | "groupsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | "http://rdfh.ch/groups/0001/thing-searcher", + | "http://www.knora.org/ontology/knora-admin#ProjectMember" + | ] + | } + | }, + | "projects": [ + | { + | "description": [ + | { + | "value": "Anything Project" + | } + | ], + | "id": "http://rdfh.ch/projects/0001", + | "keywords": [], + | "logo": null, + | "longname": "Anything Project", + | "ontologies": [ + | "http://www.knora.org/ontology/0001/anything", + | "http://www.knora.org/ontology/0001/something" + | ], + | "selfjoin": false, + | "shortcode": "0001", + | "shortname": "anything", + | "status": true + | } + | ], + | "sessionId": null, + | "status": true, + | "token": null, + | "username": "anything.user01" + |}""".stripMargin val userWithExtraProperty: String = """ - |{ - | "username" : "test", - | "id" : "http://rdfh.ch/users/normaluser", - | "extraProperty" : "test", - | "email" : "test@example.org", - | "familyName" : "Tester", - | "givenName": "Test", - | "password" : "test", - | "lang" : "en", - | "status" : true, - | "permissions": { - | "administrativePermissionsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | { - | "additionalInformation": null, - | "name": "ProjectResourceCreateAllPermission", - | "permissionCode": null - | } - | ] - | }, - | "groupsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | "http://rdfh.ch/groups/0001/thing-searcher", - | "http://www.knora.org/ontology/knora-admin#ProjectMember" - | ] - | } - | }, - | "projects" : [ "http://rdfh.ch/projects/0001" ], - | "groups" : [] - |} + |{ + | "username" : "test", + | "id" : "http://rdfh.ch/users/normaluser", + | "extraProperty" : "test", + | "email" : "test@example.org", + | "familyName" : "Tester", + | "givenName": "Test", + | "password" : "test", + | "lang" : "en", + | "status" : true, + | "permissions": { + | "administrativePermissionsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | { + | "additionalInformation": null, + | "name": "ProjectResourceCreateAllPermission", + | "permissionCode": null + | } + | ] + | }, + | "groupsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | "http://rdfh.ch/groups/0001/thing-searcher", + | "http://www.knora.org/ontology/knora-admin#ProjectMember" + | ] + | } + | }, + | "projects" : [ "http://rdfh.ch/projects/0001" ], + | "groups" : [] + |} """.stripMargin val userWithMissingUsername: String = """ - |{ - | "id" : "http://rdfh.ch/users/normaluser", - | "email" : "test@example.org", - | "familyName" : "Tester", - | "givenName": "Test", - | "password" : "test", - | "lang" : "en", - | "status" : true, - | "permissions": { - | "administrativePermissionsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | { - | "additionalInformation": null, - | "name": "ProjectResourceCreateAllPermission", - | "permissionCode": null - | } - | ] - | }, - | "groupsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | "http://rdfh.ch/groups/0001/thing-searcher", - | "http://www.knora.org/ontology/knora-admin#ProjectMember" - | ] - | } - | }, - | "projects" : [ "http://rdfh.ch/projects/0001" ], - | "groups" : [] - |} + |{ + | "id" : "http://rdfh.ch/users/normaluser", + | "email" : "test@example.org", + | "familyName" : "Tester", + | "givenName": "Test", + | "password" : "test", + | "lang" : "en", + | "status" : true, + | "permissions": { + | "administrativePermissionsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | { + | "additionalInformation": null, + | "name": "ProjectResourceCreateAllPermission", + | "permissionCode": null + | } + | ] + | }, + | "groupsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | "http://rdfh.ch/groups/0001/thing-searcher", + | "http://www.knora.org/ontology/knora-admin#ProjectMember" + | ] + | } + | }, + | "projects" : [ "http://rdfh.ch/projects/0001" ], + | "groups" : [] + |} """.stripMargin val userWithInvalidObjectType: String = """ - |{ - | "id" : "http://rdfh.ch/users/normaluser", - | "username" : "test", - | "email" : "test@example.org", - | "familyName" : "Tester", - | "givenName": "Test", - | "password" : "test", - | "lang" : "en", - | "status" : "invalidValue", - | "permissions": { - | "administrativePermissionsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | { - | "additionalInformation": null, - | "name": "ProjectResourceCreateAllPermission", - | "permissionCode": null - | } - | ] - | }, - | "groupsPerProject": { - | "http://rdfh.ch/projects/0001": [ - | "http://rdfh.ch/groups/0001/thing-searcher", - | "http://www.knora.org/ontology/knora-admin#ProjectMember" - | ] - | } - | }, - | "projects" : [ "http://rdfh.ch/projects/0001" ], - | "groups" : [] - |} + |{ + | "id" : "http://rdfh.ch/users/normaluser", + | "username" : "test", + | "email" : "test@example.org", + | "familyName" : "Tester", + | "givenName": "Test", + | "password" : "test", + | "lang" : "en", + | "status" : "invalidValue", + | "permissions": { + | "administrativePermissionsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | { + | "additionalInformation": null, + | "name": "ProjectResourceCreateAllPermission", + | "permissionCode": null + | } + | ] + | }, + | "groupsPerProject": { + | "http://rdfh.ch/projects/0001": [ + | "http://rdfh.ch/groups/0001/thing-searcher", + | "http://www.knora.org/ontology/knora-admin#ProjectMember" + | ] + | } + | }, + | "projects" : [ "http://rdfh.ch/projects/0001" ], + | "groups" : [] + |} """.stripMargin } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala index 81e16c8578..338c1bee71 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala @@ -30,8 +30,8 @@ object RejectingRouteE2ESpec { } /** - * End-to-End (E2E) test specification for testing route rejections. - */ + * End-to-End (E2E) test specification for testing route rejections. + */ class RejectingRouteE2ESpec extends E2ESpec(RejectingRouteE2ESpec.config) { implicit def default(implicit system: ActorSystem) = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala index 159ef0a1e8..083d202096 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala @@ -43,8 +43,8 @@ object GroupsADME2ESpec { } /** - * End-to-End (E2E) test specification for testing groups endpoint. - */ + * End-to-End (E2E) test specification for testing groups endpoint. + */ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJsonProtocol with SessionJsonProtocol { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(30.seconds) @@ -64,8 +64,8 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs "used to query for group information" should { "return all groups" in { - val request = Get(baseApiUrl + s"/admin/groups") ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + val request = + Get(baseApiUrl + s"/admin/groups") ~> addCredentials(BasicHttpCredentials(imagesUser01Email, testPass)) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -83,7 +83,8 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs "return the group's information" in { val request = Get(baseApiUrl + s"/admin/groups/$groupIriEnc") ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + BasicHttpCredentials(imagesUser01Email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -105,12 +106,12 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs "create a group with the provided custom IRI " in { val createGroupWithCustomIriRequest: String = s"""{ "id": "$customGroupIri", - | "name": "NewGroupWithCustomIri", - | "description": "A new group with a custom Iri", - | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "status": true, - | "selfjoin": false - |}""".stripMargin + | "name": "NewGroupWithCustomIri", + | "description": "A new group with a custom Iri", + | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "status": true, + | "selfjoin": false + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -125,8 +126,8 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs val request = Post( baseApiUrl + s"/admin/groups", - HttpEntity(ContentTypes.`application/json`, createGroupWithCustomIriRequest)) ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + HttpEntity(ContentTypes.`application/json`, createGroupWithCustomIriRequest) + ) ~> addCredentials(BasicHttpCredentials(imagesUser01Email, testPass)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -150,15 +151,17 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs "return 'BadRequest' if the supplied IRI for the group is not unique" in { val params = s"""{ "id": "$customGroupIri", - | "name": "NewGroupWithDuplicateCustomIri", - | "description": "A new group with a duplicate custom Iri", - | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "status": true, - | "selfjoin": false - |}""".stripMargin - - val request = Post(baseApiUrl + s"/admin/groups", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + | "name": "NewGroupWithDuplicateCustomIri", + | "description": "A new group with a duplicate custom Iri", + | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "status": true, + | "selfjoin": false + |}""".stripMargin + + val request = + Post(baseApiUrl + s"/admin/groups", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(imagesUser01Email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -176,12 +179,12 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs val createGroupRequest: String = s"""{ - | "name": "NewGroup", - | "description": "NewGroupDescription", - | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "status": true, - | "selfjoin": false - |}""".stripMargin + | "name": "NewGroup", + | "description": "NewGroupDescription", + | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "status": true, + | "selfjoin": false + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -194,9 +197,10 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs ) ) - val request = Post(baseApiUrl + "/admin/groups", - HttpEntity(ContentTypes.`application/json`, createGroupRequest)) ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + val request = Post( + baseApiUrl + "/admin/groups", + HttpEntity(ContentTypes.`application/json`, createGroupRequest) + ) ~> addCredentials(BasicHttpCredentials(imagesUser01Email, testPass)) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) response.status should be(StatusCodes.OK) @@ -229,9 +233,9 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs val updateGroupRequest: String = s"""{ - | "name": "UpdatedGroupName", - | "description": "UpdatedGroupDescription" - |}""".stripMargin + | "name": "UpdatedGroupName", + | "description": "UpdatedGroupDescription" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -244,9 +248,10 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs ) ) val groupIriEnc = java.net.URLEncoder.encode(newGroupIri.get, "utf-8") - val request = Put(baseApiUrl + "/admin/groups/" + groupIriEnc, - HttpEntity(ContentTypes.`application/json`, updateGroupRequest)) ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + val request = Put( + baseApiUrl + "/admin/groups/" + groupIriEnc, + HttpEntity(ContentTypes.`application/json`, updateGroupRequest) + ) ~> addCredentials(BasicHttpCredentials(imagesUser01Email, testPass)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) response.status should be(StatusCodes.OK) @@ -275,7 +280,8 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs val groupIriEnc = java.net.URLEncoder.encode(newGroupIri.get, "utf-8") val request = Delete(baseApiUrl + "/admin/groups/" + groupIriEnc) ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + BasicHttpCredentials(imagesUser01Email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) response.status should be(StatusCodes.OK) @@ -305,8 +311,8 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs val changeGroupStatusRequest: String = s"""{ - | "status": true - |}""".stripMargin + | "status": true + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -320,9 +326,10 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs ) val groupIriEnc = java.net.URLEncoder.encode(newGroupIri.get, "utf-8") - val request = Put(baseApiUrl + "/admin/groups/" + groupIriEnc + "/status", - HttpEntity(ContentTypes.`application/json`, changeGroupStatusRequest)) ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + val request = Put( + baseApiUrl + "/admin/groups/" + groupIriEnc + "/status", + HttpEntity(ContentTypes.`application/json`, changeGroupStatusRequest) + ) ~> addCredentials(BasicHttpCredentials(imagesUser01Email, testPass)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) response.status should be(StatusCodes.OK) @@ -352,7 +359,8 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs "return all members of a group" in { val request = Get(baseApiUrl + s"/admin/groups/$groupIriEnc/members") ~> addCredentials( - BasicHttpCredentials(imagesUser01Email, testPass)) + BasicHttpCredentials(imagesUser01Email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala index 93dd8a69b9..dfec4e456a 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala @@ -41,10 +41,10 @@ object PermissionsADME2ESpec { } /** - * End-to-End (E2E) test specification for testing the 'v1/permissions' route. - * - * This spec tests the 'v1/store' route. - */ + * End-to-End (E2E) test specification for testing the 'v1/permissions' route. + * + * This spec tests the 'v1/store' route. + */ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with TriplestoreJsonProtocol { // Directory path for generated client test data private val clientTestDataPath: Seq[String] = Seq("admin", "permissions") @@ -60,7 +60,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val projectIri = java.net.URLEncoder.encode(SharedTestDataV1.imagesProjectInfo.id, "utf-8") val groupIri = java.net.URLEncoder.encode(OntologyConstants.KnoraAdmin.ProjectMember, "utf-8") val request = Get(baseApiUrl + s"/admin/permissions/ap/$projectIri/$groupIri") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass) + ) val response = singleAwaitingRequest(request, 1.seconds) logger.debug("==>> " + response.toString) @@ -89,7 +90,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val projectIri = java.net.URLEncoder.encode(SharedTestDataV1.imagesProjectInfo.id, "utf-8") val request = Get(baseApiUrl + s"/admin/permissions/ap/$projectIri") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass) + ) val response = singleAwaitingRequest(request, 1.seconds) logger.debug("==>> " + response.toString) @@ -114,7 +116,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val projectIri = java.net.URLEncoder.encode(SharedTestDataV1.imagesProjectInfo.id, "utf-8") val request = Get(baseApiUrl + s"/admin/permissions/doap/$projectIri") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass) + ) val response = singleAwaitingRequest(request, 1.seconds) logger.debug("==>> " + response.toString) @@ -139,7 +142,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val projectIri = java.net.URLEncoder.encode(SharedTestDataV1.imagesProjectInfo.id, "utf-8") val request = Get(baseApiUrl + s"/admin/permissions/$projectIri") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass) + ) val response = singleAwaitingRequest(request, 1.seconds) logger.debug("==>> " + response.toString) @@ -164,10 +168,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T "create an administrative access permission" in { val createAdministrativePermissionRequest: String = s"""{ - | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", - | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "hasPermissions":[{"additionalInformation":null,"name":"ProjectAdminGroupAllPermission","permissionCode":null}] - |}""".stripMargin + | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", + | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "hasPermissions":[{"additionalInformation":null,"name":"ProjectAdminGroupAllPermission","permissionCode":null}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -182,8 +186,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val request = Post( baseApiUrl + s"/admin/permissions/ap", - HttpEntity(ContentTypes.`application/json`, createAdministrativePermissionRequest)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + HttpEntity(ContentTypes.`application/json`, createAdministrativePermissionRequest) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -199,8 +203,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T .convertTo[String] assert(projectIri == "http://rdfh.ch/projects/0001") val permissions = result - .getOrElse("hasPermissions", - throw DeserializationException("The expected field 'hasPermissions' is missing.")) + .getOrElse( + "hasPermissions", + throw DeserializationException("The expected field 'hasPermissions' is missing.") + ) .toString() assert(permissions.contains("ProjectAdminGroupAllPermission")) @@ -218,11 +224,11 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val customAPIri = "http://rdfh.ch/permissions/0001/u0PRnDl3kgcbrehZnRlEfA" val createAdministrativePermissionWithCustomIriRequest: String = s"""{ - | "id": "$customAPIri", - | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", - | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "hasPermissions":[{"additionalInformation":null,"name":"ProjectAdminGroupAllPermission","permissionCode":null}] - |}""".stripMargin + | "id": "$customAPIri", + | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", + | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "hasPermissions":[{"additionalInformation":null,"name":"ProjectAdminGroupAllPermission","permissionCode":null}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -237,19 +243,19 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val createAdministrativePermissionWithCustomIriResponse: String = s"""{ - | "administrative_permission": { - | "forGroup": "http://rdfh.ch/groups/0001/thing-searcher", - | "forProject": "http://rdfh.ch/projects/0001", - | "hasPermissions": [ - | { - | "additionalInformation": null, - | "name": "ProjectAdminGroupAllPermission", - | "permissionCode": null - | } - | ], - | "iri": "$customAPIri" - | } - |}""".stripMargin + | "administrative_permission": { + | "forGroup": "http://rdfh.ch/groups/0001/thing-searcher", + | "forProject": "http://rdfh.ch/projects/0001", + | "hasPermissions": [ + | { + | "additionalInformation": null, + | "name": "ProjectAdminGroupAllPermission", + | "permissionCode": null + | } + | ], + | "iri": "$customAPIri" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -281,21 +287,24 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T |} |""".stripMargin - val request = Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, projectPayload)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Post( + baseApiUrl + s"/admin/projects", + HttpEntity(ContentTypes.`application/json`, projectPayload) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val permissionPayload = s"""{ - | "forGroup":"http://www.knora.org/ontology/knora-admin#KnownUser", - | "forProject":"$projectIri", - | "hasPermissions":[{"additionalInformation":null,"name":"ProjectResourceCreateAllPermission","permissionCode":null}] - |}""".stripMargin + | "forGroup":"http://www.knora.org/ontology/knora-admin#KnownUser", + | "forProject":"$projectIri", + | "hasPermissions":[{"additionalInformation":null,"name":"ProjectResourceCreateAllPermission","permissionCode":null}] + |}""".stripMargin - val permissionRequest = Post(baseApiUrl + s"/admin/permissions/ap", - HttpEntity(ContentTypes.`application/json`, permissionPayload)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val permissionRequest = Post( + baseApiUrl + s"/admin/permissions/ap", + HttpEntity(ContentTypes.`application/json`, permissionPayload) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val permissionResponse: HttpResponse = singleAwaitingRequest(permissionRequest) assert(permissionResponse.status === StatusCodes.OK) @@ -305,12 +314,12 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T "create a default object access permission" in { val createDefaultObjectAccessPermissionRequest: String = s"""{ - | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", - | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "forProperty":null, - | "forResourceClass":null, - | "hasPermissions":[{"additionalInformation":"http://www.knora.org/ontology/knora-admin#ProjectMember","name":"D","permissionCode":7}] - |}""".stripMargin + | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", + | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "forProperty":null, + | "forResourceClass":null, + | "hasPermissions":[{"additionalInformation":"http://www.knora.org/ontology/knora-admin#ProjectMember","name":"D","permissionCode":7}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -325,8 +334,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val request = Post( baseApiUrl + s"/admin/permissions/doap", - HttpEntity(ContentTypes.`application/json`, createDefaultObjectAccessPermissionRequest)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + HttpEntity(ContentTypes.`application/json`, createDefaultObjectAccessPermissionRequest) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -341,8 +350,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T .convertTo[String] assert(projectIri == "http://rdfh.ch/projects/0001") val permissions = result - .getOrElse("hasPermissions", - throw DeserializationException("The expected field 'hasPermissions' is missing.")) + .getOrElse( + "hasPermissions", + throw DeserializationException("The expected field 'hasPermissions' is missing.") + ) .toString() assert(permissions.contains("http://www.knora.org/ontology/knora-admin#ProjectMember")) @@ -363,13 +374,13 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val createDefaultObjectAccessPermissionWithCustomIriRequest: String = s"""{ - | "id": "$customDOAPIri", - | "forGroup":null, - | "forProject":"${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "forProperty":null, - | "forResourceClass":"${SharedOntologyTestDataADM.IMAGES_BILD_RESOURCE_CLASS}", - | "hasPermissions":[{"additionalInformation":"http://www.knora.org/ontology/knora-admin#ProjectMember","name":"D","permissionCode":7}] - |}""".stripMargin + | "id": "$customDOAPIri", + | "forGroup":null, + | "forProject":"${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "forProperty":null, + | "forResourceClass":"${SharedOntologyTestDataADM.IMAGES_BILD_RESOURCE_CLASS}", + | "hasPermissions":[{"additionalInformation":"http://www.knora.org/ontology/knora-admin#ProjectMember","name":"D","permissionCode":7}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -382,10 +393,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T ) ) - val request = Post(baseApiUrl + s"/admin/permissions/doap", - HttpEntity(ContentTypes.`application/json`, - createDefaultObjectAccessPermissionWithCustomIriRequest)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Post( + baseApiUrl + s"/admin/permissions/doap", + HttpEntity(ContentTypes.`application/json`, createDefaultObjectAccessPermissionWithCustomIriRequest) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -396,8 +407,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T .convertTo[String] assert(permissionIri == customDOAPIri) val forResourceClassIRI = result - .getOrElse("forResourceClass", - throw DeserializationException("The expected field 'forResourceClass' is missing.")) + .getOrElse( + "forResourceClass", + throw DeserializationException("The expected field 'forResourceClass' is missing.") + ) .convertTo[String] assert(forResourceClassIRI == SharedOntologyTestDataADM.IMAGES_BILD_RESOURCE_CLASS) val projectIri = result @@ -405,8 +418,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T .convertTo[String] assert(projectIri == "http://rdfh.ch/projects/00FF") val permissions = result - .getOrElse("hasPermissions", - throw DeserializationException("The expected field 'hasPermissions' is missing.")) + .getOrElse( + "hasPermissions", + throw DeserializationException("The expected field 'hasPermissions' is missing.") + ) .toString() assert(permissions.contains("http://www.knora.org/ontology/knora-admin#ProjectMember")) @@ -443,9 +458,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = updatePermissionGroup ) ) - val request = Put(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/group", - HttpEntity(ContentTypes.`application/json`, updatePermissionGroup)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Put( + baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/group", + HttpEntity(ContentTypes.`application/json`, updatePermissionGroup) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val result = AkkaHttpUtils.httpResponseToJson(response).fields("administrative_permission").asJsObject.fields @@ -484,9 +500,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = updatePermissionGroup ) ) - val request = Put(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/group", - HttpEntity(ContentTypes.`application/json`, updatePermissionGroup)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Put( + baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/group", + HttpEntity(ContentTypes.`application/json`, updatePermissionGroup) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val result = @@ -525,15 +542,18 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = updateHasPermissions ) ) - val request = Put(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/hasPermissions", - HttpEntity(ContentTypes.`application/json`, updateHasPermissions)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Put( + baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/hasPermissions", + HttpEntity(ContentTypes.`application/json`, updateHasPermissions) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val result = AkkaHttpUtils.httpResponseToJson(response).fields("administrative_permission").asJsObject.fields val permissions = result - .getOrElse("hasPermissions", - throw DeserializationException("The expected field 'hasPermissions' is missing.")) + .getOrElse( + "hasPermissions", + throw DeserializationException("The expected field 'hasPermissions' is missing.") + ) .asInstanceOf[JsArray] .elements permissions.size should be(1) @@ -567,16 +587,19 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = updateHasPermissions ) ) - val request = Put(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/hasPermissions", - HttpEntity(ContentTypes.`application/json`, updateHasPermissions)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Put( + baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/hasPermissions", + HttpEntity(ContentTypes.`application/json`, updateHasPermissions) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val result = AkkaHttpUtils.httpResponseToJson(response).fields("default_object_access_permission").asJsObject.fields val permissions = result - .getOrElse("hasPermissions", - throw DeserializationException("The expected field 'hasPermissions' is missing.")) + .getOrElse( + "hasPermissions", + throw DeserializationException("The expected field 'hasPermissions' is missing.") + ) .asInstanceOf[JsArray] .elements permissions.size should be(1) @@ -584,7 +607,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T permissions.head.asJsObject .fields("additionalInformation") .toString - .contains("http://www.knora.org/ontology/knora-admin#ProjectMember")) + .contains("http://www.knora.org/ontology/knora-admin#ProjectMember") + ) clientTestDataCollector.addFile( TestDataFileContent( filePath = TestDataFilePath( @@ -615,16 +639,19 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = updateResourceClass ) ) - val request = Put(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/resourceClass", - HttpEntity(ContentTypes.`application/json`, updateResourceClass)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Put( + baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/resourceClass", + HttpEntity(ContentTypes.`application/json`, updateResourceClass) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val result = AkkaHttpUtils.httpResponseToJson(response).fields("default_object_access_permission").asJsObject.fields val forResourceClassIRI = result - .getOrElse("forResourceClass", - throw DeserializationException("The expected field 'forResourceClass' is missing.")) + .getOrElse( + "forResourceClass", + throw DeserializationException("The expected field 'forResourceClass' is missing.") + ) .convertTo[String] assert(forResourceClassIRI == resourceClassIri) @@ -658,9 +685,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = updateResourceClass ) ) - val request = Put(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/property", - HttpEntity(ContentTypes.`application/json`, updateResourceClass)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + val request = Put( + baseApiUrl + s"/admin/permissions/" + encodedPermissionIri + "/property", + HttpEntity(ContentTypes.`application/json`, updateResourceClass) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val result = @@ -688,7 +716,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val permissionIri = customDOAPIri val encodedPermissionIri = java.net.URLEncoder.encode(permissionIri, "utf-8") val request = Delete(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val deletedStatus = AkkaHttpUtils.httpResponseToJson(response).fields("deleted") @@ -708,7 +737,8 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val permissionIri = "http://rdfh.ch/permissions/00FF/a2" val encodedPermissionIri = java.net.URLEncoder.encode(permissionIri, "utf-8") val request = Delete(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val deletedStatus = AkkaHttpUtils.httpResponseToJson(response).fields("deleted") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala index fecc3180f8..31dcc7195a 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala @@ -50,8 +50,8 @@ object ProjectsADME2ESpec { } /** - * End-to-End (E2E) test specification for testing groups endpoint. - */ + * End-to-End (E2E) test specification for testing groups endpoint. + */ class ProjectsADME2ESpec extends E2ESpec(ProjectsADME2ESpec.config) with SessionJsonProtocol @@ -106,7 +106,8 @@ class ProjectsADME2ESpec "return the information for a single project identified by iri" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -124,7 +125,8 @@ class ProjectsADME2ESpec "return the information for a single project identified by shortname" in { val request = Get(baseApiUrl + s"/admin/projects/shortname/$projectShortname") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -132,7 +134,8 @@ class ProjectsADME2ESpec "return the information for a single project identified by shortcode" in { val request = Get(baseApiUrl + s"/admin/projects/shortcode/$projectShortcode") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -140,7 +143,8 @@ class ProjectsADME2ESpec "return the project's restricted view settings using its IRI" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/RestrictedViewSettings") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -163,8 +167,9 @@ class ProjectsADME2ESpec } "return the project's restricted view settings using its shortname" in { - val request = Get(baseApiUrl + s"/admin/projects/shortname/$projectShortname/RestrictedViewSettings") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = Get( + baseApiUrl + s"/admin/projects/shortname/$projectShortname/RestrictedViewSettings" + ) ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -176,8 +181,9 @@ class ProjectsADME2ESpec } "return the project's restricted view settings using its shortcode" in { - val request = Get(baseApiUrl + s"/admin/projects/shortcode/$projectShortcode/RestrictedViewSettings") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = Get( + baseApiUrl + s"/admin/projects/shortcode/$projectShortcode/RestrictedViewSettings" + ) ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -195,16 +201,16 @@ class ProjectsADME2ESpec val createProjectWithCustomIRIRequest: String = s"""{ - | "id": "$customProjectIri", - | "shortname": "newprojectWithIri", - | "shortcode": "3333", - | "longname": "new project with a custom IRI", - | "description": [{"value": "a project created with a custom IRI", "language": "en"}], - | "keywords": ["projectIRI"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |}""".stripMargin + | "id": "$customProjectIri", + | "shortname": "newprojectWithIri", + | "shortcode": "3333", + | "longname": "new project with a custom IRI", + | "description": [{"value": "a project created with a custom IRI", "language": "en"}], + | "keywords": ["projectIRI"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -219,8 +225,8 @@ class ProjectsADME2ESpec val request = Post( baseApiUrl + s"/admin/projects", - HttpEntity(ContentTypes.`application/json`, createProjectWithCustomIRIRequest)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + HttpEntity(ContentTypes.`application/json`, createProjectWithCustomIRIRequest) + ) ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -235,7 +241,8 @@ class ProjectsADME2ESpec result.longname should be(Some("new project with a custom IRI")) result.keywords should be(Seq("projectIRI")) result.description should be( - Seq(StringLiteralV2(value = "a project created with a custom IRI", language = Some("en")))) + Seq(StringLiteralV2(value = "a project created with a custom IRI", language = Some("en"))) + ) clientTestDataCollector.addFile( TestDataFileContent( @@ -253,19 +260,21 @@ class ProjectsADME2ESpec "return 'BadRequest' if the supplied project IRI is not unique" in { val params = s"""{ - | "id": "$customProjectIri", - | "shortname": "newprojectWithDuplicateIri", - | "shortcode": "2222", - | "longname": "new project with a duplicate custom invalid IRI", - | "description": [{"value": "a project created with a duplicate custom IRI", "language": "en"}], - | "keywords": ["projectDuplicateIRI"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |}""".stripMargin - - val request = Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + | "id": "$customProjectIri", + | "shortname": "newprojectWithDuplicateIri", + | "shortcode": "2222", + | "longname": "new project with a duplicate custom invalid IRI", + | "description": [{"value": "a project created with a duplicate custom IRI", "language": "en"}], + | "keywords": ["projectDuplicateIRI"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |}""".stripMargin + + val request = + Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -283,15 +292,15 @@ class ProjectsADME2ESpec "CREATE a new project and return the project info if the supplied shortname is unique" in { val createProjectRequest: String = s"""{ - | "shortname": "newproject", - | "shortcode": "1111", - | "longname": "project longname", - | "description": [{"value": "project description", "language": "en"}], - | "keywords": ["keywords"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |}""".stripMargin + | "shortname": "newproject", + | "shortcode": "1111", + | "longname": "project longname", + | "description": [{"value": "project description", "language": "en"}], + | "keywords": ["keywords"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -303,9 +312,10 @@ class ProjectsADME2ESpec text = createProjectRequest ) ) - val request = Post(baseApiUrl + s"/admin/projects", - HttpEntity(ContentTypes.`application/json`, createProjectRequest)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = Post( + baseApiUrl + s"/admin/projects", + HttpEntity(ContentTypes.`application/json`, createProjectRequest) + ) ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: {}", response) response.status should be(StatusCodes.OK) @@ -337,20 +347,22 @@ class ProjectsADME2ESpec "return a 'BadRequest' if the supplied project shortname during creation is not unique" in { val params = s""" - |{ - | "shortname": "newproject", - | "shortcode": "1112", - | "longname": "project longname", - | "description": [{"value": "project description", "language": "en"}], - | "keywords": ["keywords"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |} + |{ + | "shortname": "newproject", + | "shortcode": "1112", + | "longname": "project longname", + | "description": [{"value": "project description", "language": "en"}], + | "keywords": ["keywords"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |} """.stripMargin - val request = Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -359,19 +371,21 @@ class ProjectsADME2ESpec "return 'BadRequest' if 'shortname' during creation is missing" in { val params = s""" - |{ - | "shortcode": "1112", - | "longname": "project longname", - | "description": [{"value": "project description", "language": "en"}], - | "keywords": ["keywords"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |} + |{ + | "shortcode": "1112", + | "longname": "project longname", + | "description": [{"value": "project description", "language": "en"}], + | "keywords": ["keywords"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |} """.stripMargin - val request = Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -379,19 +393,21 @@ class ProjectsADME2ESpec "return 'BadRequest' if 'shortcode' during creation is missing" in { val params = s""" - |{ - | "shortname": "newproject2", - | "longname": "project longname", - | "description": [{"value": "project description", "language": "en"}], - | "keywords": ["keywords"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |} + |{ + | "shortname": "newproject2", + | "longname": "project longname", + | "description": [{"value": "project description", "language": "en"}], + | "keywords": ["keywords"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |} """.stripMargin - val request = Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -399,20 +415,22 @@ class ProjectsADME2ESpec "return 'BadRequest' if 'project description' during creation is missing" in { val params = s""" - |{ - | "shortcode": "1114", - | "shortname": "newproject5", - | "longname": "project longname", - | "description": [], - | "keywords": ["keywords"], - | "logo": "/fu/bar/baz.jpg", - | "status": true, - | "selfjoin": false - |} + |{ + | "shortcode": "1114", + | "shortname": "newproject5", + | "longname": "project longname", + | "description": [], + | "keywords": ["keywords"], + | "logo": "/fu/bar/baz.jpg", + | "status": true, + | "selfjoin": false + |} """.stripMargin - val request = Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Post(baseApiUrl + s"/admin/projects", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -421,14 +439,14 @@ class ProjectsADME2ESpec val updateProjectRequest: String = s"""{ - | "shortname": "newproject", - | "longname": "updated project longname", - | "description": [{"value": "updated project description", "language": "en"}], - | "keywords": ["updated", "keywords"], - | "logo": "/fu/bar/baz-updated.jpg", - | "status": true, - | "selfjoin": true - |}""".stripMargin + | "shortname": "newproject", + | "longname": "updated project longname", + | "description": [{"value": "updated project description", "language": "en"}], + | "keywords": ["updated", "keywords"], + | "logo": "/fu/bar/baz-updated.jpg", + | "status": true, + | "selfjoin": true + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -441,9 +459,10 @@ class ProjectsADME2ESpec ) ) val projectIriEncoded = URLEncoder.encode(newProjectIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/projects/iri/" + projectIriEncoded, - HttpEntity(ContentTypes.`application/json`, updateProjectRequest)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = Put( + baseApiUrl + s"/admin/projects/iri/" + projectIriEncoded, + HttpEntity(ContentTypes.`application/json`, updateProjectRequest) + ) ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -472,11 +491,11 @@ class ProjectsADME2ESpec "UPDATE a project with multiple description" in { val updateProjectMultipleDescriptionRequest: String = s"""{ - | "description": [ - | {"value": "Test Project", "language": "en"}, - | {"value": "Test Project", "language": "se"} - | ] - |}""".stripMargin + | "description": [ + | {"value": "Test Project", "language": "en"}, + | {"value": "Test Project", "language": "se"} + | ] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -491,8 +510,8 @@ class ProjectsADME2ESpec val projectIriEncoded = URLEncoder.encode(newProjectIri.get, "utf-8") val request = Put( baseApiUrl + s"/admin/projects/iri/" + projectIriEncoded, - HttpEntity(ContentTypes.`application/json`, updateProjectMultipleDescriptionRequest)) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + HttpEntity(ContentTypes.`application/json`, updateProjectMultipleDescriptionRequest) + ) ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -517,7 +536,8 @@ class ProjectsADME2ESpec val projectIriEncoded = URLEncoder.encode(newProjectIri.get, "utf-8") val request = Delete(baseApiUrl + s"/admin/projects/iri/" + projectIriEncoded) ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) response.status should be(StatusCodes.OK) @@ -543,7 +563,8 @@ class ProjectsADME2ESpec "return all members of a project identified by iri" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -565,7 +586,8 @@ class ProjectsADME2ESpec "return all members of a project identified by shortname" in { val request = Get(baseApiUrl + s"/admin/projects/shortname/$projectShortname/members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -576,7 +598,8 @@ class ProjectsADME2ESpec "return all members of a project identified by shortcode" in { val request = Get(baseApiUrl + s"/admin/projects/shortcode/$projectShortcode/members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -587,7 +610,8 @@ class ProjectsADME2ESpec "return all admin members of a project identified by iri" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/admin-members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -608,7 +632,8 @@ class ProjectsADME2ESpec "return all admin members of a project identified by shortname" in { val request = Get(baseApiUrl + s"/admin/projects/shortname/$projectShortname/admin-members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -619,7 +644,8 @@ class ProjectsADME2ESpec "return all admin members of a project identified by shortcode" in { val request = Get(baseApiUrl + s"/admin/projects/shortcode/$projectShortcode/admin-members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -633,42 +659,48 @@ class ProjectsADME2ESpec "return members of a project to a SystemAdmin" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) } "return members of a project to a ProjectAdmin" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/members") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, testPass)) + BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) } "return `Forbidden` for members of a project to a normal user" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/members") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.imagesUser02.email, testPass)) + BasicHttpCredentials(SharedTestDataADM.imagesUser02.email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.Forbidden) } "return admin-members of a project to a SystemAdmin" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/admin-members") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) } "return admin-members of a project to a ProjectAdmin" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/admin-members") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, testPass)) + BasicHttpCredentials(SharedTestDataADM.imagesUser01.email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) } "return `Forbidden` for admin-members of a project to a normal user" in { val request = Get(baseApiUrl + s"/admin/projects/iri/$projectIriEnc/admin-members") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.imagesUser02.email, testPass)) + BasicHttpCredentials(SharedTestDataADM.imagesUser02.email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.Forbidden) } @@ -677,8 +709,8 @@ class ProjectsADME2ESpec "used to query keywords" should { "return all unique keywords for all projects" in { - val request = Get(baseApiUrl + s"/admin/projects/Keywords") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Get(baseApiUrl + s"/admin/projects/Keywords") ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -700,7 +732,8 @@ class ProjectsADME2ESpec "return all keywords for a single project" in { val incunabulaIriEnc = URLEncoder.encode(SharedTestDataADM.incunabulaProject.id, "utf-8") val request = Get(baseApiUrl + s"/admin/projects/iri/$incunabulaIriEnc/Keywords") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -722,7 +755,8 @@ class ProjectsADME2ESpec "return empty list for a project without keywords" in { val dokubibIriEnc = URLEncoder.encode(SharedTestDataADM.dokubibProject.id, "utf-8") val request = Get(baseApiUrl + s"/admin/projects/iri/$dokubibIriEnc/Keywords") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -734,7 +768,8 @@ class ProjectsADME2ESpec "return 'NotFound' when the project IRI is unknown" in { val notexistingIriEnc = URLEncoder.encode("http://rdfh.ch/projects/notexisting", "utf-8") val request = Get(baseApiUrl + s"/admin/projects/iri/$notexistingIriEnc/Keywords") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: {}", response) assert(response.status === StatusCodes.NotFound) @@ -745,7 +780,8 @@ class ProjectsADME2ESpec "return a TriG file containing all data from a project" in { val anythingProjectIriEnc = URLEncoder.encode(SharedTestDataADM.anythingProject.id, "utf-8") val request = Get(baseApiUrl + s"/admin/projects/iri/$anythingProjectIriEnc/AllData") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, testPass)) + BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val trigStrFuture: Future[String] = Unmarshal(response.entity).to[String] @@ -760,7 +796,8 @@ class ProjectsADME2ESpec "http://www.knora.org/data/0001/anything", "http://www.knora.org/data/permissions", "http://www.knora.org/data/admin" - )) + ) + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/SipiADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/SipiADME2ESpec.scala index 98ddd747f1..8a2a34f8ac 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/SipiADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/SipiADME2ESpec.scala @@ -44,10 +44,10 @@ object SipiADME2ESpec { } /** - * End-to-End (E2E) test specification for Sipi access. - * - * This spec tests the 'admin/files'. - */ + * End-to-End (E2E) test specification for Sipi access. + * + * This spec tests the 'admin/files'. + */ class SipiADME2ESpec extends E2ESpec(SipiADME2ESpec.config) with SessionJsonProtocol with TriplestoreJsonProtocol { private implicit def default(implicit system: ActorSystem) = RouteTestTimeout(30.seconds) @@ -72,16 +72,16 @@ class SipiADME2ESpec extends E2ESpec(SipiADME2ESpec.config) with SessionJsonProt sr.sid } - def sessionLogout(sessionId: String): Unit = { + def sessionLogout(sessionId: String): Unit = Get(baseApiUrl + "/v1/session?logout") ~> Cookie(KNORA_AUTHENTICATION_COOKIE_NAME, sessionId) - } "The Files Route ('admin/files') using token credentials" should { "return CR (8) permission code" in { /* anything image */ val request = Get( - baseApiUrl + s"/admin/files/0001/B1D0OkEgfFp-Cew2Seur7Wi.jp2?email=$anythingAdminEmailEnc&password=$testPass") + baseApiUrl + s"/admin/files/0001/B1D0OkEgfFp-Cew2Seur7Wi.jp2?email=$anythingAdminEmailEnc&password=$testPass" + ) val response: HttpResponse = singleAwaitingRequest(request) // println(response.toString) @@ -144,7 +144,8 @@ class SipiADME2ESpec extends E2ESpec(SipiADME2ESpec.config) with SessionJsonProt /* anything image */ val request = Get(baseApiUrl + s"/admin/files/0001/B1D0OkEgfFp-Cew2Seur7Wi.jp2") ~> Cookie( KNORA_AUTHENTICATION_COOKIE_NAME, - sessionId) + sessionId + ) val response: HttpResponse = singleAwaitingRequest(request) // println(response.toString) @@ -167,7 +168,8 @@ class SipiADME2ESpec extends E2ESpec(SipiADME2ESpec.config) with SessionJsonProt /* anything image */ val request = Get(baseApiUrl + s"/admin/files/0001/B1D0OkEgfFp-Cew2Seur7Wi.jp2") ~> Cookie( KNORA_AUTHENTICATION_COOKIE_NAME, - sessionId) + sessionId + ) val response: HttpResponse = singleAwaitingRequest(request) // println(response.toString) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala index 98b1b5c98e..e59291c793 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala @@ -38,25 +38,25 @@ object StoreADME2ESpec { } /** - * End-to-End (E2E) test specification for testing the 'v1/store' route. - * - * This spec tests the 'v1/store' route. - */ + * End-to-End (E2E) test specification for testing the 'v1/store' route. + * + * This spec tests the 'v1/store' route. + */ class StoreADME2ESpec extends E2ESpec(StoreADME2ESpec.config) with TriplestoreJsonProtocol { implicit def default(implicit system: ActorSystem) = RouteTestTimeout(120.seconds) /** - * The marshaling to Json is done automatically by spray, hence the import of the 'TriplestoreJsonProtocol'. - * The Json which spray generates looks like this: - * - * [ - * {"path": "test_data/all_data/incunabula-data.ttl", "name": "http://www.knora.org/data/0803/incunabula"}, - * {"path": "test_data/demo_data/images-demo-data.ttl", "name": "http://www.knora.org/data/00FF/images"} - * ] - * - * and could have been supplied to the post request instead of the scala object. - */ + * The marshaling to Json is done automatically by spray, hence the import of the 'TriplestoreJsonProtocol'. + * The Json which spray generates looks like this: + * + * [ + * {"path": "test_data/all_data/incunabula-data.ttl", "name": "http://www.knora.org/data/0803/incunabula"}, + * {"path": "test_data/demo_data/images-demo-data.ttl", "name": "http://www.knora.org/data/00FF/images"} + * ] + * + * and could have been supplied to the post request instead of the scala object. + */ /* override lazy val rdfDataObjects: List[RdfDataObject] = List( RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), @@ -69,14 +69,16 @@ class StoreADME2ESpec extends E2ESpec(StoreADME2ESpec.config) with TriplestoreJs "succeed with resetting if startup flag is set" in { /** - * This test corresponds to the following curl call: - * curl -H "Content-Type: application/json" -X POST -d '[{"path":"../knora-ontologies/knora-base.ttl","name":"http://www.knora.org/ontology/knora-base"}]' http://localhost:3333/admin/store/ResetTriplestoreContent - */ + * This test corresponds to the following curl call: + * curl -H "Content-Type: application/json" -X POST -d '[{"path":"../knora-ontologies/knora-base.ttl","name":"http://www.knora.org/ontology/knora-base"}]' http://localhost:3333/admin/store/ResetTriplestoreContent + */ logger.debug("==>>") appActor ! SetAllowReloadOverHTTPState(true) logger.debug("==>>") - val request = Post(baseApiUrl + "/admin/store/ResetTriplestoreContent", - HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint)) + val request = Post( + baseApiUrl + "/admin/store/ResetTriplestoreContent", + HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint) + ) val response = singleAwaitingRequest(request, 300.seconds) // log.debug("==>> " + response.toString) assert(response.status === StatusCodes.OK) @@ -84,8 +86,10 @@ class StoreADME2ESpec extends E2ESpec(StoreADME2ESpec.config) with TriplestoreJs "fail with resetting if startup flag is not set" in { appActor ! SetAllowReloadOverHTTPState(false) - val request = Post(baseApiUrl + "/admin/store/ResetTriplestoreContent", - HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint)) + val request = Post( + baseApiUrl + "/admin/store/ResetTriplestoreContent", + HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint) + ) val response = singleAwaitingRequest(request, 300.seconds) // log.debug("==>> " + response.toString) assert(response.status === StatusCodes.Forbidden) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala index e55e8edda3..119906e6c2 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala @@ -49,8 +49,8 @@ object UsersADME2ESpec { } /** - * End-to-End (E2E) test specification for testing users endpoint. - */ + * End-to-End (E2E) test specification for testing users endpoint. + */ class UsersADME2ESpec extends E2ESpec(UsersADME2ESpec.config) with ProjectsADMJsonProtocol @@ -103,43 +103,46 @@ class UsersADME2ESpec private val clientTestDataCollector = new ClientTestDataCollector(settings) /** - * Convenience method returning the users project memberships. - * - * @param userIri the user's IRI. - * @param credentials the credentials of the user making the request. - */ + * Convenience method returning the users project memberships. + * + * @param userIri the user's IRI. + * @param credentials the credentials of the user making the request. + */ private def getUserProjectMemberships(userIri: IRI, credentials: CredentialsV1): Seq[ProjectADM] = { val userIriEnc = java.net.URLEncoder.encode(userIri, "utf-8") val request = Get(baseApiUrl + s"/admin/users/iri/$userIriEnc/project-memberships") ~> addCredentials( - BasicHttpCredentials(credentials.email, credentials.password)) + BasicHttpCredentials(credentials.email, credentials.password) + ) val response: HttpResponse = singleAwaitingRequest(request) AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[Seq[ProjectADM]] } /** - * Convenience method returning the users project-admin memberships. - * - * @param userIri the user's IRI. - * @param credentials the credentials of the user making the request. - */ + * Convenience method returning the users project-admin memberships. + * + * @param userIri the user's IRI. + * @param credentials the credentials of the user making the request. + */ private def getUserProjectAdminMemberships(userIri: IRI, credentials: CredentialsV1): Seq[ProjectADM] = { val userIriEnc = java.net.URLEncoder.encode(userIri, "utf-8") val request = Get(baseApiUrl + s"/admin/users/iri/$userIriEnc/project-admin-memberships") ~> addCredentials( - BasicHttpCredentials(credentials.email, credentials.password)) + BasicHttpCredentials(credentials.email, credentials.password) + ) val response: HttpResponse = singleAwaitingRequest(request) AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[Seq[ProjectADM]] } /** - * Convenience method returning the users group memberships. - * - * @param userIri the user's IRI. - * @param credentials the credentials of the user making the request. - */ + * Convenience method returning the users group memberships. + * + * @param userIri the user's IRI. + * @param credentials the credentials of the user making the request. + */ private def getUserGroupMemberships(userIri: IRI, credentials: CredentialsV1): Seq[GroupADM] = { val userIriEnc = java.net.URLEncoder.encode(userIri, "utf-8") val request = Get(baseApiUrl + s"/admin/users/iri/$userIriEnc/group-memberships") ~> addCredentials( - BasicHttpCredentials(credentials.email, credentials.password)) + BasicHttpCredentials(credentials.email, credentials.password) + ) val response: HttpResponse = singleAwaitingRequest(request) AkkaHttpUtils.httpResponseToJson(response).fields("groups").convertTo[Seq[GroupADM]] } @@ -149,8 +152,8 @@ class UsersADME2ESpec "used to query user information [FUNCTIONALITY]" should { "return all users" in { - val request = Get(baseApiUrl + s"/admin/users") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = + Get(baseApiUrl + s"/admin/users") ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -169,7 +172,8 @@ class UsersADME2ESpec "return a single user profile identified by iri" in { /* Correct username and password */ val request = Get(baseApiUrl + s"/admin/users/iri/${rootCreds.urlEncodedIri}") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -188,7 +192,8 @@ class UsersADME2ESpec "return a single user profile identified by email" in { /* Correct username and password */ val request = Get(baseApiUrl + s"/admin/users/email/${rootCreds.urlEncodedEmail}") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -196,8 +201,9 @@ class UsersADME2ESpec "return a single user profile identified by username" in { /* Correct username and password */ - val request = Get(baseApiUrl + s"/admin/users/username/${SharedTestDataADM.rootUser.username}") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Get( + baseApiUrl + s"/admin/users/username/${SharedTestDataADM.rootUser.username}" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -209,7 +215,8 @@ class UsersADME2ESpec "return single user for SystemAdmin" in { val request = Get(baseApiUrl + s"/admin/users/iri/$normalUserIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) clientTestDataCollector.addFile( @@ -226,7 +233,8 @@ class UsersADME2ESpec "return single user for itself" in { val request = Get(baseApiUrl + s"/admin/users/iri/$normalUserIriEnc") ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, normalUserCreds.password)) + BasicHttpCredentials(normalUserCreds.email, normalUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) clientTestDataCollector.addFile( @@ -243,7 +251,8 @@ class UsersADME2ESpec "return only public information for single user for non SystemAdmin and self" in { val request = Get(baseApiUrl + s"/admin/users/iri/$normalUserIriEnc") ~> addCredentials( - BasicHttpCredentials(projectAdminCreds.email, projectAdminCreds.password)) + BasicHttpCredentials(projectAdminCreds.email, projectAdminCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val result: UserADM = AkkaHttpUtils.httpResponseToJson(response).fields("user").convertTo[UserADM] @@ -287,8 +296,8 @@ class UsersADME2ESpec } "return all users for SystemAdmin" in { - val request = Get(baseApiUrl + s"/admin/users") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = + Get(baseApiUrl + s"/admin/users") ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) clientTestDataCollector.addFile( @@ -305,7 +314,8 @@ class UsersADME2ESpec "return all users for ProjectAdmin" in { val request = Get(baseApiUrl + s"/admin/users") ~> addCredentials( - BasicHttpCredentials(projectAdminCreds.email, projectAdminCreds.password)) + BasicHttpCredentials(projectAdminCreds.email, projectAdminCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) clientTestDataCollector.addFile( @@ -322,7 +332,8 @@ class UsersADME2ESpec "return 'Forbidden' for all users for normal user" in { val request = Get(baseApiUrl + s"/admin/users") ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, normalUserCreds.password)) + BasicHttpCredentials(normalUserCreds.email, normalUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.Forbidden) } @@ -334,16 +345,16 @@ class UsersADME2ESpec "create a user with the provided custom IRI " in { val createUserWithCustomIriRequest: String = s"""{ - | "id": "$customUserIri", - | "username": "userWithCustomIri", - | "email": "userWithCustomIri@example.org", - | "givenName": "a user", - | "familyName": "with a custom Iri", - | "password": "test", - | "status": true, - | "lang": "en", - | "systemAdmin": false - |}""".stripMargin + | "id": "$customUserIri", + | "username": "userWithCustomIri", + | "email": "userWithCustomIri@example.org", + | "givenName": "a user", + | "familyName": "with a custom Iri", + | "password": "test", + | "status": true, + | "lang": "en", + | "systemAdmin": false + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -355,8 +366,10 @@ class UsersADME2ESpec text = createUserWithCustomIriRequest ) ) - val request = Post(baseApiUrl + s"/admin/users", - HttpEntity(ContentTypes.`application/json`, createUserWithCustomIriRequest)) + val request = Post( + baseApiUrl + s"/admin/users", + HttpEntity(ContentTypes.`application/json`, createUserWithCustomIriRequest) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -381,16 +394,16 @@ class UsersADME2ESpec "return 'BadRequest' if the supplied IRI for the user is not unique" in { val params = s"""{ - | "id": "$customUserIri", - | "username": "userWithDuplicateCustomIri", - | "email": "userWithDuplicateCustomIri@example.org", - | "givenName": "a user", - | "familyName": "with a duplicate custom Iri", - | "password": "test", - | "status": true, - | "lang": "en", - | "systemAdmin": false - |}""".stripMargin + | "id": "$customUserIri", + | "username": "userWithDuplicateCustomIri", + | "email": "userWithDuplicateCustomIri@example.org", + | "givenName": "a user", + | "familyName": "with a duplicate custom Iri", + | "password": "test", + | "status": true, + | "lang": "en", + | "systemAdmin": false + |}""".stripMargin val request = Post(baseApiUrl + s"/admin/users", HttpEntity(ContentTypes.`application/json`, params)) val response: HttpResponse = singleAwaitingRequest(request) @@ -420,8 +433,10 @@ class UsersADME2ESpec | "systemAdmin": false |}""".stripMargin - val request = Post(baseApiUrl + s"/admin/users", - HttpEntity(ContentTypes.`application/json`, createUserWithApostropheRequest)) + val request = Post( + baseApiUrl + s"/admin/users", + HttpEntity(ContentTypes.`application/json`, createUserWithApostropheRequest) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -443,9 +458,10 @@ class UsersADME2ESpec |}""".stripMargin val userIriEncoded = java.net.URLEncoder.encode(otherCustomUserIri, "utf-8") - val request = Put(baseApiUrl + s"/admin/users/iri/$userIriEncoded/BasicUserInformation", - HttpEntity(ContentTypes.`application/json`, updateUserRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$userIriEncoded/BasicUserInformation", + HttpEntity(ContentTypes.`application/json`, updateUserRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -459,7 +475,8 @@ class UsersADME2ESpec val userIriEncoded = java.net.URLEncoder.encode(otherCustomUserIri, "utf-8") val request = Get(baseApiUrl + s"/admin/users/iri/$userIriEncoded") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -609,7 +626,8 @@ class UsersADME2ESpec "authenticate the newly created user using HttpBasicAuth" in { val request = Get(baseApiUrl + s"/v2/authentication") ~> addCredentials( - BasicHttpCredentials("donald.duck@example.org", "test")) + BasicHttpCredentials("donald.duck@example.org", "test") + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -639,12 +657,12 @@ class UsersADME2ESpec val updateUserRequest: String = s"""{ - | "username": "donald.big.duck", - | "email": "donald.big.duck@example.org", - | "givenName": "Big Donald", - | "familyName": "Duckmann", - | "lang": "de" - |}""".stripMargin + | "username": "donald.big.duck", + | "email": "donald.big.duck@example.org", + | "givenName": "Big Donald", + | "familyName": "Duckmann", + | "lang": "de" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -658,9 +676,10 @@ class UsersADME2ESpec ) val userIriEncoded = java.net.URLEncoder.encode(donaldIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/users/iri/$userIriEncoded/BasicUserInformation", - HttpEntity(ContentTypes.`application/json`, updateUserRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$userIriEncoded/BasicUserInformation", + HttpEntity(ContentTypes.`application/json`, updateUserRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -702,9 +721,10 @@ class UsersADME2ESpec ) val missingUserIri = "" - val request = Put(baseApiUrl + s"/admin/users/iri/$missingUserIri/BasicUserInformation", - HttpEntity(ContentTypes.`application/json`, updateUserRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$missingUserIri/BasicUserInformation", + HttpEntity(ContentTypes.`application/json`, updateUserRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.NotFound) @@ -721,9 +741,10 @@ class UsersADME2ESpec ) val missingUserIriNone = None - val request2 = Put(baseApiUrl + s"/admin/users/iri/$missingUserIriNone/BasicUserInformation", - HttpEntity(ContentTypes.`application/json`, updateUserRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request2 = Put( + baseApiUrl + s"/admin/users/iri/$missingUserIriNone/BasicUserInformation", + HttpEntity(ContentTypes.`application/json`, updateUserRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response2: HttpResponse = singleAwaitingRequest(request2) response2.status should be(StatusCodes.BadRequest) @@ -745,9 +766,9 @@ class UsersADME2ESpec val changeUserPasswordRequest: String = s"""{ - | "requesterPassword": "test", - | "newPassword": "test123456" - |}""".stripMargin + | "requesterPassword": "test", + | "newPassword": "test123456" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -760,9 +781,10 @@ class UsersADME2ESpec ) ) - val request1 = Put(baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", - HttpEntity(ContentTypes.`application/json`, changeUserPasswordRequest)) ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test")) // requester's password + val request1 = Put( + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", + HttpEntity(ContentTypes.`application/json`, changeUserPasswordRequest) + ) ~> addCredentials(BasicHttpCredentials(normalUserCreds.email, "test")) // requester's password val response1: HttpResponse = singleAwaitingRequest(request1) logger.debug(s"response: ${response1.toString}") response1.status should be(StatusCodes.OK) @@ -780,7 +802,8 @@ class UsersADME2ESpec // check if the password was changed, i.e. if the new one is accepted val request2 = Get(baseApiUrl + s"/v2/authentication") ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test123456")) // new password + BasicHttpCredentials(normalUserCreds.email, "test123456") + ) // new password val response2: HttpResponse = singleAwaitingRequest(request2) response2.status should be(StatusCodes.OK) @@ -796,16 +819,18 @@ class UsersADME2ESpec } """.stripMargin - val request1 = Put(baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", - HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, "test")) // requester's password + val request1 = Put( + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", + HttpEntity(ContentTypes.`application/json`, params01) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, "test")) // requester's password val response1: HttpResponse = singleAwaitingRequest(request1) logger.debug(s"response: ${response1.toString}") response1.status should be(StatusCodes.OK) // check if the password was changed, i.e. if the new one is accepted val request2 = Get(baseApiUrl + s"/v2/authentication") ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test654321")) // new password + BasicHttpCredentials(normalUserCreds.email, "test654321") + ) // new password val response2: HttpResponse = singleAwaitingRequest(request2) response2.status should be(StatusCodes.OK) } @@ -828,9 +853,10 @@ class UsersADME2ESpec ) ) - val request1 = Put(baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", - HttpEntity(ContentTypes.`application/json`, changeUserPasswordRequest)) ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test")) // requester's password + val request1 = Put( + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", + HttpEntity(ContentTypes.`application/json`, changeUserPasswordRequest) + ) ~> addCredentials(BasicHttpCredentials(normalUserCreds.email, "test")) // requester's password val response1: HttpResponse = singleAwaitingRequest(request1) response1.status should be(StatusCodes.BadRequest) @@ -848,7 +874,8 @@ class UsersADME2ESpec // check that the password was not changed, i.e. the old one is still accepted val request2 = Get(baseApiUrl + s"/v2/authentication") ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test654321")) // old password (taken from previous test) + BasicHttpCredentials(normalUserCreds.email, "test654321") + ) // old password (taken from previous test) val response2: HttpResponse = singleAwaitingRequest(request2) response2.status should be(StatusCodes.OK) } @@ -871,9 +898,10 @@ class UsersADME2ESpec ) ) - val request1 = Put(baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", - HttpEntity(ContentTypes.`application/json`, changeUserPasswordRequest)) ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test")) // requester's password + val request1 = Put( + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/Password", + HttpEntity(ContentTypes.`application/json`, changeUserPasswordRequest) + ) ~> addCredentials(BasicHttpCredentials(normalUserCreds.email, "test")) // requester's password val response1: HttpResponse = singleAwaitingRequest(request1) response1.status should be(StatusCodes.BadRequest) @@ -891,7 +919,8 @@ class UsersADME2ESpec // check that the password was not changed, i.e. the old one is still accepted val request2 = Get(baseApiUrl + s"/v2/authentication") ~> addCredentials( - BasicHttpCredentials(normalUserCreds.email, "test654321")) // old password + BasicHttpCredentials(normalUserCreds.email, "test654321") + ) // old password val response2: HttpResponse = singleAwaitingRequest(request2) response2.status should be(StatusCodes.OK) } @@ -899,8 +928,8 @@ class UsersADME2ESpec "change user's status" in { val changeUserStatusRequest: String = s"""{ - | "status": false - |}""".stripMargin + | "status": false + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -913,9 +942,10 @@ class UsersADME2ESpec ) ) val donaldIriEncoded = java.net.URLEncoder.encode(donaldIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/users/iri/$donaldIriEncoded/Status", - HttpEntity(ContentTypes.`application/json`, changeUserStatusRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$donaldIriEncoded/Status", + HttpEntity(ContentTypes.`application/json`, changeUserStatusRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -942,9 +972,10 @@ class UsersADME2ESpec |}""".stripMargin val donaldIriEncoded = java.net.URLEncoder.encode(donaldIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/users/iri/$donaldIriEncoded/Status", - HttpEntity(ContentTypes.`application/json`, updateUserRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$donaldIriEncoded/Status", + HttpEntity(ContentTypes.`application/json`, updateUserRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -953,8 +984,8 @@ class UsersADME2ESpec "update the user's system admin membership status" in { val changeUserSystemAdminMembershipRequest: String = s"""{ - | "systemAdmin": true - |}""".stripMargin + | "systemAdmin": true + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -969,8 +1000,8 @@ class UsersADME2ESpec val donaldIriEncoded = java.net.URLEncoder.encode(donaldIri.get, "utf-8") val request = Put( baseApiUrl + s"/admin/users/iri/$donaldIriEncoded/SystemAdmin", - HttpEntity(ContentTypes.`application/json`, changeUserSystemAdminMembershipRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + HttpEntity(ContentTypes.`application/json`, changeUserSystemAdminMembershipRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -993,8 +1024,8 @@ class UsersADME2ESpec // Throw BadRequest exception if user is built-in user val badRequest = Put( baseApiUrl + s"/admin/users/iri/$systemUserIriEncoded/SystemAdmin", - HttpEntity(ContentTypes.`application/json`, changeUserSystemAdminMembershipRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + HttpEntity(ContentTypes.`application/json`, changeUserSystemAdminMembershipRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val badResponse: HttpResponse = singleAwaitingRequest(badRequest) badResponse.status should be(StatusCodes.BadRequest) } @@ -1007,8 +1038,8 @@ class UsersADME2ESpec val request = Put( baseApiUrl + s"/admin/users/iri/$systemUserIriEncoded/SystemAdmin", - HttpEntity(ContentTypes.`application/json`, changeUserSystemAdminMembershipRequest)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + HttpEntity(ContentTypes.`application/json`, changeUserSystemAdminMembershipRequest) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -1021,9 +1052,10 @@ class UsersADME2ESpec } """.stripMargin - val request = Put(baseApiUrl + s"/admin/users/iri/$systemUserIriEncoded/Status", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$systemUserIriEncoded/Status", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -1039,9 +1071,10 @@ class UsersADME2ESpec } """.stripMargin - val request = Put(baseApiUrl + s"/admin/users/iri/$anonymousUserIriEncoded/Status", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Put( + baseApiUrl + s"/admin/users/iri/$anonymousUserIriEncoded/Status", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -1049,7 +1082,8 @@ class UsersADME2ESpec "delete a user" in { val userIriEncoded = java.net.URLEncoder.encode(customUserIri, "utf-8") val request = Delete(baseApiUrl + s"/admin/users/iri/$userIriEncoded") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -1067,7 +1101,8 @@ class UsersADME2ESpec "not allow deleting the system user" in { val request = Delete(baseApiUrl + s"/admin/users/iri/$systemUserIriEncoded") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -1076,7 +1111,8 @@ class UsersADME2ESpec val anonymousUserIriEncoded = java.net.URLEncoder.encode(KnoraSystemInstances.Users.AnonymousUser.id, "utf-8") val request = Delete(baseApiUrl + s"/admin/users/iri/$anonymousUserIriEncoded") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) } @@ -1087,16 +1123,19 @@ class UsersADME2ESpec "return all projects the user is a member of" in { val request = Get(baseApiUrl + s"/admin/users/iri/$multiUserIriEnc/project-memberships") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val projects: Seq[ProjectADM] = AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[List[ProjectADM]] - projects should contain allElementsOf Seq(SharedTestDataADM.imagesProject, - SharedTestDataADM.incunabulaProject, - SharedTestDataADM.anythingProject) + projects should contain allElementsOf Seq( + SharedTestDataADM.imagesProject, + SharedTestDataADM.incunabulaProject, + SharedTestDataADM.anythingProject + ) // testing getUserProjectMemberships method, which should return the same result projects should contain allElementsOf getUserProjectMemberships(multiUserIri, rootCreds) @@ -1120,8 +1159,8 @@ class UsersADME2ESpec membershipsBeforeUpdate should equal(Seq()) val request = Post( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -1145,8 +1184,8 @@ class UsersADME2ESpec val membershipsBeforeTryUpdate = getUserProjectMemberships(normalUserCreds.userIri, rootCreds) val request = Post( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.BadRequest) @@ -1173,8 +1212,8 @@ class UsersADME2ESpec membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProject)) val request = Delete( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -1199,16 +1238,19 @@ class UsersADME2ESpec "used to query project admin group memberships" should { "return all projects the user is a member of the project admin group" in { - val request = Get(baseApiUrl + s"/admin/users/iri/$multiUserIriEnc/project-admin-memberships") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = Get( + baseApiUrl + s"/admin/users/iri/$multiUserIriEnc/project-admin-memberships" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) val projects: Seq[ProjectADM] = AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[Seq[ProjectADM]] - projects should contain allElementsOf Seq(SharedTestDataADM.imagesProject, - SharedTestDataADM.incunabulaProject, - SharedTestDataADM.anythingProject) + projects should contain allElementsOf Seq( + SharedTestDataADM.imagesProject, + SharedTestDataADM.incunabulaProject, + SharedTestDataADM.anythingProject + ) // explicitly testing 'getUserProjectsAdminMemberships' method, which should return the same result projects should contain allElementsOf getUserProjectAdminMemberships(multiUserIri, rootCreds) @@ -1233,8 +1275,8 @@ class UsersADME2ESpec membershipsBeforeUpdate should equal(Seq()) val request = Post( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-admin-memberships/$imagesProjectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-admin-memberships/$imagesProjectIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -1262,8 +1304,8 @@ class UsersADME2ESpec membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProject)) val request = Delete( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-admin-memberships/$imagesProjectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-admin-memberships/$imagesProjectIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -1290,7 +1332,8 @@ class UsersADME2ESpec "return all groups the user is a member of" in { val request = Get(baseApiUrl + s"/admin/users/iri/$multiUserIriEnc/group-memberships") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -1324,8 +1367,8 @@ class UsersADME2ESpec membershipsBeforeUpdate should equal(Seq.empty[GroupADM]) val request = Post( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/group-memberships/$imagesReviewerGroupIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/group-memberships/$imagesReviewerGroupIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -1351,8 +1394,8 @@ class UsersADME2ESpec membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesReviewerGroup)) val request = Delete( - baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/group-memberships/$imagesReviewerGroupIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/group-memberships/$imagesReviewerGroupIriEnc" + ) ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala index 2be4eef75d..b19c962cf2 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala @@ -43,8 +43,8 @@ object DeleteListItemsRouteADME2ESpec { } /** - * End-to-End (E2E) test specification for testing endpoint. - */ + * End-to-End (E2E) test specification for testing endpoint. + */ class DeleteListItemsRouteADME2ESpec extends E2ESpec(DeleteListItemsRouteADME2ESpec.config) with SessionJsonProtocol @@ -92,7 +92,8 @@ class DeleteListItemsRouteADME2ESpec "return forbidden exception when requesting user is not system or project admin" in { val encodedNodeUrl = java.net.URLEncoder.encode(SharedListsTestDataADM.otherTreeListInfo.id, "utf-8") val request = Delete(baseApiUrl + s"/admin/lists/" + encodedNodeUrl) ~> addCredentials( - BasicHttpCredentials(anythingUserCreds.user.email, anythingUserCreds.password)) + BasicHttpCredentials(anythingUserCreds.user.email, anythingUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.Forbidden) } @@ -100,7 +101,8 @@ class DeleteListItemsRouteADME2ESpec "delete first of two child node and remaining child" in { val encodedNodeUrl = java.net.URLEncoder.encode("http://rdfh.ch/lists/0001/notUsedList0141", "utf-8") val request = Delete(baseApiUrl + s"/admin/lists/" + encodedNodeUrl) ~> addCredentials( - BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password)) + BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val node = AkkaHttpUtils.httpResponseToJson(response).fields("node").convertTo[ListNodeADM] @@ -115,7 +117,8 @@ class DeleteListItemsRouteADME2ESpec "delete a middle node and shift its siblings" in { val encodedNodeUrl = java.net.URLEncoder.encode("http://rdfh.ch/lists/0001/notUsedList02", "utf-8") val request = Delete(baseApiUrl + s"/admin/lists/" + encodedNodeUrl) ~> addCredentials( - BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password)) + BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val node = AkkaHttpUtils.httpResponseToJson(response).fields("node").convertTo[ListNodeADM] @@ -145,7 +148,8 @@ class DeleteListItemsRouteADME2ESpec "delete the single child of a node" in { val encodedNodeUrl = java.net.URLEncoder.encode("http://rdfh.ch/lists/0001/notUsedList031", "utf-8") val request = Delete(baseApiUrl + s"/admin/lists/" + encodedNodeUrl) ~> addCredentials( - BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password)) + BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val node = AkkaHttpUtils.httpResponseToJson(response).fields("node").convertTo[ListNodeADM] @@ -158,7 +162,8 @@ class DeleteListItemsRouteADME2ESpec "delete a list entirely with all its children" in { val encodedNodeUrl = java.net.URLEncoder.encode("http://rdfh.ch/lists/0001/notUsedList", "utf-8") val request = Delete(baseApiUrl + s"/admin/lists/" + encodedNodeUrl) ~> addCredentials( - BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password)) + BasicHttpCredentials(anythingAdminUserCreds.user.email, anythingAdminUserCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) val deletedStatus = AkkaHttpUtils.httpResponseToJson(response).fields("deleted") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala index 8b54138b8b..8f1625de95 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala @@ -46,8 +46,8 @@ object NewListsRouteADMFeatureE2ESpec { } /** - * End-to-End (E2E) test specification for testing new lists endpoint. - */ + * End-to-End (E2E) test specification for testing new lists endpoint. + */ class NewListsRouteADMFeatureE2ESpec extends E2ESpec(NewListsRouteADMFeatureE2ESpec.config) with SessionJsonProtocol @@ -91,15 +91,14 @@ class NewListsRouteADMFeatureE2ESpec private val treeListNodes: Seq[ListChildNodeADM] = SharedListsTestDataADM.treeListChildNodes private val customChildNodeIRI = "http://rdfh.ch/lists/0001/JbKZ-L_i5rTwHlv4dSNp4A" - def addChildListNodeRequest(parentNodeIri: IRI, name: String, label: String, comment: String): String = { + def addChildListNodeRequest(parentNodeIri: IRI, name: String, label: String, comment: String): String = s"""{ - | "parentNodeIri": "$parentNodeIri", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "$name", - | "labels": [{ "value": "$label", "language": "en"}], - | "comments": [{ "value": "$comment", "language": "en"}] - |}""".stripMargin - } + | "parentNodeIri": "$parentNodeIri", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "$name", + | "labels": [{ "value": "$label", "language": "en"}], + | "comments": [{ "value": "$comment", "language": "en"}] + |}""".stripMargin "The Lists Route (/admin/lists)" when { @@ -108,7 +107,8 @@ class NewListsRouteADMFeatureE2ESpec "return all lists" in { val request = Get(baseApiUrl + s"/admin/lists") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") @@ -136,7 +136,8 @@ class NewListsRouteADMFeatureE2ESpec "return all lists belonging to the images project" in { val request = Get(baseApiUrl + s"/admin/lists?projectIri=http%3A%2F%2Frdfh.ch%2Fprojects%2F00FF") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - rootCreds.basicHttpCredentials) + rootCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -164,7 +165,8 @@ class NewListsRouteADMFeatureE2ESpec "return all lists belonging to the anything project" in { val request = Get(baseApiUrl + s"/admin/lists?projectIri=http%3A%2F%2Frdfh.ch%2Fprojects%2F0001") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - rootCreds.basicHttpCredentials) + rootCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -192,7 +194,8 @@ class NewListsRouteADMFeatureE2ESpec "return basic list information" in { val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList/info") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - rootCreds.basicHttpCredentials) + rootCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -220,7 +223,8 @@ class NewListsRouteADMFeatureE2ESpec "return a complete list" in { val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - rootCreds.basicHttpCredentials) + rootCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") @@ -245,7 +249,8 @@ class NewListsRouteADMFeatureE2ESpec "return node info without children" in { val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList01/info") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - rootCreds.basicHttpCredentials) + rootCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -273,7 +278,8 @@ class NewListsRouteADMFeatureE2ESpec "return a complete node with children" in { val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList03") .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - rootCreds.basicHttpCredentials) + rootCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -300,11 +306,11 @@ class NewListsRouteADMFeatureE2ESpec "create a list with the provided custom Iri" in { val createListWithCustomIriRequest: String = s"""{ - | "id": "${SharedTestDataADM.customListIRI}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "New list with a custom IRI", "language": "en"}], - | "comments": [] - |}""".stripMargin + | "id": "${SharedTestDataADM.customListIRI}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "New list with a custom IRI", "language": "en"}], + | "comments": [] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -316,10 +322,13 @@ class NewListsRouteADMFeatureE2ESpec text = createListWithCustomIriRequest ) ) - val request = Post(baseApiUrl + s"/admin/lists", - HttpEntity(ContentTypes.`application/json`, createListWithCustomIriRequest)) + val request = Post( + baseApiUrl + s"/admin/lists", + HttpEntity(ContentTypes.`application/json`, createListWithCustomIriRequest) + ) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -349,17 +358,18 @@ class NewListsRouteADMFeatureE2ESpec // duplicate list IRI val params = s""" - |{ - | "id": "${SharedTestDataADM.customListIRI}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "New List", "language": "en"}], - | "comments": [] - |} + |{ + | "id": "${SharedTestDataADM.customListIRI}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "New List", "language": "en"}], + | "comments": [] + |} """.stripMargin val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -373,13 +383,13 @@ class NewListsRouteADMFeatureE2ESpec val createChildNodeWithCustomIriRequest = s""" - |{ "id": "$customChildNodeIRI", - | "parentNodeIri": "${SharedTestDataADM.customListIRI}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "node with a custom IRI", - | "labels": [{ "value": "New List Node", "language": "en"}], - | "comments": [] - |}""".stripMargin + |{ "id": "$customChildNodeIRI", + | "parentNodeIri": "${SharedTestDataADM.customListIRI}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "node with a custom IRI", + | "labels": [{ "value": "New List Node", "language": "en"}], + | "comments": [] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -392,10 +402,13 @@ class NewListsRouteADMFeatureE2ESpec ) ) - val request = Post(baseApiUrl + s"/admin/lists", - HttpEntity(ContentTypes.`application/json`, createChildNodeWithCustomIriRequest)) + val request = Post( + baseApiUrl + s"/admin/lists", + HttpEntity(ContentTypes.`application/json`, createChildNodeWithCustomIriRequest) + ) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -433,10 +446,10 @@ class NewListsRouteADMFeatureE2ESpec "create a list" in { val createListRequest: String = s"""{ - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |}""".stripMargin + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -450,7 +463,8 @@ class NewListsRouteADMFeatureE2ESpec ) val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, createListRequest)) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -487,16 +501,17 @@ class NewListsRouteADMFeatureE2ESpec "return a ForbiddenException if the user creating the list is not project or system admin" in { val params = s""" - |{ - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingUserCreds.basicHttpCredentials) + anythingUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.Forbidden) @@ -507,11 +522,11 @@ class NewListsRouteADMFeatureE2ESpec // no project IRI val params01 = s""" - |{ - | "projectIri": "", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val request01 = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params01)) @@ -523,11 +538,11 @@ class NewListsRouteADMFeatureE2ESpec // invalid project IRI val params02 = s""" - |{ - | "projectIri": "notvalidIRI", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "notvalidIRI", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val request02 = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params02)) @@ -539,11 +554,11 @@ class NewListsRouteADMFeatureE2ESpec // missing label val params03 = s""" - |{ - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin val request03 = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params03)) @@ -558,11 +573,11 @@ class NewListsRouteADMFeatureE2ESpec val updateListInfo: String = s"""{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue geänderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |}""".stripMargin + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue geänderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -576,10 +591,13 @@ class NewListsRouteADMFeatureE2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, updateListInfo)) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateListInfo) + ) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -610,10 +628,10 @@ class NewListsRouteADMFeatureE2ESpec "update basic list information with a new name" in { val updateListName = s"""{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "a totally new name" - |}""".stripMargin + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "a totally new name" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -627,10 +645,13 @@ class NewListsRouteADMFeatureE2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, updateListName)) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateListName) + ) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -658,19 +679,19 @@ class NewListsRouteADMFeatureE2ESpec val updateListInfoWithRepeatedCommentAndLabelValuesRequest: String = s"""{ - | "listIri": "http://rdfh.ch/lists/0001/treeList", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [ - | {"language": "en", "value": "Test List"}, - | {"language": "se", "value": "Test List"} - | ], - | "comments": [ - | {"language": "en", "value": "test"}, - | {"language": "de", "value": "test"}, - | {"language": "fr", "value": "test"}, - | {"language": "it", "value": "test"} - | ] - |}""".stripMargin + | "listIri": "http://rdfh.ch/lists/0001/treeList", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [ + | {"language": "en", "value": "Test List"}, + | {"language": "se", "value": "Test List"} + | ], + | "comments": [ + | {"language": "en", "value": "test"}, + | {"language": "de", "value": "test"}, + | {"language": "fr", "value": "test"}, + | {"language": "it", "value": "test"} + | ] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -685,11 +706,13 @@ class NewListsRouteADMFeatureE2ESpec val encodedListUrl = java.net.URLEncoder.encode("http://rdfh.ch/lists/0001/treeList", "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, - updateListInfoWithRepeatedCommentAndLabelValuesRequest)) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateListInfoWithRepeatedCommentAndLabelValuesRequest) + ) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -720,20 +743,21 @@ class NewListsRouteADMFeatureE2ESpec "return a ForbiddenException if the user updating the list is not project or system admin" in { val params = s""" - |{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params)) - .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingUserCreds.basicHttpCredentials) + val request = + Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params)) + .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( + anythingUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.Forbidden) @@ -746,18 +770,19 @@ class NewListsRouteADMFeatureE2ESpec // empty list IRI val params01 = s""" - |{ - | "listIri": "", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin - val request01 = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params01)) - .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request01 = + Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params01)) + .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( + anythingAdminUserCreds.basicHttpCredentials + ) val response01: HttpResponse = singleAwaitingRequest(request01) // log.debug(s"response: ${response.toString}") response01.status should be(StatusCodes.BadRequest) @@ -765,18 +790,19 @@ class NewListsRouteADMFeatureE2ESpec // empty project val params02 = s""" - |{ - | "listIri": "${newListIri.get}", - | "projectIri": "", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "${newListIri.get}", + | "projectIri": "", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin - val request02 = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params02)) - .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request02 = + Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params02)) + .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( + anythingAdminUserCreds.basicHttpCredentials + ) val response02: HttpResponse = singleAwaitingRequest(request02) // log.debug(s"response: ${response.toString}") response02.status should be(StatusCodes.BadRequest) @@ -784,18 +810,19 @@ class NewListsRouteADMFeatureE2ESpec // empty parameters val params03 = s""" - |{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin - val request03 = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params03)) - .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request03 = + Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params03)) + .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( + anythingAdminUserCreds.basicHttpCredentials + ) val response03: HttpResponse = singleAwaitingRequest(request03) // log.debug(s"response: ${response.toString}") response03.status should be(StatusCodes.BadRequest) @@ -827,7 +854,8 @@ class NewListsRouteADMFeatureE2ESpec val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, addChildToRoot)) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -893,10 +921,11 @@ class NewListsRouteADMFeatureE2ESpec text = addSecondChildToRoot ) ) - val request = Post(baseApiUrl + s"/admin/lists", - HttpEntity(ContentTypes.`application/json`, addSecondChildToRoot)) - .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = + Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, addSecondChildToRoot)) + .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -964,10 +993,11 @@ class NewListsRouteADMFeatureE2ESpec text = addChildToSecondChild ) ) - val request = Post(baseApiUrl + s"/admin/lists", - HttpEntity(ContentTypes.`application/json`, addChildToSecondChild)) - .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = + Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, addChildToSecondChild)) + .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -1016,10 +1046,10 @@ class NewListsRouteADMFeatureE2ESpec val newName = "modified third child" val updateNodeName = s"""{ - | "listIri": "$customChildNodeIRI", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "${newName}" - |}""".stripMargin + | "listIri": "$customChildNodeIRI", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "${newName}" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1034,10 +1064,13 @@ class NewListsRouteADMFeatureE2ESpec val encodedListUrl = java.net.URLEncoder.encode(customChildNodeIRI, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, updateNodeName)) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) .addHeader(RawHeader(FeatureToggle.REQUEST_HEADER, "new-list-admin-routes:1=on")) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala index 157ed38521..564fac404b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala @@ -45,8 +45,8 @@ object OldListsRouteADMFeatureE2ESpec { } /** - * End-to-End (E2E) test specification for testing lists endpoint. - */ + * End-to-End (E2E) test specification for testing lists endpoint. + */ class OldListsRouteADMFeatureE2ESpec extends E2ESpec(OldListsRouteADMFeatureE2ESpec.config) with SessionJsonProtocol @@ -89,23 +89,22 @@ class OldListsRouteADMFeatureE2ESpec private val treeListInfo: ListRootNodeInfoADM = SharedListsTestDataADM.treeListInfo private val treeListNodes: Seq[ListChildNodeADM] = SharedListsTestDataADM.treeListChildNodes private val customChildNodeIRI = "http://rdfh.ch/lists/0001/JbKZ-L_i5rTwHlv4dSNp4A" - def addChildListNodeRequest(parentNodeIri: IRI, name: String, label: String, comment: String): String = { + def addChildListNodeRequest(parentNodeIri: IRI, name: String, label: String, comment: String): String = s"""{ - | "parentNodeIri": "$parentNodeIri", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "$name", - | "labels": [{ "value": "$label", "language": "en"}], - | "comments": [{ "value": "$comment", "language": "en"}] - |}""".stripMargin - } + | "parentNodeIri": "$parentNodeIri", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "$name", + | "labels": [{ "value": "$label", "language": "en"}], + | "comments": [{ "value": "$comment", "language": "en"}] + |}""".stripMargin "The Lists Route (/admin/lists)" when { "used to query information about lists" should { "return all lists" in { - val request = Get(baseApiUrl + s"/admin/lists") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = + Get(baseApiUrl + s"/admin/lists") ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") @@ -131,8 +130,9 @@ class OldListsRouteADMFeatureE2ESpec } "return all lists belonging to the images project" in { - val request = Get(baseApiUrl + s"/admin/lists?projectIri=http%3A%2F%2Frdfh.ch%2Fprojects%2F00FF") ~> addCredentials( - rootCreds.basicHttpCredentials) + val request = Get( + baseApiUrl + s"/admin/lists?projectIri=http%3A%2F%2Frdfh.ch%2Fprojects%2F00FF" + ) ~> addCredentials(rootCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -158,8 +158,9 @@ class OldListsRouteADMFeatureE2ESpec } "return all lists belonging to the anything project" in { - val request = Get(baseApiUrl + s"/admin/lists?projectIri=http%3A%2F%2Frdfh.ch%2Fprojects%2F0001") ~> addCredentials( - rootCreds.basicHttpCredentials) + val request = Get( + baseApiUrl + s"/admin/lists?projectIri=http%3A%2F%2Frdfh.ch%2Fprojects%2F0001" + ) ~> addCredentials(rootCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -185,8 +186,9 @@ class OldListsRouteADMFeatureE2ESpec } "return basic list information" in { - val request = Get(baseApiUrl + s"/admin/lists/infos/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList") ~> addCredentials( - rootCreds.basicHttpCredentials) + val request = Get( + baseApiUrl + s"/admin/lists/infos/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList" + ) ~> addCredentials(rootCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -212,8 +214,9 @@ class OldListsRouteADMFeatureE2ESpec } "return a complete list" in { - val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList") ~> addCredentials( - rootCreds.basicHttpCredentials) + val request = Get( + baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList" + ) ~> addCredentials(rootCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") @@ -236,8 +239,9 @@ class OldListsRouteADMFeatureE2ESpec } "return node info without children" in { - val request = Get(baseApiUrl + s"/admin/lists/nodes/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList01") ~> addCredentials( - rootCreds.basicHttpCredentials) + val request = Get( + baseApiUrl + s"/admin/lists/nodes/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList01" + ) ~> addCredentials(rootCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") @@ -263,8 +267,9 @@ class OldListsRouteADMFeatureE2ESpec } "return a complete node with children" in { - val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList03") ~> addCredentials( - rootCreds.basicHttpCredentials) + val request = Get( + baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList03" + ) ~> addCredentials(rootCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -291,11 +296,11 @@ class OldListsRouteADMFeatureE2ESpec "create a list with the provided custom Iri" in { val createListWithCustomIriRequest: String = s"""{ - | "id": "${SharedTestDataADM.customListIRI}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "New list with a custom IRI", "language": "en"}], - | "comments": [] - |}""".stripMargin + | "id": "${SharedTestDataADM.customListIRI}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "New list with a custom IRI", "language": "en"}], + | "comments": [] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -309,8 +314,8 @@ class OldListsRouteADMFeatureE2ESpec ) val request = Post( baseApiUrl + s"/admin/lists", - HttpEntity(ContentTypes.`application/json`, createListWithCustomIriRequest)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + HttpEntity(ContentTypes.`application/json`, createListWithCustomIriRequest) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -340,16 +345,18 @@ class OldListsRouteADMFeatureE2ESpec // duplicate list IRI val params = s""" - |{ - | "id": "${SharedTestDataADM.customListIRI}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "New List", "language": "en"}], - | "comments": [] - |} + |{ + | "id": "${SharedTestDataADM.customListIRI}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "New List", "language": "en"}], + | "comments": [] + |} """.stripMargin - val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = + Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + anythingAdminUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -363,13 +370,13 @@ class OldListsRouteADMFeatureE2ESpec val createChildNodeWithCustomIriRequest = s""" - |{ "id": "$customChildNodeIRI", - | "parentNodeIri": "${SharedTestDataADM.customListIRI}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "node with a custom IRI", - | "labels": [{ "value": "New List Node", "language": "en"}], - | "comments": [] - |}""".stripMargin + |{ "id": "$customChildNodeIRI", + | "parentNodeIri": "${SharedTestDataADM.customListIRI}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "node with a custom IRI", + | "labels": [{ "value": "New List Node", "language": "en"}], + | "comments": [] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -386,8 +393,8 @@ class OldListsRouteADMFeatureE2ESpec val request = Post( baseApiUrl + s"/admin/lists/" + encodedParentNodeUrl, - HttpEntity(ContentTypes.`application/json`, createChildNodeWithCustomIriRequest)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + HttpEntity(ContentTypes.`application/json`, createChildNodeWithCustomIriRequest) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -425,10 +432,10 @@ class OldListsRouteADMFeatureE2ESpec "create a list" in { val createListRequest: String = s"""{ - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |}""".stripMargin + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -440,8 +447,10 @@ class OldListsRouteADMFeatureE2ESpec text = createListRequest ) ) - val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, createListRequest)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Post( + baseApiUrl + s"/admin/lists", + HttpEntity(ContentTypes.`application/json`, createListRequest) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -478,15 +487,17 @@ class OldListsRouteADMFeatureE2ESpec "return a ForbiddenException if the user creating the list is not project or system admin" in { val params = s""" - |{ - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin - val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - anythingUserCreds.basicHttpCredentials) + val request = + Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + anythingUserCreds.basicHttpCredentials + ) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.Forbidden) @@ -497,11 +508,11 @@ class OldListsRouteADMFeatureE2ESpec // no project IRI val params01 = s""" - |{ - | "projectIri": "", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val request01 = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params01)) @@ -512,11 +523,11 @@ class OldListsRouteADMFeatureE2ESpec // invalid project IRI val params02 = s""" - |{ - | "projectIri": "notvalidIRI", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "notvalidIRI", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val request02 = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params02)) @@ -527,11 +538,11 @@ class OldListsRouteADMFeatureE2ESpec // missing label val params03 = s""" - |{ - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin val request03 = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params03)) @@ -545,11 +556,11 @@ class OldListsRouteADMFeatureE2ESpec val updateListInfo: String = s"""{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue geänderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |}""".stripMargin + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue geänderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -563,9 +574,10 @@ class OldListsRouteADMFeatureE2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, updateListInfo)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateListInfo) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -596,10 +608,10 @@ class OldListsRouteADMFeatureE2ESpec "update basic list information with a new name" in { val updateListName = s"""{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "a totally new name" - |}""".stripMargin + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "a totally new name" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( filePath = TestDataFilePath( @@ -612,9 +624,10 @@ class OldListsRouteADMFeatureE2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, updateListName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateListName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -642,19 +655,19 @@ class OldListsRouteADMFeatureE2ESpec val updateListInfoWithRepeatedCommentAndLabelValuesRequest: String = s"""{ - | "listIri": "http://rdfh.ch/lists/0001/treeList", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [ - | {"language": "en", "value": "Test List"}, - | {"language": "se", "value": "Test List"} - | ], - | "comments": [ - | {"language": "en", "value": "test"}, - | {"language": "de", "value": "test"}, - | {"language": "fr", "value": "test"}, - | {"language": "it", "value": "test"} - | ] - |}""".stripMargin + | "listIri": "http://rdfh.ch/lists/0001/treeList", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [ + | {"language": "en", "value": "Test List"}, + | {"language": "se", "value": "Test List"} + | ], + | "comments": [ + | {"language": "en", "value": "test"}, + | {"language": "de", "value": "test"}, + | {"language": "fr", "value": "test"}, + | {"language": "it", "value": "test"} + | ] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -669,10 +682,10 @@ class OldListsRouteADMFeatureE2ESpec val encodedListUrl = java.net.URLEncoder.encode("http://rdfh.ch/lists/0001/treeList", "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, - updateListInfoWithRepeatedCommentAndLabelValuesRequest)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateListInfoWithRepeatedCommentAndLabelValuesRequest) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -703,19 +716,20 @@ class OldListsRouteADMFeatureE2ESpec "return a ForbiddenException if the user updating the list is not project or system admin" in { val params = s""" - |{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - anythingUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(anythingUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.Forbidden) @@ -728,17 +742,18 @@ class OldListsRouteADMFeatureE2ESpec // empty list IRI val params01 = s""" - |{ - | "listIri": "", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin - val request01 = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request01 = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, params01) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response01: HttpResponse = singleAwaitingRequest(request01) // log.debug(s"response: ${response.toString}") response01.status should be(StatusCodes.BadRequest) @@ -746,17 +761,18 @@ class OldListsRouteADMFeatureE2ESpec // empty project val params02 = s""" - |{ - | "listIri": "${newListIri.get}", - | "projectIri": "", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "${newListIri.get}", + | "projectIri": "", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin - val request02 = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request02 = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, params02) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response02: HttpResponse = singleAwaitingRequest(request02) // log.debug(s"response: ${response.toString}") response02.status should be(StatusCodes.BadRequest) @@ -764,17 +780,18 @@ class OldListsRouteADMFeatureE2ESpec // empty parameters val params03 = s""" - |{ - | "listIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "listIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin - val request03 = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, params03)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request03 = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, params03) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response03: HttpResponse = singleAwaitingRequest(request03) // log.debug(s"response: ${response.toString}") response03.status should be(StatusCodes.BadRequest) @@ -807,9 +824,10 @@ class OldListsRouteADMFeatureE2ESpec ) ) - val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, addChildToRoot)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Post( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, addChildToRoot) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -877,9 +895,10 @@ class OldListsRouteADMFeatureE2ESpec text = addSecondChildToRoot ) ) - val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, addSecondChildToRoot)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Post( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, addSecondChildToRoot) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -935,13 +954,13 @@ class OldListsRouteADMFeatureE2ESpec val insertChild = s"""{ - | "parentNodeIri": "${newListIri.get}", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "$name", - | "position": 1, - | "labels": [{ "value": "$label", "language": "en"}], - | "comments": [{ "value": "$comment", "language": "en"}] - |}""".stripMargin + | "parentNodeIri": "${newListIri.get}", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "$name", + | "position": 1, + | "labels": [{ "value": "$label", "language": "en"}], + | "comments": [{ "value": "$comment", "language": "en"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -953,9 +972,10 @@ class OldListsRouteADMFeatureE2ESpec text = insertChild ) ) - val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, insertChild)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Post( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, insertChild) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -1025,9 +1045,10 @@ class OldListsRouteADMFeatureE2ESpec text = addChildToSecondChild ) ) - val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, addChildToSecondChild)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Post( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, addChildToSecondChild) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // println(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -1077,10 +1098,10 @@ class OldListsRouteADMFeatureE2ESpec val newName = "modified third child" val updateNodeName = s"""{ - | "listIri": "$customChildNodeIRI", - | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", - | "name": "${newName}" - |}""".stripMargin + | "listIri": "$customChildNodeIRI", + | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", + | "name": "${newName}" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1095,9 +1116,10 @@ class OldListsRouteADMFeatureE2ESpec val encodedListUrl = java.net.URLEncoder.encode(customChildNodeIRI, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl, - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl, + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala index 6697990fa3..ec6826e64d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala @@ -42,8 +42,8 @@ object UpdateListItemsRouteADME2ESpec { } /** - * End-to-End (E2E) test specification for testing update node props routes. - */ + * End-to-End (E2E) test specification for testing update node props routes. + */ class UpdateListItemsRouteADME2ESpec extends E2ESpec(UpdateListItemsRouteADME2ESpec.config) with SessionJsonProtocol @@ -92,8 +92,8 @@ class UpdateListItemsRouteADME2ESpec "update only node name" in { val updateNodeName = s"""{ - | "name": "updated root node name" - |}""".stripMargin + | "name": "updated root node name" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( filePath = TestDataFilePath( @@ -106,9 +106,10 @@ class UpdateListItemsRouteADME2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(treeListInfo.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/name", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/name", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -134,8 +135,8 @@ class UpdateListItemsRouteADME2ESpec "update only node labels" in { val updateNodeLabels = s"""{ - | "labels": [{"language": "se", "value": "nya märkningen"}] - |}""".stripMargin + | "labels": [{"language": "se", "value": "nya märkningen"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( filePath = TestDataFilePath( @@ -148,9 +149,10 @@ class UpdateListItemsRouteADME2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(treeListInfo.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/labels", - HttpEntity(ContentTypes.`application/json`, updateNodeLabels)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/labels", + HttpEntity(ContentTypes.`application/json`, updateNodeLabels) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -178,8 +180,8 @@ class UpdateListItemsRouteADME2ESpec "update node comments" in { val updateCommentsLabels = s"""{ - | "comments": [{"language": "se", "value": "nya kommentarer"}] - |}""".stripMargin + | "comments": [{"language": "se", "value": "nya kommentarer"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( filePath = TestDataFilePath( @@ -192,9 +194,10 @@ class UpdateListItemsRouteADME2ESpec ) val encodedListUrl = java.net.URLEncoder.encode(treeListInfo.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/comments", - HttpEntity(ContentTypes.`application/json`, updateCommentsLabels)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/comments", + HttpEntity(ContentTypes.`application/json`, updateCommentsLabels) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -225,9 +228,10 @@ class UpdateListItemsRouteADME2ESpec |}""".stripMargin val encodedListUrl = java.net.URLEncoder.encode(treeListInfo.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/comments", - HttpEntity(ContentTypes.`application/json`, deleteCommentsLabels)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/comments", + HttpEntity(ContentTypes.`application/json`, deleteCommentsLabels) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) // log.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -246,8 +250,8 @@ class UpdateListItemsRouteADME2ESpec val newName = "updated third child name" val updateNodeName = s"""{ - | "name": "$newName" - |}""".stripMargin + | "name": "$newName" + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -262,9 +266,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(treeChildNode.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/name", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/name", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -288,8 +293,8 @@ class UpdateListItemsRouteADME2ESpec "update only the labels of the child node" in { val updateNodeLabels = s"""{ - | "labels": [{"language": "se", "value": "nya märkningen för nod"}] - |}""".stripMargin + | "labels": [{"language": "se", "value": "nya märkningen för nod"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -304,9 +309,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(treeChildNode.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/labels", - HttpEntity(ContentTypes.`application/json`, updateNodeLabels)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/labels", + HttpEntity(ContentTypes.`application/json`, updateNodeLabels) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -332,8 +338,8 @@ class UpdateListItemsRouteADME2ESpec "update only comments of the child node" in { val updateNodeComments = s"""{ - | "comments": [{"language": "se", "value": "nya kommentarer för nod"}] - |}""".stripMargin + | "comments": [{"language": "se", "value": "nya kommentarer för nod"}] + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -348,9 +354,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(treeChildNode.id, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/comments", - HttpEntity(ContentTypes.`application/json`, updateNodeComments)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/comments", + HttpEntity(ContentTypes.`application/json`, updateNodeComments) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -379,15 +386,16 @@ class UpdateListItemsRouteADME2ESpec val nodeIri = "invalid-iri" val updateNodeName = s"""{ - | "parentNodeIri": "$parentIri", - | "position": $newPosition - |}""".stripMargin + | "parentNodeIri": "$parentIri", + | "position": $newPosition + |}""".stripMargin val encodedListUrl = java.net.URLEncoder.encode(nodeIri, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.BadRequest) @@ -399,9 +407,9 @@ class UpdateListItemsRouteADME2ESpec val nodeIri = "http://rdfh.ch/lists/0001/notUsedList014" val updateNodeName = s"""{ - | "parentNodeIri": "$parentIri", - | "position": $newPosition - |}""".stripMargin + | "parentNodeIri": "$parentIri", + | "position": $newPosition + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -416,9 +424,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(nodeIri, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -444,9 +453,9 @@ class UpdateListItemsRouteADME2ESpec val nodeIri = "http://rdfh.ch/lists/0001/notUsedList012" val updateNodeName = s"""{ - | "parentNodeIri": "$parentIri", - | "position": $newPosition - |}""".stripMargin + | "parentNodeIri": "$parentIri", + | "position": $newPosition + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -461,9 +470,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(nodeIri, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -489,9 +499,9 @@ class UpdateListItemsRouteADME2ESpec val nodeIri = "http://rdfh.ch/lists/0001/notUsedList015" val updateNodeName = s"""{ - | "parentNodeIri": "$parentIri", - | "position": $newPosition - |}""".stripMargin + | "parentNodeIri": "$parentIri", + | "position": $newPosition + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -506,9 +516,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(nodeIri, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) @@ -534,9 +545,9 @@ class UpdateListItemsRouteADME2ESpec val nodeIri = "http://rdfh.ch/lists/0001/notUsedList015" val updateNodeName = s"""{ - | "parentNodeIri": "$parentIri", - | "position": $newPosition - |}""".stripMargin + | "parentNodeIri": "$parentIri", + | "position": $newPosition + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -551,9 +562,10 @@ class UpdateListItemsRouteADME2ESpec val encodedListUrl = java.net.URLEncoder.encode(nodeIri, "utf-8") - val request = Put(baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", - HttpEntity(ContentTypes.`application/json`, updateNodeName)) ~> addCredentials( - anythingAdminUserCreds.basicHttpCredentials) + val request = Put( + baseApiUrl + s"/admin/lists/" + encodedListUrl + "/position", + HttpEntity(ContentTypes.`application/json`, updateNodeName) + ) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials) val response: HttpResponse = singleAwaitingRequest(request) response.status should be(StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala index 6d0c232bb4..97ee92415d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala @@ -34,8 +34,8 @@ object ServerVersionE2ESpec { } /** - * End-to-End (E2E) test specification for testing the server response. - */ + * End-to-End (E2E) test specification for testing the server response. + */ class ServerVersionE2ESpec extends E2ESpec(ServerVersionE2ESpec.config) { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala index 3275ef3145..c488f357a3 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala @@ -42,10 +42,10 @@ object AuthenticationV1E2ESpec { } /** - * End-to-End (E2E) test specification for testing authentication. - * - * This spec tests the 'v1/authentication' and 'v1/session' route. - */ + * End-to-End (E2E) test specification for testing authentication. + * + * This spec tests the 'v1/authentication' and 'v1/session' route. + */ class AuthenticationV1E2ESpec extends E2ESpec(AuthenticationV1E2ESpec.config) with SessionJsonProtocol @@ -123,11 +123,16 @@ class AuthenticationV1E2ESpec assert( response.headers.contains( `Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - value = sid, - domain = Some(settings.cookieDomain), - path = Some("/"), - httpOnly = true)))) + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + value = sid, + domain = Some(settings.cookieDomain), + path = Some("/"), + httpOnly = true + ) + ) + ) + ) /* check for sensitive information leakage */ val body: String = Await.result(Unmarshal(response.entity).to[String], 1.seconds) @@ -165,12 +170,17 @@ class AuthenticationV1E2ESpec assert( response.headers.contains( `Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - "", - domain = Some(settings.cookieDomain), - path = Some("/"), - httpOnly = true, - expires = Some(DateTime(1970, 1, 1, 0, 0, 0)))))) + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + "", + domain = Some(settings.cookieDomain), + path = Some("/"), + httpOnly = true, + expires = Some(DateTime(1970, 1, 1, 0, 0, 0)) + ) + ) + ) + ) } "fail authentication with provided session cookie after logout" in { @@ -219,11 +229,16 @@ class AuthenticationV1E2ESpec assert( response.headers.contains( `Set-Cookie`( - HttpCookie(KNORA_AUTHENTICATION_COOKIE_NAME, - value = sid, - domain = Some(settings.cookieDomain), - path = Some("/"), - httpOnly = true)))) + HttpCookie( + KNORA_AUTHENTICATION_COOKIE_NAME, + value = sid, + domain = Some(settings.cookieDomain), + path = Some("/"), + httpOnly = true + ) + ) + ) + ) /* check for sensitive information leakage */ val body: String = Await.result(Unmarshal(response.entity).to[String], 1.seconds) @@ -280,8 +295,8 @@ class AuthenticationV1E2ESpec "succeed authentication using HTTP Basic Auth headers with correct username and correct password " in { /* Correct email / correct password */ - val request = Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response = singleAwaitingRequest(request) //log.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.OK) @@ -289,8 +304,8 @@ class AuthenticationV1E2ESpec "fail authentication using HTTP Basic Auth headers with correct username and wrong password " in { /* Correct email / wrong password */ - val request = Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials( - BasicHttpCredentials(rootEmail, wrongPass)) + val request = + Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials(BasicHttpCredentials(rootEmail, wrongPass)) val response = singleAwaitingRequest(request) //log.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.Unauthorized) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ErrorV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ErrorV1E2ESpec.scala index bbfe108ee4..78557d1575 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ErrorV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ErrorV1E2ESpec.scala @@ -7,8 +7,8 @@ import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtoc import scala.concurrent.duration._ /** - * Causes an internal server error to see if logging is working correctly. - */ + * Causes an internal server error to see if logging is working correctly. + */ class ErrorV1E2ESpec extends E2ESpec with TriplestoreJsonProtocol { "Make a request that causes an internal server error (unit type message)" in { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala index 9d078c8a8a..22ae507ab4 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala @@ -39,8 +39,8 @@ object ListsV1E2ESpec { } /** - * End-to-End (E2E) test specification for testing users endpoint. - */ + * End-to-End (E2E) test specification for testing users endpoint. + */ class ListsV1E2ESpec extends E2ESpec(ListsV1E2ESpec.config) with SessionJsonProtocol @@ -97,7 +97,7 @@ class ListsV1E2ESpec val listInfos: Seq[ListInfoV1] = AkkaHttpUtils.httpResponseToJson(response).fields("lists").convertTo[Seq[ListInfoV1]] listInfos.size should be (6) - */ + */ } } } @@ -115,7 +115,7 @@ class ListsV1E2ESpec val listInfos: Seq[ListInfoV1] = AkkaHttpUtils.httpResponseToJson(response).fields("lists").convertTo[Seq[ListInfoV1]] listInfos.size should be (6) - */ + */ } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala index b6753c08e3..dee9fb2aed 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala @@ -34,8 +34,8 @@ object PermissionsHandlingV1E2ESpec { } /** - * End-to-end test specification for testing the handling of permissions. - */ + * End-to-end test specification for testing the handling of permissions. + */ class PermissionsHandlingV1E2ESpec extends E2ESpec(PermissionsHandlingV1E2ESpec.config) with TriplestoreJsonProtocol { private val rootUser = SharedTestDataV1.rootUser @@ -61,19 +61,21 @@ class PermissionsHandlingV1E2ESpec extends E2ESpec(PermissionsHandlingV1E2ESpec. val params = """ - |{ - | "restype_id": "http://www.knora.org/ontology/00FF/images#person", - | "label": "Testperson", - | "project_id": "http://rdfh.ch/projects/00FF", - | "properties": { - | "http://www.knora.org/ontology/00FF/images#lastname": [{"richtext_value":{"utf8str":"Testname"}}], - | "http://www.knora.org/ontology/00FF/images#firstname": [{"richtext_value":{"utf8str":"Name"}}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/00FF/images#person", + | "label": "Testperson", + | "project_id": "http://rdfh.ch/projects/00FF", + | "properties": { + | "http://www.knora.org/ontology/00FF/images#lastname": [{"richtext_value":{"utf8str":"Testname"}}], + | "http://www.knora.org/ontology/00FF/images#firstname": [{"richtext_value":{"utf8str":"Name"}}] + | } + |} """.stripMargin - val request = Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(imagesUserEmail, password)) + val request = + Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(imagesUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -84,19 +86,21 @@ class PermissionsHandlingV1E2ESpec extends E2ESpec(PermissionsHandlingV1E2ESpec. val params = """ - |{ - | "restype_id": "http://www.knora.org/ontology/00FF/images#person", - | "label": "Testperson", - | "project_id": "http://rdfh.ch/projects/00FF", - | "properties": { - | "http://www.knora.org/ontology/00FF/images#lastname": [{"richtext_value":{"utf8str":"Testname"}}], - | "http://www.knora.org/ontology/00FF/images#firstname": [{"richtext_value":{"utf8str":"Name"}}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/00FF/images#person", + | "label": "Testperson", + | "project_id": "http://rdfh.ch/projects/00FF", + | "properties": { + | "http://www.knora.org/ontology/00FF/images#lastname": [{"richtext_value":{"utf8str":"Testname"}}], + | "http://www.knora.org/ontology/00FF/images#firstname": [{"richtext_value":{"utf8str":"Name"}}] + | } + |} """.stripMargin - val request = Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(rootUserEmail, password)) + val request = + Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(rootUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) } @@ -104,19 +108,21 @@ class PermissionsHandlingV1E2ESpec extends E2ESpec(PermissionsHandlingV1E2ESpec. val params = """ - |{ - | "restype_id": "http://www.knora.org/ontology/00FF/images#person", - | "label": "Testperson", - | "project_id": "http://rdfh.ch/projects/00FF", - | "properties": { - | "http://www.knora.org/ontology/00FF/images#lastname": [{"richtext_value":{"utf8str":"Testname"}}], - | "http://www.knora.org/ontology/00FF/images#firstname": [{"richtext_value":{"utf8str":"Name"}}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/00FF/images#person", + | "label": "Testperson", + | "project_id": "http://rdfh.ch/projects/00FF", + | "properties": { + | "http://www.knora.org/ontology/00FF/images#lastname": [{"richtext_value":{"utf8str":"Testname"}}], + | "http://www.knora.org/ontology/00FF/images#firstname": [{"richtext_value":{"utf8str":"Name"}}] + | } + |} """.stripMargin - val request = Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) + val request = + Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(incunabulaUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala index 038b4e0650..109ec1fd8a 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala @@ -41,8 +41,8 @@ object ProjectsV1E2ESpec { } /** - * End-to-End (E2E) test specification for testing groups endpoint. - */ + * End-to-End (E2E) test specification for testing groups endpoint. + */ class ProjectsV1E2ESpec extends E2ESpec(ProjectsV1E2ESpec.config) with SessionJsonProtocol @@ -79,8 +79,8 @@ class ProjectsV1E2ESpec } "return the information for a single project identified by iri" in { - val request = Get(baseApiUrl + s"/v1/projects/$projectIriEnc") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + val request = + Get(baseApiUrl + s"/v1/projects/$projectIriEnc") ~> addCredentials(BasicHttpCredentials(rootEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) // logger.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) @@ -88,7 +88,8 @@ class ProjectsV1E2ESpec "return the information for a single project identified by shortname" in { val request = Get(baseApiUrl + s"/v1/projects/$projectShortnameEnc?identifier=shortname") ~> addCredentials( - BasicHttpCredentials(rootEmail, testPass)) + BasicHttpCredentials(rootEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) // logger.debug(s"response: {}", response) assert(response.status === StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala index 3fa6caf073..aa0ad620f5 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala @@ -58,19 +58,19 @@ import scala.util.{Random, Try} import scala.xml.{Node, NodeSeq, XML} /** - * End-to-end test specification for the resources endpoint. - */ + * End-to-end test specification for the resources endpoint. + */ class ResourcesV1R2RSpec extends R2RSpec { override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin - private val resourcesPathV1 = DSPApiDirectives.handleErrors(system) { new ResourcesRouteV1(routeData).knoraApiPath } - private val resourcesPathV2 = DSPApiDirectives.handleErrors(system) { new ResourcesRouteV2(routeData).knoraApiPath } - private val valuesPathV1 = DSPApiDirectives.handleErrors(system) { new ValuesRouteV1(routeData).knoraApiPath } + private val resourcesPathV1 = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV1(routeData).knoraApiPath) + private val resourcesPathV2 = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) + private val valuesPathV1 = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) private val superUser = SharedTestDataADM.superUser private val superUserEmail = superUser.email @@ -100,12 +100,18 @@ class ResourcesV1R2RSpec extends R2RSpec { implicit val ec: ExecutionContextExecutor = system.dispatcher override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/ontologies/example-box.ttl", - name = "http://www.knora.org/ontology/shared/example-box"), - RdfDataObject(path = "test_data/ontologies/example-ibox.ttl", - name = "http://www.knora.org/ontology/shared/example-ibox"), - RdfDataObject(path = "test_data/ontologies/empty-thing-onto.ttl", - name = "http://www.knora.org/ontology/0001/empty-thing"), + RdfDataObject( + path = "test_data/ontologies/example-box.ttl", + name = "http://www.knora.org/ontology/shared/example-box" + ), + RdfDataObject( + path = "test_data/ontologies/example-ibox.ttl", + name = "http://www.knora.org/ontology/shared/example-ibox" + ), + RdfDataObject( + path = "test_data/ontologies/empty-thing-onto.ttl", + name = "http://www.knora.org/ontology/0001/empty-thing" + ), RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything"), RdfDataObject(path = "test_data/demo_data/images-demo-data.ttl", name = "http://www.knora.org/data/00FF/images"), RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula") @@ -139,128 +145,121 @@ class ResourcesV1R2RSpec extends R2RSpec { private val xml1 = """ - |Test
text
+ |Test
text
""".stripMargin private val xml2 = """ - |a new value + |a new value """.stripMargin private val xml3 = s""" - | - | This text links to Google and a Knora resource. - | + | + | This text links to Google and a Knora resource. + | """.stripMargin private val xml4 = s""" - | - | This text links to Google and a Knora resource and another Knora resource resource. - | + | + | This text links to Google and a Knora resource and another Knora resource resource. + | """.stripMargin /** - * Gets the field `res_id` from a JSON response to resource creation. - * - * @param response the response sent back from the API. - * @return the value of `res_id`. - */ - private def getResIriFromJsonResponse(response: HttpResponse) = { + * Gets the field `res_id` from a JSON response to resource creation. + * + * @param response the response sent back from the API. + * @return the value of `res_id`. + */ + private def getResIriFromJsonResponse(response: HttpResponse) = AkkaHttpUtils.httpResponseToJson(response).fields.get("res_id") match { case Some(JsString(resourceId)) => resourceId case None => throw InvalidApiJsonException(s"The response does not contain a field called 'res_id'") case other => throw InvalidApiJsonException(s"The response does not contain a res_id of type JsString, but $other") } - } /** - * Gets the field `id` from a JSON response to value creation (new value). - * - * @param response the response sent back from the API. - * @return the value of `res_id`. - */ - private def getNewValueIriFromJsonResponse(response: HttpResponse) = { + * Gets the field `id` from a JSON response to value creation (new value). + * + * @param response the response sent back from the API. + * @return the value of `res_id`. + */ + private def getNewValueIriFromJsonResponse(response: HttpResponse) = AkkaHttpUtils.httpResponseToJson(response).fields.get("id") match { case Some(JsString(resourceId)) => resourceId case None => throw InvalidApiJsonException(s"The response does not contain a field called 'res_id'") case other => throw InvalidApiJsonException(s"The response does not contain a res_id of type JsString, but $other") } - } /** - * Gets the given property's values from a resource full response. - * - * @param response the response to a resource full request. - * @param prop the given property IRI. - * @return the property's values. - */ - private def getValuesForProp(response: HttpResponse, prop: IRI): JsValue = { + * Gets the given property's values from a resource full response. + * + * @param response the response to a resource full request. + * @param prop the given property IRI. + * @return the property's values. + */ + private def getValuesForProp(response: HttpResponse, prop: IRI): JsValue = AkkaHttpUtils.httpResponseToJson(response).fields("props").asJsObject.fields(prop).asJsObject.fields("values") - } /** - * Gets the given property's comments from a resource full response. - * - * @param response the response to a resource full request. - * @param prop the given property IRI. - * @return the property's comments. - */ - private def getCommentsForProp(response: HttpResponse, prop: IRI): JsValue = { + * Gets the given property's comments from a resource full response. + * + * @param response the response to a resource full request. + * @param prop the given property IRI. + * @return the property's comments. + */ + private def getCommentsForProp(response: HttpResponse, prop: IRI): JsValue = AkkaHttpUtils.httpResponseToJson(response).fields("props").asJsObject.fields(prop).asJsObject.fields("comments") - } /** - * Creates a SPARQL query string to get the standoff links (direct links) for a given resource. - * - * @param resIri the resource whose standoff links are to be queried. - * @return SPARQL query string. - */ - private def getDirectLinksSPARQL(resIri: IRI): String = { + * Creates a SPARQL query string to get the standoff links (direct links) for a given resource. + * + * @param resIri the resource whose standoff links are to be queried. + * @return SPARQL query string. + */ + private def getDirectLinksSPARQL(resIri: IRI): String = s""" - |PREFIX knora-base: - | - |SELECT ?referredResourceIRI WHERE { - | BIND(IRI("$resIri") as ?resIRI) - | ?resIRI knora-base:hasStandoffLinkTo ?referredResourceIRI . - |} + |PREFIX knora-base: + | + |SELECT ?referredResourceIRI WHERE { + | BIND(IRI("$resIri") as ?resIRI) + | ?resIRI knora-base:hasStandoffLinkTo ?referredResourceIRI . + |} """.stripMargin - } /** - * Creates a SPARQL query to get the standoff links reifications to check for the target resource and the reference count. - * - * @param resIri the resource whose standoff reifications are to be queried. - * @return SPARQL query string. - */ - private def getRefCountsSPARQL(resIri: IRI): String = { + * Creates a SPARQL query to get the standoff links reifications to check for the target resource and the reference count. + * + * @param resIri the resource whose standoff reifications are to be queried. + * @return SPARQL query string. + */ + private def getRefCountsSPARQL(resIri: IRI): String = s""" - |PREFIX rdf: - |PREFIX knora-base: - | - |SELECT DISTINCT ?reificationIRI ?object ?refCnt WHERE { - | BIND(IRI("$resIri") as ?resIRI) - | ?resIRI knora-base:hasStandoffLinkToValue ?reificationIRI . - | ?reificationIRI rdf:object ?object . - | ?reificationIRI knora-base:valueHasRefCount ?refCnt . - |} + |PREFIX rdf: + |PREFIX knora-base: + | + |SELECT DISTINCT ?reificationIRI ?object ?refCnt WHERE { + | BIND(IRI("$resIri") as ?resIRI) + | ?resIRI knora-base:hasStandoffLinkToValue ?reificationIRI . + | ?reificationIRI rdf:object ?object . + | ?reificationIRI knora-base:valueHasRefCount ?refCnt . + |} """.stripMargin - } private val search = "/v1/resources?restype_id=http%3A%2F%2Fwww.knora.org%2Fontology%2F0001%2Fanything%23Thing" private val filter = "&searchstr=value*" /** - * Test the result of two subsequent requests nearly identical requests - * (used here for requesting different number of properties to be displayed) - * @param search : search query as a string - * @return : nothing, assert is called within this function - */ - private def checkSearchWithDifferentNumberOfProperties(search: String): Assertion = { - + * Test the result of two subsequent requests nearly identical requests + * (used here for requesting different number of properties to be displayed) + * @param search : search query as a string + * @return : nothing, assert is called within this function + */ + private def checkSearchWithDifferentNumberOfProperties(search: String): Assertion = Get(search) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -284,14 +283,15 @@ class ResourcesV1R2RSpec extends R2RSpec { assert(expectedLabels.subsetOf(labels)) } - } "The Resources Endpoint" should { "provide a HTML representation of the resource properties " in { /* Incunabula resources*/ /* A Book without a preview image */ - Get("/v1/resources.html/http%3A%2F%2Frdfh.ch%2F0803%2Fc5058f3a?noresedit=true&reqtype=properties") ~> resourcesPathV1 ~> check { + Get( + "/v1/resources.html/http%3A%2F%2Frdfh.ch%2F0803%2Fc5058f3a?noresedit=true&reqtype=properties" + ) ~> resourcesPathV1 ~> check { //log.debug("==>> " + responseAs[String]) assert(status === StatusCodes.OK) assert(responseAs[String] contains "Physical description") @@ -305,7 +305,9 @@ class ResourcesV1R2RSpec extends R2RSpec { } /* A Page with a preview image */ - Get("/v1/resources.html/http%3A%2F%2Frdfh.ch%2F0803%2Fde6c38ce3401?noresedit=true&reqtype=properties") ~> resourcesPathV1 ~> check { + Get( + "/v1/resources.html/http%3A%2F%2Frdfh.ch%2F0803%2Fde6c38ce3401?noresedit=true&reqtype=properties" + ) ~> resourcesPathV1 ~> check { //log.debug("==>> " + responseAs[String]) assert(status === StatusCodes.OK) assert(responseAs[String] contains "preview") @@ -316,7 +318,9 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the regions of a page when doing a context query with resinfo set to true" in { - Get("/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2F9d626dc76c03?resinfo=true&reqtype=context") ~> resourcesPathV1 ~> check { + Get( + "/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2F9d626dc76c03?resinfo=true&reqtype=context" + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -350,19 +354,20 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "$IMAGES_ONTOLOGY_IRI#person", - | "label": "Testperson", - | "project_id": "$IMAGES_PROJECT_IRI", - | "properties": { - | "$IMAGES_ONTOLOGY_IRI#lastname": [{"richtext_value":{"utf8str":"Testname"}}], - | "$IMAGES_ONTOLOGY_IRI#firstname": [{"richtext_value":{"utf8str":"Name"}}] - | } - |} + |{ + | "restype_id": "$IMAGES_ONTOLOGY_IRI#person", + | "label": "Testperson", + | "project_id": "$IMAGES_PROJECT_IRI", + | "properties": { + | "$IMAGES_ONTOLOGY_IRI#lastname": [{"richtext_value":{"utf8str":"Testname"}}], + | "$IMAGES_ONTOLOGY_IRI#firstname": [{"richtext_value":{"utf8str":"Name"}}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(imagesUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(imagesUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) } } @@ -371,7 +376,7 @@ class ResourcesV1R2RSpec extends R2RSpec { val expectedXML = """ - |

Derselbe Holzschnitt wird auf Seite c7r der lateinischen Ausgabe des Narrenschiffs verwendet.

+ |

Derselbe Holzschnitt wird auf Seite c7r der lateinischen Ausgabe des Narrenschiffs verwendet.

""".stripMargin Get("/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2F047db418ae06") ~> resourcesPathV1 ~> check { @@ -401,16 +406,17 @@ class ResourcesV1R2RSpec extends R2RSpec { val expectedXML1 = """ - |Na ja, die Dinge sind OK. + |Na ja, die Dinge sind OK. """.stripMargin val expectedXML2 = """ - |Ich liebe die Dinge, sie sind alles für mich. + |Ich liebe die Dinge, sie sind alles für mich. """.stripMargin Get("/v1/resources/http%3A%2F%2Frdfh.ch%2F0001%2Fa-thing-with-text-values") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -473,23 +479,23 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml": ${xml1.toJson.compactPrint}, "mapping_id": "$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}], - | "http://www.knora.org/ontology/0001/anything#hasTimeStamp": [{"time_value": "2019-08-28T15:13:10.968318Z"}], - | "http://www.knora.org/ontology/0001/anything#hasBoolean": [{"boolean_value":true}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml": ${xml1.toJson.compactPrint}, "mapping_id": "$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}], + | "http://www.knora.org/ontology/0001/anything#hasTimeStamp": [{"time_value": "2019-08-28T15:13:10.968318Z"}], + | "http://www.knora.org/ontology/0001/anything#hasBoolean": [{"boolean_value":true}] + | } + |} """.stripMargin // TODO: these properties have been commented out in the thing test ontology because of compatibility with the GUI @@ -497,7 +503,8 @@ class ResourcesV1R2RSpec extends R2RSpec { // "http://www.knora.org/ontology/0001/anything#hasGeometry": [{"geom_value":"{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.5516074450084602,\"y\":0.4444444444444444},{\"x\":0.2791878172588832,\"y\":0.5}],\"type\":\"rectangle\",\"original_index\":0}"}], Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val resId = getResIriFromJsonResponse(response) @@ -509,7 +516,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the created resource and check its standoff in the response" in { Get("/v1/resources/" + URLEncoder.encode(firstThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -536,19 +544,20 @@ class ResourcesV1R2RSpec extends R2RSpec { val newValueParams = s""" - |{ - | "project_id": "http://rdfh.ch/projects/0001", - | "res_id": "${firstThingIri.get}", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": { - | "xml": ${xml2.toJson.compactPrint}, - | "mapping_id": "$mappingIri" - | } - |} + |{ + | "project_id": "http://rdfh.ch/projects/0001", + | "res_id": "${firstThingIri.get}", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": { + | "xml": ${xml2.toJson.compactPrint}, + | "mapping_id": "$mappingIri" + | } + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -576,23 +585,24 @@ class ResourcesV1R2RSpec extends R2RSpec { val xml = s""" - |a new value with a standoff link + |a new value with a standoff link """.stripMargin val newValueParams = s""" - |{ - | "project_id": "http://rdfh.ch/projects/0001", - | "richtext_value": { - | "xml": ${xml.toJson.compactPrint}, - | "mapping_id": "$mappingIri" - | } - |} + |{ + | "project_id": "http://rdfh.ch/projects/0001", + | "richtext_value": { + | "xml": ${xml.toJson.compactPrint}, + | "mapping_id": "$mappingIri" + | } + |} """.stripMargin - Put("/v1/values/" + URLEncoder.encode(firstTextValueIRI.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPathV1 ~> check { + Put( + "/v1/values/" + URLEncoder.encode(firstTextValueIRI.get, "UTF-8"), + HttpEntity(ContentTypes.`application/json`, newValueParams) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -641,30 +651,31 @@ class ResourcesV1R2RSpec extends R2RSpec { val xml = s""" - |This text links to a thing + |This text links to a thing """.stripMargin val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id" :"$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id" :"$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -677,7 +688,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the second resource of type anything:Thing, containing the correct standoff link" in { Get("/v1/resources/" + URLEncoder.encode(secondThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val text: JsValue = getValuesForProp(response, "http://www.knora.org/ontology/0001/anything#hasText") @@ -708,7 +720,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the first thing resource that is referred to by the second thing resource" in { Get("/v1/resources/" + URLEncoder.encode(firstThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -743,8 +756,10 @@ class ResourcesV1R2RSpec extends R2RSpec { propIriJsString match { case JsString(pid) => - assert(pid == OntologyConstants.KnoraBase.HasStandoffLinkTo, - s"This resource should be referred to by ${OntologyConstants.KnoraBase.HasStandoffLinkTo}") + assert( + pid == OntologyConstants.KnoraBase.HasStandoffLinkTo, + s"This resource should be referred to by ${OntologyConstants.KnoraBase.HasStandoffLinkTo}" + ) case _ => throw InvalidApiJsonException("pid is not a JsString") } @@ -759,30 +774,31 @@ class ResourcesV1R2RSpec extends R2RSpec { // use a tag name that is not defined in the standard mapping ("trong" instead of "strong") val xml = """ - |This text + |This text """.stripMargin val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint}, "mapping_id": "$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint}, "mapping_id": "$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { // the route should reject the request because `trong` is not a tag name supported by the standard mapping assert(status == StatusCodes.BadRequest, response.toString) @@ -793,31 +809,32 @@ class ResourcesV1R2RSpec extends R2RSpec { val xml = s""" - |This text links to two things + |This text links to two things """.stripMargin val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id": "$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} - | + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id": "$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} + | """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { //println(response) @@ -830,35 +847,36 @@ class ResourcesV1R2RSpec extends R2RSpec { val firstXML = s""" - |This text links to a thing + |This text links to a thing """.stripMargin val secondXML = s""" - |This text links to the same thing twice and to another thing + |This text links to the same thing twice and to another thing """.stripMargin val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${firstXML.toJson.compactPrint},"mapping_id": "$mappingIri"}}, {"richtext_value":{"xml":${secondXML.toJson.compactPrint},"mapping_id": "$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${firstXML.toJson.compactPrint},"mapping_id": "$mappingIri"}}, {"richtext_value":{"xml":${secondXML.toJson.compactPrint},"mapping_id": "$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -918,8 +936,9 @@ class ResourcesV1R2RSpec extends R2RSpec { "mark a resource as deleted" in { - Delete("/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2F9d626dc76c03?deleteComment=deleted%20for%20testing") ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail2, password)) ~> resourcesPathV1 ~> check { + Delete( + "/v1/resources/http%3A%2F%2Frdfh.ch%2F0803%2F9d626dc76c03?deleteComment=deleted%20for%20testing" + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail2, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) } } @@ -928,30 +947,31 @@ class ResourcesV1R2RSpec extends R2RSpec { val xml = """ - |This text links to Google. + |This text links to Google. """.stripMargin val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id":"$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id":"$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -964,7 +984,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the fourth resource of type anything:Thing, containing the hyperlink in standoff" in { Get("/v1/resources/" + URLEncoder.encode(fourthThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val text: JsValue = getValuesForProp(response, "http://www.knora.org/ontology/0001/anything#hasText") @@ -998,25 +1019,26 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml3.toJson.compactPrint}, "mapping_id": "$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml3.toJson.compactPrint}, "mapping_id": "$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1029,7 +1051,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the fifth resource of type anything:Thing, containing various standoff markup" in { Get("/v1/resources/" + URLEncoder.encode(fifthThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val text: JsValue = getValuesForProp(response, "http://www.knora.org/ontology/0001/anything#hasText") @@ -1059,7 +1082,9 @@ class ResourcesV1R2RSpec extends R2RSpec { assert(linkToGoogle.nonEmpty && linkToGoogle.head.text == "http://www.google.ch") - assert(links(1).attributes.asAttrMap("class") == "salsah-link") // The link to a resource IRI has class="salsah-link" + assert( + links(1).attributes.asAttrMap("class") == "salsah-link" + ) // The link to a resource IRI has class="salsah-link" val linkKnoraResource: Seq[Node] = links(1).attributes("href") assert(linkKnoraResource.nonEmpty && linkKnoraResource.head.text == incunabulaBookBiechlin) @@ -1077,25 +1102,26 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml": ${xml4.toJson.compactPrint},"mapping_id": "$mappingIri"}}], - | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], - | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], - | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], - | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], - | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], - | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml": ${xml4.toJson.compactPrint},"mapping_id": "$mappingIri"}}], + | "http://www.knora.org/ontology/0001/anything#hasInteger": [{"int_value":12345}], + | "http://www.knora.org/ontology/0001/anything#hasDecimal": [{"decimal_value":5.6}], + | "http://www.knora.org/ontology/0001/anything#hasUri": [{"uri_value":"http://dhlab.unibas.ch"}], + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value":"JULIAN:1291-08-01:1291-08-01"}], + | "http://www.knora.org/ontology/0001/anything#hasColor": [{"color_value":"#4169E1"}], + | "http://www.knora.org/ontology/0001/anything#hasListItem": [{"hlist_value":"http://rdfh.ch/lists/0001/treeList10"}], + | "http://www.knora.org/ontology/0001/anything#hasInterval": [{"interval_value": [1000000000000000.0000000000000001, 1000000000000000.0000000000000002]}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1108,7 +1134,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the sixth resource of type anything:Thing with internal links to two different resources" in { Get("/v1/resources/" + URLEncoder.encode(sixthThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val text: JsValue = getValuesForProp(response, "http://www.knora.org/ontology/0001/anything#hasText") @@ -1157,14 +1184,15 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "label": "$newLabel" - |} + |{ + | "label": "$newLabel" + |} """.stripMargin - Put("/v1/resources/label/" + URLEncoder.encode("http://rdfh.ch/0803/c5058f3a", "UTF-8"), - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> resourcesPathV1 ~> check { + Put( + "/v1/resources/label/" + URLEncoder.encode("http://rdfh.ch/0803/c5058f3a", "UTF-8"), + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val label = AkkaHttpUtils.httpResponseToJson(response).fields.get("label") match { @@ -1182,19 +1210,20 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A thing with a link value that has a comment", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value": {"utf8str": "simple text"}}], - | "http://www.knora.org/ontology/0001/anything#hasOtherThing": [{"link_value":"${sixthThingIri.get}", "comment":"$notTheMostBoringComment"}] - | } + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A thing with a link value that has a comment", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value": {"utf8str": "simple text"}}], + | "http://www.knora.org/ontology/0001/anything#hasOtherThing": [{"link_value":"${sixthThingIri.get}", "comment":"$notTheMostBoringComment"}] + | } } """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val resId = getResIriFromJsonResponse(response) @@ -1206,7 +1235,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the created resource and check the comment on the link value" in { Get("/v1/resources/" + URLEncoder.encode(seventhThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1236,18 +1266,19 @@ class ResourcesV1R2RSpec extends R2RSpec { val newValueParams = s""" - |{ - | "project_id": "http://rdfh.ch/projects/0001", - | "res_id": "${seventhThingIri.get}", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": { - | "utf8str": "another simple text" - | } - |} + |{ + | "project_id": "http://rdfh.ch/projects/0001", + | "res_id": "${seventhThingIri.get}", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": { + | "utf8str": "another simple text" + | } + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1268,18 +1299,19 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A thing with a BCE date of the murder of Caesar", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value": "JULIAN:44-03-15 BCE"}] - | } + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A thing with a BCE date of the murder of Caesar", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasDate": [{"date_value": "JULIAN:44-03-15 BCE"}] + | } } """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val resId = getResIriFromJsonResponse(response) @@ -1291,7 +1323,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the eighth resource and check its date" in { Get("/v1/resources/" + URLEncoder.encode(eighthThingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1349,55 +1382,56 @@ class ResourcesV1R2RSpec extends R2RSpec { "create resources from an XML import" in { val xmlImport = s""" - | - | - | Niels Henrik Abel - | Abel - | Niels Henrik - | Sir - | - | - | Sherlock Holmes - | Holmes - | Sherlock - | - | - | Math Intelligencer - | Math Intelligencer - | - | - | Strings in the 16th and 17th Centuries - | - | The most interesting article in Math Intelligencer. - | - | 73 - | - | - | - | 27 - | - | - | - | - | - | - | GREGORIAN:500 BC:400 BC - | Strings in the 16th and 17th Centuries - | An alternate title - | 48 - | - |""".stripMargin + | + | + | Niels Henrik Abel + | Abel + | Niels Henrik + | Sir + | + | + | Sherlock Holmes + | Holmes + | Sherlock + | + | + | Math Intelligencer + | Math Intelligencer + | + | + | Strings in the 16th and 17th Centuries + | + | The most interesting article in Math Intelligencer. + | + | 73 + | + | + | + | 27 + | + | + | + | + | + | + | GREGORIAN:500 BC:400 BC + | Strings in the 16th and 17th Centuries + | An alternate title + | 48 + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { val responseStr: String = responseAs[String] assert(status == StatusCodes.OK, responseStr) responseStr should include("createdResources") @@ -1412,54 +1446,55 @@ class ResourcesV1R2RSpec extends R2RSpec { "reject XML import data that fails schema validation" in { val xmlImport = s""" - | - | - | Niels Henrik Abel - | Abel - | Niels Henrik - | - | - | Sherlock Holmes - | Holmes - | Sherlock - | - | - | Math Intelligencer - | Math Intelligencer - | - | - | Strings in the 16th and 17th Centuries - | - | The most interesting article in Math Intelligencer. - | - | 73 - | - | - | - | 27 - | - | - | - | - | - | - | GREGORIAN:19foo76 - | Strings in the 16th and 17th Centuries - | An alternate title - | 48 - | - |""".stripMargin + | + | + | Niels Henrik Abel + | Abel + | Niels Henrik + | + | + | Sherlock Holmes + | Holmes + | Sherlock + | + | + | Math Intelligencer + | Math Intelligencer + | + | + | Strings in the 16th and 17th Centuries + | + | The most interesting article in Math Intelligencer. + | + | 73 + | + | + | + | 27 + | + | + | + | + | + | + | GREGORIAN:19foo76 + | Strings in the 16th and 17th Centuries + | An alternate title + | 48 + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.BadRequest, response.toString) val responseStr = responseAs[String] responseStr should include("org.xml.sax.SAXParseException") @@ -1470,36 +1505,37 @@ class ResourcesV1R2RSpec extends R2RSpec { "refer to existing resources in an XML import" in { val xmlImport = s""" - | - | - | Strings in the 18th Century - | - | The most boring article in Math Intelligencer. - | - | 76 - | - | - | - | 27 - | - | - | - | GREGORIAN:1977 - | Strings in the 18th Century - | 52 - | - |""".stripMargin + | + | + | Strings in the 18th Century + | + | The most boring article in Math Intelligencer. + | + | 76 + | + | + | + | 27 + | + | + | + | GREGORIAN:1977 + | Strings in the 18th Century + | 52 + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) responseStr should include("createdResources") @@ -1509,33 +1545,34 @@ class ResourcesV1R2RSpec extends R2RSpec { "create an anything:Thing with all data types from an XML import" in { val xmlImport = s""" - | - | - | These are a few of my favorite things - | true - | #4169E1 - | JULIAN:1291-08-01:1291-08-01 - | 5.6 - | 12345 - | 1000000000000000.0000000000000001,1000000000000000.0000000000000002 - | http://rdfh.ch/lists/0001/treeList10 - | - | - | - | This is a test. - | http://dhlab.unibas.ch - | - |""".stripMargin + | + | + | These are a few of my favorite things + | true + | #4169E1 + | JULIAN:1291-08-01:1291-08-01 + | 5.6 + | 12345 + | 1000000000000000.0000000000000001,1000000000000000.0000000000000002 + | http://rdfh.ch/lists/0001/treeList10 + | + | + | + | This is a test. + | http://dhlab.unibas.ch + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) responseStr should include("createdResources") @@ -1545,22 +1582,23 @@ class ResourcesV1R2RSpec extends R2RSpec { "not create an anything:Thing in the incunabula project in a bulk import" in { val xmlImport = s""" - | - | - | These are a few of my favorite things - | This is a test. - | - |""".stripMargin + | + | + | These are a few of my favorite things + | This is a test. + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0803", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.BadRequest, response.toString) val responseStr = responseAs[String] responseStr should include("not shared") @@ -1570,23 +1608,24 @@ class ResourcesV1R2RSpec extends R2RSpec { "not create a resource in a shared ontologies project in a bulk import" in { val xmlImport = s""" - | - | - | test box - | This is a test. - | - |""".stripMargin + | + | + | test box + | This is a test. + | + |""".stripMargin val projectIri = URLEncoder.encode("http://www.knora.org/ontology/knora-admin#DefaultSharedOntologiesProject", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(superUserEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(superUserEmail, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.BadRequest, response.toString) val responseStr = responseAs[String] responseStr should include("Resources cannot be created in project") @@ -1596,22 +1635,23 @@ class ResourcesV1R2RSpec extends R2RSpec { "create a resource in the incunabula project using a class from the default shared ontologies project" in { val xmlImport = s""" - | - | - | test box - | This is a test. - | - |""".stripMargin + | + | + | test box + | This is a test. + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0803", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) responseStr should include("createdResources") @@ -1621,22 +1661,23 @@ class ResourcesV1R2RSpec extends R2RSpec { "use a knora-base property directly in a bulk import" in { val xmlImport = s""" - | - | - | Thing with seqnum - | 3 - | - |""".stripMargin + | + | + | Thing with seqnum + | 3 + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) responseStr should include("createdResources") @@ -1647,7 +1688,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val ontologyIri = URLEncoder.encode("http://www.knora.org/ontology/0801/biblio", "UTF-8") Get(s"/v1/resources/xmlimportschemas/$ontologyIri") ~> addCredentials( - BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(beolUserEmail, password) + ) ~> resourcesPathV1 ~> check { val responseBodyFuture: Future[Array[Byte]] = response.entity.toStrict(5.seconds).map(_.data.toArray) val responseBytes: Array[Byte] = Await.result(responseBodyFuture, 5.seconds) val zippedFilenames = collection.mutable.Set.empty[String] @@ -1670,7 +1712,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val ontologyIri = URLEncoder.encode("http://www.knora.org/ontology/0001/something", "UTF-8") Get(s"/v1/resources/xmlimportschemas/$ontologyIri") ~> addCredentials( - BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(beolUserEmail, password) + ) ~> resourcesPathV1 ~> check { val responseBodyFuture: Future[Array[Byte]] = response.entity.toStrict(5.seconds).map(_.data.toArray) val responseBytes: Array[Byte] = Await.result(responseBodyFuture, 5.seconds) val zippedFilenames = collection.mutable.Set.empty[String] @@ -1694,7 +1737,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val ontologyIri = URLEncoder.encode("http://www.knora.org/ontology/0001/empty-thing", "UTF-8") Get(s"/v1/resources/xmlimportschemas/$ontologyIri") ~> addCredentials( - BasicHttpCredentials(beolUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(beolUserEmail, password) + ) ~> resourcesPathV1 ~> check { val responseBodyFuture: Future[Array[Byte]] = response.entity.toStrict(5.seconds).map(_.data.toArray) val responseBytes: Array[Byte] = Await.result(responseBodyFuture, 5.seconds) val zippedFilenames = collection.mutable.Set.empty[String] @@ -1714,36 +1758,36 @@ class ResourcesV1R2RSpec extends R2RSpec { } "create 10,000 anything:Thing resources with random contents" in { - def maybeAppendValue(random: Random, xmlStringBuilder: StringBuilder, value: String): Unit = { + def maybeAppendValue(random: Random, xmlStringBuilder: StringBuilder, value: String): Unit = if (random.nextBoolean()) { xmlStringBuilder.append(value) } - } val xmlStringBuilder = new StringBuilder val random = new Random xmlStringBuilder.append( """ - | - | - """.stripMargin) + | + | + """.stripMargin + ) for (i <- 1 to 10000) { xmlStringBuilder.append(s""" - | - |This is thing $i + | + |This is thing $i """.stripMargin) maybeAppendValue( random = random, xmlStringBuilder = xmlStringBuilder, value = """ - |true + |true """.stripMargin ) @@ -1751,15 +1795,16 @@ class ResourcesV1R2RSpec extends R2RSpec { random = random, xmlStringBuilder = xmlStringBuilder, value = """ - |#4169E1 + |#4169E1 """.stripMargin ) maybeAppendValue( random = random, xmlStringBuilder = xmlStringBuilder, - value = """ - |JULIAN:1291-08-01:1291-08-01 + value = + """ + |JULIAN:1291-08-01:1291-08-01 """.stripMargin ) @@ -1767,7 +1812,7 @@ class ResourcesV1R2RSpec extends R2RSpec { random = random, xmlStringBuilder = xmlStringBuilder, value = s""" - |$i.$i + |$i.$i """.stripMargin ) @@ -1775,7 +1820,7 @@ class ResourcesV1R2RSpec extends R2RSpec { random = random, xmlStringBuilder = xmlStringBuilder, value = s""" - |$i + |$i """.stripMargin ) @@ -1784,7 +1829,7 @@ class ResourcesV1R2RSpec extends R2RSpec { xmlStringBuilder = xmlStringBuilder, value = """ - |1000000000000000.0000000000000001,1000000000000000.0000000000000002 + |1000000000000000.0000000000000001,1000000000000000.0000000000000002 """.stripMargin ) @@ -1793,7 +1838,7 @@ class ResourcesV1R2RSpec extends R2RSpec { xmlStringBuilder = xmlStringBuilder, value = """ - |http://rdfh.ch/lists/0001/treeList10 + |http://rdfh.ch/lists/0001/treeList10 """.stripMargin ) @@ -1801,7 +1846,7 @@ class ResourcesV1R2RSpec extends R2RSpec { random = random, xmlStringBuilder = xmlStringBuilder, value = s""" - |This is a test in thing $i. + |This is a test in thing $i. """.stripMargin ) @@ -1809,7 +1854,7 @@ class ResourcesV1R2RSpec extends R2RSpec { random = random, xmlStringBuilder = xmlStringBuilder, value = s""" - |2019-08-28T15:13:10.968318Z + |2019-08-28T15:13:10.968318Z """.stripMargin ) @@ -1817,25 +1862,25 @@ class ResourcesV1R2RSpec extends R2RSpec { random = random, xmlStringBuilder = xmlStringBuilder, value = """ - |http://dhlab.unibas.ch + |http://dhlab.unibas.ch """.stripMargin ) xmlStringBuilder.append(""" - | + | """.stripMargin) } xmlStringBuilder.append(""" - | + | """.stripMargin) val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") Post( s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlStringBuilder.toString)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlStringBuilder.toString) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) responseStr should include("createdResources") @@ -1846,18 +1891,19 @@ class ResourcesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "Ein Ding auf deutsch", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value": {"utf8str": "Ein deutscher Text", "language": "de"}}] - | } + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "Ein Ding auf deutsch", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value": {"utf8str": "Ein deutscher Text", "language": "de"}}] + | } } """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val resId = getResIriFromJsonResponse(response) @@ -1869,7 +1915,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the deutschesDing Resource and check its textValue" in { Get("/v1/resources/" + URLEncoder.encode(deutschesDingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1902,7 +1949,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the resource created by bulk import and check language of its textValue" in { Get("/v1/resources/" + URLEncoder.encode(abelAuthorIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1935,23 +1983,24 @@ class ResourcesV1R2RSpec extends R2RSpec { val xml = """ - |This text links to Google. + |This text links to Google. """.stripMargin val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", - | "label": "A second thing", - | "project_id": "http://rdfh.ch/projects/0001", - | "properties": { - | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id":"$mappingIri", "language": "en"}}] - | } - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0001/anything#Thing", + | "label": "A second thing", + | "project_id": "http://rdfh.ch/projects/0001", + | "properties": { + | "http://www.knora.org/ontology/0001/anything#hasText": [{"richtext_value":{"xml":${xml.toJson.compactPrint},"mapping_id":"$mappingIri", "language": "en"}}] + | } + |} """.stripMargin Post("/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val resId = getResIriFromJsonResponse(response) @@ -1962,7 +2011,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the Resource with standoff and language and check its textValue" in { Get("/v1/resources/" + URLEncoder.encode(standoffLangDingIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1988,22 +2038,23 @@ class ResourcesV1R2RSpec extends R2RSpec { "create a string value with chars encoded as entities but without markup in a bulk import" in { val xmlImport = s""" - | - | - | Thing with string - | test & ' > < test - | - |""".stripMargin + | + | + | Thing with string + | test & ' > < test + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) @@ -2019,7 +2070,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the resource created by bulk import and check for entities in string value" in { Get("/v1/resources/" + URLEncoder.encode(thingWithString.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2056,23 +2108,24 @@ class ResourcesV1R2RSpec extends R2RSpec { "create a string value with a newline in a bulk import" in { val xmlImport = s""" - | - | - | Thing with string - | test - | test - | - |""".stripMargin + | + | + | Thing with string + | test + | test + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) @@ -2088,7 +2141,8 @@ class ResourcesV1R2RSpec extends R2RSpec { "get the resource created by bulk import and check for the newline in the string value" in { Get("/v1/resources/" + URLEncoder.encode(thingWithString.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV1 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2117,21 +2171,22 @@ class ResourcesV1R2RSpec extends R2RSpec { "create a resource whose label ends in a double quote" in { val xmlImport = s""" - | - | - | Thing with "label" - | - |""".stripMargin + | + | + | Thing with "label" + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) @@ -2149,22 +2204,23 @@ class ResourcesV1R2RSpec extends R2RSpec { val xmlImport = s""" - | - | - | Thing with creation date - | test - | - |""".stripMargin + | + | + | Thing with creation date + | test + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val responseStr = responseAs[String] responseStr should include("createdResources") @@ -2174,7 +2230,8 @@ class ResourcesV1R2RSpec extends R2RSpec { } Get("/v2/resourcespreview/" + URLEncoder.encode(thingWithCreationDate.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcesPathV2 ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcesPathV2 ~> check { assert(status == StatusCodes.OK, response.toString) val responseStr = responseAs[String] responseStr should include(creationDateStr) @@ -2184,24 +2241,25 @@ class ResourcesV1R2RSpec extends R2RSpec { "create a resource belonging to a class in a shared ontology that refers to a property in another shared ontology" in { val xmlImport = s""" - | - | - | test box 2 - | This is a test. - | - | + | + | + | test box 2 + | This is a test. + | + | """.stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - Post(s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport)) ~> addCredentials( - BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { + Post( + s"/v1/resources/xmlimport/$projectIri", + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), xmlImport) + ) ~> addCredentials(BasicHttpCredentials(anythingAdminEmail, password)) ~> resourcesPathV1 ~> check { assert(status == StatusCodes.OK, response.toString) val responseStr = responseAs[String] responseStr should include("createdResources") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala index d92a9f3735..0fd56f255d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala @@ -32,15 +32,15 @@ import spray.json._ import scala.concurrent.ExecutionContextExecutor /** - * End-to-end test specification for the search endpoint. This specification uses the Spray Testkit as documented - * here: http://spray.io/documentation/1.2.2/spray-testkit/ - */ + * End-to-end test specification for the search endpoint. This specification uses the Spray Testkit as documented + * here: http://spray.io/documentation/1.2.2/spray-testkit/ + */ class SearchV1R2RSpec extends R2RSpec { override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin private val searchPath = new SearchRouteV1(routeData).knoraApiPath @@ -56,12 +56,12 @@ class SearchV1R2RSpec extends R2RSpec { ) /** - * Checks for the number of expected results to be returned. - * - * @param responseJson the response send back by the search route. - * @param expectedNumber the expected number of results for the query. - * @return an assertion that the actual amount of results corresponds with the expected number of results. - */ + * Checks for the number of expected results to be returned. + * + * @param responseJson the response send back by the search route. + * @param expectedNumber the expected number of results for the query. + * @return an assertion that the actual amount of results corresponds with the expected number of results. + */ def checkNumberOfHits(responseJson: String, expectedNumber: Int): Assertion = { val response: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields @@ -285,7 +285,9 @@ class SearchV1R2RSpec extends R2RSpec { val filter = "&show_nrows=25&start_at=0&filter_by_project=http%3A%2F%2Frdfh.ch%2Fprojects%2F0001" - Get("/v1/search/?searchtype=extended" + props_two_lists_one + props_two_lists_two + filter) ~> searchPath ~> check { + Get( + "/v1/search/?searchtype=extended" + props_two_lists_one + props_two_lists_two + filter + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala index d8e0795772..0bb83c13db 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala @@ -37,9 +37,9 @@ import org.knora.webapi.settings._ import org.knora.webapi.sharedtestdata.SharedTestDataV1 /** - * End-to-end test specification for the resources endpoint. This specification uses the Spray Testkit as documented - * here: http://spray.io/documentation/1.2.2/spray-testkit/ - */ + * End-to-end test specification for the resources endpoint. This specification uses the Spray Testkit as documented + * here: http://spray.io/documentation/1.2.2/spray-testkit/ + */ class SipiV1R2RSpec extends R2RSpec { override def testConfigSource: String = @@ -64,7 +64,8 @@ class SipiV1R2RSpec extends R2RSpec { /* we need to run our app with the mocked sipi actor */ override lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with ManagersWithMockedSipi).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + name = APPLICATION_MANAGER_ACTOR_NAME + ) object RequestParams { @@ -76,23 +77,29 @@ class SipiV1R2RSpec extends R2RSpec { richtext_value = Some( CreateRichtextV1( utf8str = Some("test_page") - )) - )), + ) + ) + ) + ), "http://www.knora.org/ontology/0803/incunabula#origname" -> Seq( CreateResourceValueV1( richtext_value = Some( CreateRichtextV1( utf8str = Some("test") - )) - )), + ) + ) + ) + ), "http://www.knora.org/ontology/0803/incunabula#partOf" -> Seq( CreateResourceValueV1( link_value = Some("http://rdfh.ch/0803/5e77e98d2603") - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Seq( CreateResourceValueV1( int_value = Some(999) - )) + ) + ) ), label = "test", project_id = "http://rdfh.ch/projects/0803" @@ -125,7 +132,8 @@ class SipiV1R2RSpec extends R2RSpec { ) Post("/v1/resources", HttpEntity(MediaTypes.`application/json`, params.toJsValue.compactPrint)) ~> addCredentials( - BasicHttpCredentials(incunabulaProjectAdminEmail, testPass)) ~> resourcesPath ~> check { + BasicHttpCredentials(incunabulaProjectAdminEmail, testPass) + ) ~> resourcesPath ~> check { assert(status == StatusCodes.OK, "Status code is not set to OK, Knora says:\n" + responseAs[String]) } } @@ -143,8 +151,10 @@ class SipiV1R2RSpec extends R2RSpec { val resIri = URLEncoder.encode("http://rdfh.ch/0803/8a0b1e75", "UTF-8") - Put("/v1/filevalue/" + resIri, HttpEntity(MediaTypes.`application/json`, params.toJsValue.compactPrint)) ~> addCredentials( - BasicHttpCredentials(incunabulaProjectAdminEmail, testPass)) ~> valuesPath ~> check { + Put( + "/v1/filevalue/" + resIri, + HttpEntity(MediaTypes.`application/json`, params.toJsValue.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(incunabulaProjectAdminEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, "Status code is not set to OK, Knora says:\n" + responseAs[String]) } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala index 0f8394bac3..bdd727f4b3 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala @@ -43,9 +43,9 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContextExecutor, Future} /** - * End-to-end test specification for the standoff endpoint. This specification uses the Spray Testkit as documented - * here: http://spray.io/documentation/1.2.2/spray-testkit/ - */ + * End-to-end test specification for the standoff endpoint. This specification uses the Spray Testkit as documented + * here: http://spray.io/documentation/1.2.2/spray-testkit/ + */ class StandoffV1R2RSpec extends R2RSpec { override def testConfigSource: String = @@ -54,8 +54,8 @@ class StandoffV1R2RSpec extends R2RSpec { # akka.stdout-loglevel = "DEBUG" """.stripMargin - private val standoffPath = DSPApiDirectives.handleErrors(system) { new StandoffRouteV1(routeData).knoraApiPath } - private val valuesPath = DSPApiDirectives.handleErrors(system) { new ValuesRouteV1(routeData).knoraApiPath } + private val standoffPath = DSPApiDirectives.handleErrors(system)(new StandoffRouteV1(routeData).knoraApiPath) + private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) private val anythingUser = SharedTestDataV1.anythingUser1 private val anythingUserEmail = anythingUser.userData.email.get @@ -90,7 +90,8 @@ class StandoffV1R2RSpec extends R2RSpec { case None => throw InvalidApiJsonException(s"The response does not contain a field called '$memberName'") case other => throw InvalidApiJsonException( - s"The response does not contain a field '$memberName' of type JsString, but $other") + s"The response does not contain a field '$memberName' of type JsString, but $other" + ) } } @@ -105,11 +106,11 @@ class StandoffV1R2RSpec extends R2RSpec { val paramsCreateLetterMappingFromXML: String = s""" - |{ - | "project_id": "$ANYTHING_PROJECT_IRI", - | "label": "mapping for letters", - | "mappingName": "LetterMapping" - |} + |{ + | "project_id": "$ANYTHING_PROJECT_IRI", + | "label": "mapping for letters", + | "mappingName": "LetterMapping" + |} """.stripMargin val pathToLetterMapping = "test_data/test_route/texts/mappingForLetter.xml" @@ -122,11 +123,11 @@ class StandoffV1R2RSpec extends R2RSpec { val paramsCreateHTMLMappingFromXML: String = s""" - |{ - | "project_id": "$ANYTHING_PROJECT_IRI", - | "label": "mapping for HTML", - | "mappingName": "HTMLMapping" - |} + |{ + | "project_id": "$ANYTHING_PROJECT_IRI", + | "label": "mapping for HTML", + | "mappingName": "HTMLMapping" + |} """.stripMargin // Standard HTML is the html code that can be translated into Standoff markup with the OntologyConstants.KnoraBase.StandardMapping @@ -146,39 +147,39 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping: String = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRot - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | - | - | - | - | p - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/standoff#StandoffParagraphTag - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRot + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | + | + | + | + | p + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/standoff#StandoffParagraphTag + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -193,7 +194,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -209,39 +212,39 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRootTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDoc - | - | - | - | - | - | - | - | p - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/standoff#StandoffParagraphTag - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRootTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDoc + | + | + | + | + | + | + | + | p + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/standoff#StandoffParagraphTag + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -256,7 +259,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -272,46 +277,46 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRootTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | - | - | - | - | p - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/standoff#StandoffParagraphTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRootTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | + | + | + | + | p + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/standoff#StandoffParagraphTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -326,7 +331,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -343,44 +350,44 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRootTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | - | - | - | - | event - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/0001/anything#StandoffEventTag - | - | - | http://www.knora.org/ontology/knora-base#StandoffDateTag - | src - | - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRootTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | + | + | + | + | event + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/0001/anything#StandoffEventTag + | + | + | http://www.knora.org/ontology/knora-base#StandoffDateTag + | src + | + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -395,7 +402,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -404,7 +413,8 @@ class StandoffV1R2RSpec extends R2RSpec { // make sure the user gets informed about the missing required property anything:standoffEventTagHasDescription assert(responseAs[String].contains("http://www.knora.org/ontology/0001/anything#StandoffEventTag")) assert( - responseAs[String].contains("http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription")) + responseAs[String].contains("http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription") + ) } } @@ -413,47 +423,47 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRootTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | - | - | - | - | event - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/0001/anything#StandoffEventTag - | - | - | desc - | noNamespace - | http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription - | - | - | - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRootTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | + | + | + | + | event + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/0001/anything#StandoffEventTag + | + | + | desc + | noNamespace + | http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription + | + | + | + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -468,7 +478,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -485,50 +497,50 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRootTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | - | - | - | - | event - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/0001/anything#StandoffEventTag - | - | - | desc - | noNamespace - | http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription - | - | - | - | http://www.knora.org/ontology/knora-base#StandoffUriTag - | src - | - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRootTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | + | + | + | + | event + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/0001/anything#StandoffEventTag + | + | + | desc + | noNamespace + | http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription + | + | + | + | http://www.knora.org/ontology/knora-base#StandoffUriTag + | src + | + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -543,7 +555,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -560,54 +574,54 @@ class StandoffV1R2RSpec extends R2RSpec { val brokenMapping = """ - | - | - | - | text - | noClass - | noNamespace - | false - | - | - | http://www.knora.org/ontology/standoff#StandoffRootTag - | - | - | documentType - | noNamespace - | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType - | - | - | - | http://www.knora.org/ontology/knora-base#StandoffDateTag - | src - | - | - | - | - | - | - | event - | noClass - | noNamespace - | true - | - | - | http://www.knora.org/ontology/0001/anything#StandoffEventTag - | - | - | desc - | noNamespace - | http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription - | - | - | - | http://www.knora.org/ontology/knora-base#StandoffDateTag - | src - | - | - | - | - | """.stripMargin + | + | + | + | text + | noClass + | noNamespace + | false + | + | + | http://www.knora.org/ontology/standoff#StandoffRootTag + | + | + | documentType + | noNamespace + | http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType + | + | + | + | http://www.knora.org/ontology/knora-base#StandoffDateTag + | src + | + | + | + | + | + | + | event + | noClass + | noNamespace + | true + | + | + | http://www.knora.org/ontology/0001/anything#StandoffEventTag + | + | + | desc + | noNamespace + | http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription + | + | + | + | http://www.knora.org/ontology/knora-base#StandoffDateTag + | src + | + | + | + | + | """.stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -622,7 +636,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -652,10 +668,14 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { - assert(status == StatusCodes.OK, - "standoff mapping creation route returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "standoff mapping creation route returned a non successful HTTP status code: " + responseAs[String] + ) // check if mappingIri is correct val mappingIri = ResponseUtils.getStringMemberFromResponse(response, "mappingIri") @@ -685,10 +705,13 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String] + ) firstTextValueIri.set(ResponseUtils.getStringMemberFromResponse(response, "id")) @@ -702,7 +725,8 @@ class StandoffV1R2RSpec extends R2RSpec { val xmlStr = FileUtil.readTextFile(xmlFile) Get("/v1/values/" + URLEncoder.encode(firstTextValueIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(response.status == StatusCodes.OK, "reading back text value to XML failed") @@ -741,12 +765,15 @@ class StandoffV1R2RSpec extends R2RSpec { """ // change standoff from XML - Put("/v1/values/" + URLEncoder.encode(firstTextValueIri.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + Put( + "/v1/values/" + URLEncoder.encode(firstTextValueIri.get, "UTF-8"), + HttpEntity(ContentTypes.`application/json`, newValueParams) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "standoff creation route returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "standoff creation route returned a non successful HTTP status code: " + responseAs[String] + ) firstTextValueIri.set(ResponseUtils.getStringMemberFromResponse(response, "id")) @@ -760,7 +787,8 @@ class StandoffV1R2RSpec extends R2RSpec { val xmlStr = FileUtil.readTextFile(xmlFile) Get("/v1/values/" + URLEncoder.encode(firstTextValueIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(response.status == StatusCodes.OK, "reading back text value to XML failed") @@ -802,10 +830,13 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String] + ) secondTextValueIri.set(ResponseUtils.getStringMemberFromResponse(response, "id")) @@ -819,7 +850,8 @@ class StandoffV1R2RSpec extends R2RSpec { val xmlStr = FileUtil.readTextFile(xmlFile) Get("/v1/values/" + URLEncoder.encode(secondTextValueIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(response.status == StatusCodes.OK, "reading back text value to XML failed") @@ -858,10 +890,14 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { - assert(status == StatusCodes.OK, - "standoff mapping creation route returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "standoff mapping creation route returned a non successful HTTP status code: " + responseAs[String] + ) // check if mappingIri is correct val mappingIri = ResponseUtils.getStringMemberFromResponse(response, "mappingIri") @@ -892,10 +928,13 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String] + ) thirdTextValueIri.set(ResponseUtils.getStringMemberFromResponse(response, "id")) @@ -909,7 +948,8 @@ class StandoffV1R2RSpec extends R2RSpec { val htmlStr = FileUtil.readTextFile(htmlFile) Get("/v1/values/" + URLEncoder.encode(thirdTextValueIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(response.status == StatusCodes.OK, "reading back text value to XML failed") @@ -951,10 +991,13 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String] + ) fourthTextValueIri.set(ResponseUtils.getStringMemberFromResponse(response, "id")) @@ -968,7 +1011,8 @@ class StandoffV1R2RSpec extends R2RSpec { val htmlStr = FileUtil.readTextFile(htmlFile) Get("/v1/values/" + URLEncoder.encode(fourthTextValueIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(response.status == StatusCodes.OK, "reading back text value to XML failed") @@ -1010,10 +1054,13 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String] + ) thirdTextValueIri.set(ResponseUtils.getStringMemberFromResponse(response, "id")) @@ -1027,7 +1074,8 @@ class StandoffV1R2RSpec extends R2RSpec { val htmlStr = FileUtil.readTextFile(htmlFile) Get("/v1/values/" + URLEncoder.encode(thirdTextValueIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(response.status == StatusCodes.OK, "reading back text value to XML failed") @@ -1073,13 +1121,15 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) // the error message should inform the user that the required property http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription is missing assert( - responseAs[String].contains("http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription")) + responseAs[String].contains("http://www.knora.org/ontology/0001/anything#standoffEventTagHasDescription") + ) } @@ -1110,7 +1160,8 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -1145,7 +1196,8 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -1180,7 +1232,8 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -1218,7 +1271,8 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) @@ -1253,14 +1307,17 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) // the error message should inform the user that the provided mapping Iri is invalid assert( responseAs[String].contains( - s"mapping $ANYTHING_PROJECT_IRI/invalidPathForMappings/HTMLMapping does not exist")) + s"mapping $ANYTHING_PROJECT_IRI/invalidPathForMappings/HTMLMapping does not exist" + ) + ) } @@ -1303,11 +1360,11 @@ class StandoffV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "project_id": "$ANYTHING_PROJECT_IRI", - | "label": "mapping for elements separating words", - | "mappingName": "MappingSeparatingWords" - |} + |{ + | "project_id": "$ANYTHING_PROJECT_IRI", + | "label": "mapping for elements separating words", + | "mappingName": "MappingSeparatingWords" + |} """.stripMargin val formDataMapping = Multipart.FormData( @@ -1323,7 +1380,9 @@ class StandoffV1R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v1/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.OK) @@ -1355,10 +1414,13 @@ class StandoffV1R2RSpec extends R2RSpec { // create standoff from XML Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { - assert(status == StatusCodes.OK, - "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a TextValue from XML returned a non successful HTTP status code: " + responseAs[String] + ) } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala index 87a808c04f..34eba3ce67 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala @@ -41,8 +41,8 @@ object UsersV1E2ESpec { } /** - * End-to-End (E2E) test specification for testing users endpoint. - */ + * End-to-End (E2E) test specification for testing users endpoint. + */ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProtocol with TriplestoreJsonProtocol { implicit def default(implicit system: ActorSystem) = RouteTestTimeout(30.seconds) @@ -78,43 +78,46 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt private val imagesProjectIriEnc = java.net.URLEncoder.encode(imagesProjectIri, "utf-8") /** - * Convenience method returning the users project memberships. - * - * @param userIri the user's IRI. - * @param credentials the credentials of the user making the request. - */ + * Convenience method returning the users project memberships. + * + * @param userIri the user's IRI. + * @param credentials the credentials of the user making the request. + */ private def getUserProjectMemberships(userIri: IRI, credentials: CredentialsV1): Seq[IRI] = { val userIriEnc = java.net.URLEncoder.encode(userIri, "utf-8") val request = Get(baseApiUrl + "/v1/users/projects/" + userIriEnc) ~> addCredentials( - BasicHttpCredentials(credentials.email, credentials.password)) + BasicHttpCredentials(credentials.email, credentials.password) + ) val response: HttpResponse = singleAwaitingRequest(request) AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[Seq[IRI]] } /** - * Convenience method returning the users project-admin memberships. - * - * @param userIri the user's IRI. - * @param credentials the credentials of the user making the request. - */ + * Convenience method returning the users project-admin memberships. + * + * @param userIri the user's IRI. + * @param credentials the credentials of the user making the request. + */ private def getUserProjectAdminMemberships(userIri: IRI, credentials: CredentialsV1): Seq[IRI] = { val userIriEnc = java.net.URLEncoder.encode(userIri, "utf-8") val request = Get(baseApiUrl + "/v1/users/projects-admin/" + userIriEnc) ~> addCredentials( - BasicHttpCredentials(credentials.email, credentials.password)) + BasicHttpCredentials(credentials.email, credentials.password) + ) val response: HttpResponse = singleAwaitingRequest(request) AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[Seq[IRI]] } /** - * Convenience method returning the users group memberships. - * - * @param userIri the user's IRI. - * @param credentials the credentials of the user making the request. - */ + * Convenience method returning the users group memberships. + * + * @param userIri the user's IRI. + * @param credentials the credentials of the user making the request. + */ private def getUserGroupMemberships(userIri: IRI, credentials: CredentialsV1): Seq[IRI] = { val userIriEnc = java.net.URLEncoder.encode(userIri, "utf-8") val request = Get(baseApiUrl + "/v1/users/groups/" + userIriEnc) ~> addCredentials( - BasicHttpCredentials(credentials.email, credentials.password)) + BasicHttpCredentials(credentials.email, credentials.password) + ) val response: HttpResponse = singleAwaitingRequest(request) AkkaHttpUtils.httpResponseToJson(response).fields("groups").convertTo[Seq[IRI]] } @@ -124,8 +127,8 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt "used to query user information" should { "return all users" in { - val request = Get(baseApiUrl + s"/v1/users") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + val request = + Get(baseApiUrl + s"/v1/users") ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) // logger.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -134,7 +137,8 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt "return a single user profile identified by iri" in { /* Correct username and password */ val request = Get(baseApiUrl + s"/v1/users/${rootCreds.urlEncodedIri}") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) // logger.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -143,7 +147,8 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt "return a single user profile identified by email" in { /* Correct username and password */ val request = Get(baseApiUrl + s"/v1/users/${rootCreds.urlEncodedEmail}?identifier=email") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) // logger.debug(s"response: ${response.toString}") response.status should be(StatusCodes.OK) @@ -155,15 +160,18 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt "return all projects the user is a member of" in { val request = Get(baseApiUrl + s"/v1/users/projects/$multiUserIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: ${response.toString}") assert(response.status === StatusCodes.OK) val projects: Seq[IRI] = AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[List[IRI]] - projects should contain allElementsOf Seq(SharedTestDataV1.IMAGES_PROJECT_IRI, - SharedTestDataV1.INCUNABULA_PROJECT_IRI, - SharedTestDataV1.ANYTHING_PROJECT_IRI) + projects should contain allElementsOf Seq( + SharedTestDataV1.IMAGES_PROJECT_IRI, + SharedTestDataV1.INCUNABULA_PROJECT_IRI, + SharedTestDataV1.ANYTHING_PROJECT_IRI + ) // testing getUserProjectMemberships method, which should return the same result projects should contain allElementsOf getUserProjectMemberships(multiUserIri, rootCreds) @@ -174,15 +182,18 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt "return all projects the user is a member of the project admin group" in { val request = Get(baseApiUrl + s"/v1/users/projects-admin/$multiUserIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: ${response.toString}") assert(response.status === StatusCodes.OK) val projects: Seq[IRI] = AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[List[IRI]] - projects should contain allElementsOf Seq(SharedTestDataV1.IMAGES_PROJECT_IRI, - SharedTestDataV1.INCUNABULA_PROJECT_IRI, - SharedTestDataV1.ANYTHING_PROJECT_IRI) + projects should contain allElementsOf Seq( + SharedTestDataV1.IMAGES_PROJECT_IRI, + SharedTestDataV1.INCUNABULA_PROJECT_IRI, + SharedTestDataV1.ANYTHING_PROJECT_IRI + ) // explicitly testing 'getUserProjectsAdminMemberships' method, which should return the same result projects should contain allElementsOf getUserProjectAdminMemberships(multiUserIri, rootCreds) @@ -193,7 +204,8 @@ class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProt "return all groups the user is a member of" in { val request = Get(baseApiUrl + s"/v1/users/groups/$multiUserIriEnc") ~> addCredentials( - BasicHttpCredentials(rootCreds.email, rootCreds.password)) + BasicHttpCredentials(rootCreds.email, rootCreds.password) + ) val response: HttpResponse = singleAwaitingRequest(request) //log.debug(s"response: ${response.toString}") assert(response.status === StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala index e1feb8bba2..f68eb5eac7 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala @@ -35,8 +35,8 @@ import org.knora.webapi.util.{AkkaHttpUtils, MutableTestIri} import spray.json._ /** - * Tests the values route. - */ + * Tests the values route. + */ class ValuesV1R2RSpec extends R2RSpec { override def testConfigSource: String = @@ -45,7 +45,7 @@ class ValuesV1R2RSpec extends R2RSpec { # akka.stdout-loglevel = "DEBUG" """.stripMargin - private val valuesPath = DSPApiDirectives.handleErrors(system) { new ValuesRouteV1(routeData).knoraApiPath } + private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) @@ -70,15 +70,16 @@ class ValuesV1R2RSpec extends R2RSpec { "add an integer value to a resource" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasInteger", - | "int_value": 1234 - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasInteger", + | "int_value": 1234 + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields val valueIri: IRI = responseJson("id").asInstanceOf[JsString].value @@ -89,16 +90,17 @@ class ValuesV1R2RSpec extends R2RSpec { "change an integer value" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasInteger", - | "int_value": 4321 - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasInteger", + | "int_value": 4321 + |} """.stripMargin - Put(s"/v1/values/${URLEncoder.encode(integerValueIri.get, "UTF-8")}", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Put( + s"/v1/values/${URLEncoder.encode(integerValueIri.get, "UTF-8")}", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields val valueIri: IRI = responseJson("id").asInstanceOf[JsString].value @@ -107,8 +109,9 @@ class ValuesV1R2RSpec extends R2RSpec { } "mark an integer value as deleted" in { - Delete(s"/v1/values/${URLEncoder.encode(integerValueIri.get, "UTF-8")}?deleteComment=deleted%20for%20testing") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Delete( + s"/v1/values/${URLEncoder.encode(integerValueIri.get, "UTF-8")}?deleteComment=deleted%20for%20testing" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) } } @@ -116,15 +119,16 @@ class ValuesV1R2RSpec extends R2RSpec { "add a time value to a resource" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasTimeStamp", - | "time_value": "2019-08-28T14:40:17.215927Z" - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasTimeStamp", + | "time_value": "2019-08-28T14:40:17.215927Z" + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields val valueIri: IRI = responseJson("id").asInstanceOf[JsString].value @@ -135,16 +139,17 @@ class ValuesV1R2RSpec extends R2RSpec { "change a time value" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasTimeStamp", - | "time_value": "2019-08-28T14:45:37.756142Z" - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasTimeStamp", + | "time_value": "2019-08-28T14:45:37.756142Z" + |} """.stripMargin - Put(s"/v1/values/${URLEncoder.encode(timeValueIri.get, "UTF-8")}", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Put( + s"/v1/values/${URLEncoder.encode(timeValueIri.get, "UTF-8")}", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields val valueIri: IRI = responseJson("id").asInstanceOf[JsString].value @@ -153,10 +158,10 @@ class ValuesV1R2RSpec extends R2RSpec { } "get a link value" in { - Get(s"/v1/links/${URLEncoder.encode("http://rdfh.ch/0001/contained-thing-1", "UTF-8")}/${URLEncoder.encode( - "http://www.knora.org/ontology/0001/anything#isPartOfOtherThing", - "UTF-8")}/${URLEncoder.encode("http://rdfh.ch/0001/containing-thing", "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Get( + s"/v1/links/${URLEncoder.encode("http://rdfh.ch/0001/contained-thing-1", "UTF-8")}/${URLEncoder + .encode("http://www.knora.org/ontology/0001/anything#isPartOfOtherThing", "UTF-8")}/${URLEncoder.encode("http://rdfh.ch/0001/containing-thing", "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val linkValue = AkkaHttpUtils.httpResponseToJson(response).fields("value").asJsObject.fields @@ -175,15 +180,16 @@ class ValuesV1R2RSpec extends R2RSpec { "not add an empty text value to a resource" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": {"utf8str":""} - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": {"utf8str":""} + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.BadRequest, response.toString) } } @@ -191,22 +197,23 @@ class ValuesV1R2RSpec extends R2RSpec { "add a text value containing a standoff reference to another resource" in { val xmlStr = """ - | - | This text links to another resource. - | + | + | This text links to another resource. + | """.stripMargin val params = s""" - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": {"xml": ${xmlStr.toJson.compactPrint}, "mapping_id": "$mappingIri"} - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": {"xml": ${xmlStr.toJson.compactPrint}, "mapping_id": "$mappingIri"} + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields @@ -228,23 +235,24 @@ class ValuesV1R2RSpec extends R2RSpec { "change a text value containing a standoff reference to another resource" in { val xmlStr = """ - | - | This new version of the text links to another resource. - | + | + | This new version of the text links to another resource. + | """.stripMargin val params = s""" - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": {"xml": ${xmlStr.toJson.compactPrint}, "mapping_id": "$mappingIri"} - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": {"xml": ${xmlStr.toJson.compactPrint}, "mapping_id": "$mappingIri"} + |} """.stripMargin - Put(s"/v1/values/${URLEncoder.encode(textValueIri.get, "UTF-8")}", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Put( + s"/v1/values/${URLEncoder.encode(textValueIri.get, "UTF-8")}", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields @@ -264,10 +272,10 @@ class ValuesV1R2RSpec extends R2RSpec { } "get the version history of a value" in { - Get(s"/v1/values/history/${URLEncoder.encode("http://rdfh.ch/0001/a-thing", "UTF-8")}/${URLEncoder.encode( - "http://www.knora.org/ontology/0001/anything#hasText", - "UTF-8")}/${URLEncoder.encode(textValueIri.get, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Get( + s"/v1/values/history/${URLEncoder.encode("http://rdfh.ch/0001/a-thing", "UTF-8")}/${URLEncoder + .encode("http://www.knora.org/ontology/0001/anything#hasText", "UTF-8")}/${URLEncoder.encode(textValueIri.get, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val versionHistory: JsValue = AkkaHttpUtils.httpResponseToJson(response).fields("valueVersions") @@ -279,14 +287,16 @@ class ValuesV1R2RSpec extends R2RSpec { assert( mostRecentVersion("previousValue").asInstanceOf[JsString].value == originalVersion("valueObjectIri") .asInstanceOf[JsString] - .value) + .value + ) assert(originalVersion("previousValue") == JsNull) } } "mark as deleted a text value containing a standoff reference to another resource" in { - Delete(s"/v1/values/${URLEncoder.encode(textValueIri.get, "UTF-8")}?deleteComment=deleted%20for%20testing") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Delete( + s"/v1/values/${URLEncoder.encode(textValueIri.get, "UTF-8")}?deleteComment=deleted%20for%20testing" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) } } @@ -294,15 +304,16 @@ class ValuesV1R2RSpec extends R2RSpec { "add a link value to a resource" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasOtherThing", - | "link_value": "http://rdfh.ch/0001/another-thing" - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasOtherThing", + | "link_value": "http://rdfh.ch/0001/another-thing" + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields val valueIri: IRI = responseJson("id").asInstanceOf[JsString].value @@ -311,8 +322,9 @@ class ValuesV1R2RSpec extends R2RSpec { } "mark a link value as deleted" in { - Delete(s"/v1/values/${URLEncoder.encode(linkValueIri.get, "UTF-8")}?deleteComment=deleted%20for%20testing") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Delete( + s"/v1/values/${URLEncoder.encode(linkValueIri.get, "UTF-8")}?deleteComment=deleted%20for%20testing" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) } } @@ -320,16 +332,17 @@ class ValuesV1R2RSpec extends R2RSpec { "add a link value with a comment to a resource" in { val params = s""" - |{ - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasOtherThing", - | "link_value": "http://rdfh.ch/0001/another-thing", - | "comment":"$boringComment" - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasOtherThing", + | "link_value": "http://rdfh.ch/0001/another-thing", + | "comment":"$boringComment" + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields val valueIri: IRI = responseJson("id").asInstanceOf[JsString].value @@ -338,10 +351,10 @@ class ValuesV1R2RSpec extends R2RSpec { } "get a link value with a comment" in { - Get(s"/v1/links/${URLEncoder.encode("http://rdfh.ch/0001/a-thing", "UTF-8")}/${URLEncoder.encode( - "http://www.knora.org/ontology/0001/anything#hasOtherThing", - "UTF-8")}/${URLEncoder.encode("http://rdfh.ch/0001/another-thing", "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Get( + s"/v1/links/${URLEncoder.encode("http://rdfh.ch/0001/a-thing", "UTF-8")}/${URLEncoder + .encode("http://www.knora.org/ontology/0001/anything#hasOtherThing", "UTF-8")}/${URLEncoder.encode("http://rdfh.ch/0001/another-thing", "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseObj = AkkaHttpUtils.httpResponseToJson(response).fields @@ -362,15 +375,16 @@ class ValuesV1R2RSpec extends R2RSpec { "add a text value with language to a resource" in { val params = """ - |{ - | "res_id": "http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": {"utf8str":"Guten Tag", "language": "de"} - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": {"utf8str":"Guten Tag", "language": "de"} + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, testPass) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields assert(responseJson("value").asInstanceOf[JsObject].fields("utf8str").toString.contains("Guten Tag")) @@ -384,16 +398,17 @@ class ValuesV1R2RSpec extends R2RSpec { val params = s""" - |{ - | "res_id": "http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": {"utf8str": "Salam", "language": "fa"} - |} + |{ + | "res_id": "http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": {"utf8str": "Salam", "language": "fa"} + |} """.stripMargin - Put(s"/v1/values/${URLEncoder.encode(textValueWithLangIri.get, "UTF-8")}", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { + Put( + s"/v1/values/${URLEncoder.encode(textValueWithLangIri.get, "UTF-8")}", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, testPass)) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJson: Map[String, JsValue] = responseAs[String].parseJson.asJsObject.fields assert(responseJson("value").asInstanceOf[JsObject].fields("utf8str").toString.contains("Salam")) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala index 3d9fad45af..5d5455de05 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala @@ -42,10 +42,10 @@ object AuthenticationV2E2ESpec { } /** - * End-to-End (E2E) test specification for testing authentication. - * - * This spec tests the 'v1/authentication' and 'v1/session' route. - */ + * End-to-End (E2E) test specification for testing authentication. + * + * This spec tests the 'v1/authentication' and 'v1/session' route. + */ class AuthenticationV2E2ESpec extends E2ESpec(AuthenticationV2E2ESpec.config) with AuthenticationV2JsonProtocol @@ -127,8 +127,8 @@ class AuthenticationV2E2ESpec "fail authentication with the user set as 'not active' " in { /* User not active */ - val request = Get(baseApiUrl + s"/v2/authentication") ~> addCredentials( - BasicHttpCredentials(inactiveUserEmail, testPass)) + val request = + Get(baseApiUrl + s"/v2/authentication") ~> addCredentials(BasicHttpCredentials(inactiveUserEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) logger.debug(s"response: ${response.toString}") assert(response.status === StatusCodes.Unauthorized) @@ -143,10 +143,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "iri": "$rootIri", - | "password": "$testPass" - |} + |{ + | "iri": "$rootIri", + | "password": "$testPass" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -167,10 +167,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "email": "$rootEmail", - | "password": "$testPass" - |} + |{ + | "email": "$rootEmail", + | "password": "$testPass" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -189,10 +189,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "username": "$rootUsername", - | "password": "$testPass" - |} + |{ + | "username": "$rootUsername", + | "password": "$testPass" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -209,8 +209,8 @@ class AuthenticationV2E2ESpec "authenticate with token in header" in { // authenticate by calling '/v2/authenticate' without parameters but by providing token (from earlier login) in authorization header - val request = Get(baseApiUrl + "/v2/authentication") ~> addCredentials( - GenericHttpCredentials("Bearer", token.get)) + val request = + Get(baseApiUrl + "/v2/authentication") ~> addCredentials(GenericHttpCredentials("Bearer", token.get)) val response = singleAwaitingRequest(request) logger.debug("response: {}", response.toString()) assert(response.status === StatusCodes.OK) @@ -226,8 +226,8 @@ class AuthenticationV2E2ESpec "logout when providing token in header" in { // do logout with stored token - val request = Delete(baseApiUrl + "/v2/authentication?") ~> addCredentials( - GenericHttpCredentials("Bearer", token.get)) + val request = + Delete(baseApiUrl + "/v2/authentication?") ~> addCredentials(GenericHttpCredentials("Bearer", token.get)) val response = singleAwaitingRequest(request) // logger.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.OK) @@ -237,10 +237,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "email": "$rootEmail", - | "password": "$testPass" - |} + |{ + | "email": "$rootEmail", + | "password": "$testPass" + |} """.stripMargin val request1 = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -255,8 +255,8 @@ class AuthenticationV2E2ESpec } "fail with authentication when providing the token after logout" in { - val request = Get(baseApiUrl + "/v2/authentication") ~> addCredentials( - GenericHttpCredentials("Bearer", token.get)) + val request = + Get(baseApiUrl + "/v2/authentication") ~> addCredentials(GenericHttpCredentials("Bearer", token.get)) val response = singleAwaitingRequest(request) // logger.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.Unauthorized) @@ -267,10 +267,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "email": "$rootEmail", - | "password": "wrong" - |} + |{ + | "email": "$rootEmail", + | "password": "wrong" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -283,10 +283,10 @@ class AuthenticationV2E2ESpec /* wrong username */ val params = s""" - |{ - | "username": "wrong", - | "password": "wrong" - |} + |{ + | "username": "wrong", + | "password": "wrong" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -319,10 +319,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "email": "$rootEmail", - | "password": "$testPass" - |} + |{ + | "email": "$rootEmail", + | "password": "$testPass" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -358,8 +358,8 @@ class AuthenticationV2E2ESpec "allow access using HTTP Bearer Auth header and token from v2" in { /* Correct email / correct password */ - val request = Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials( - GenericHttpCredentials("Bearer", token.get)) + val request = + Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials(GenericHttpCredentials("Bearer", token.get)) val response = singleAwaitingRequest(request) // logger.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.OK) @@ -367,8 +367,8 @@ class AuthenticationV2E2ESpec "fail with authentication using HTTP Bearer Auth header and wrong token " in { /* Correct email / wrong password */ - val request = Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials( - GenericHttpCredentials("Bearer", "123456")) + val request = + Get(baseApiUrl + s"/v1/users/$rootIriEnc") ~> addCredentials(GenericHttpCredentials("Bearer", "123456")) val response = singleAwaitingRequest(request) // logger.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.Unauthorized) @@ -396,10 +396,10 @@ class AuthenticationV2E2ESpec val params = s""" - |{ - | "email": "$rootEmail", - | "password": "$testPass" - |} + |{ + | "email": "$rootEmail", + | "password": "$testPass" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -435,8 +435,8 @@ class AuthenticationV2E2ESpec "allow access using HTTP Bearer Auth header with token from v2" in { /* Correct token */ - val request = Get(baseApiUrl + s"/admin/users/iri/$rootIriEnc") ~> addCredentials( - GenericHttpCredentials("Bearer", token.get)) + val request = + Get(baseApiUrl + s"/admin/users/iri/$rootIriEnc") ~> addCredentials(GenericHttpCredentials("Bearer", token.get)) val response = singleAwaitingRequest(request) // logger.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.OK) @@ -444,8 +444,8 @@ class AuthenticationV2E2ESpec "fail with authentication using HTTP Bearer Auth header with wrong token " in { /* Wrong token */ - val request = Get(baseApiUrl + s"/admin/users/iri/$rootIriEnc") ~> addCredentials( - GenericHttpCredentials("Bearer", "123456")) + val request = + Get(baseApiUrl + s"/admin/users/iri/$rootIriEnc") ~> addCredentials(GenericHttpCredentials("Bearer", "123456")) val response = singleAwaitingRequest(request) // logger.debug("==>> " + responseAs[String]) assert(response.status === StatusCodes.Unauthorized) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala index 5e6819890a..299f47afd7 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala @@ -35,14 +35,14 @@ import spray.json._ import scala.concurrent.ExecutionContextExecutor /** - * End-to-end specification for the handling of JSONLD documents. - */ + * End-to-end specification for the handling of JSONLD documents. + */ class JSONLDHandlingV2R2RSpec extends R2RSpec { override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin private val resourcesPath = new ResourcesRouteV2(routeData).knoraApiPath @@ -70,12 +70,17 @@ class JSONLDHandlingV2R2RSpec extends R2RSpec { // expected result after expansion val expectedJsonldExpandedParsed = JsonLDUtil.parseJsonLD( - readOrWriteTextFile("", - Paths.get("test_data/resourcesR2RV2/NarrenschiffFirstPageExpanded.jsonld"), - writeFile = false)) - - compareParsedJSONLDForResourcesResponse(expectedResponse = expectedJsonldExpandedParsed, - receivedResponse = jsonldParsedExpanded) + readOrWriteTextFile( + "", + Paths.get("test_data/resourcesR2RV2/NarrenschiffFirstPageExpanded.jsonld"), + writeFile = false + ) + ) + + compareParsedJSONLDForResourcesResponse( + expectedResponse = expectedJsonldExpandedParsed, + receivedResponse = jsonldParsedExpanded + ) } @@ -89,9 +94,12 @@ class JSONLDHandlingV2R2RSpec extends R2RSpec { val expectedJson: JsObject = JsonParser( - readOrWriteTextFile("", - Paths.get("test_data/resourcesR2RV2/NarrenschiffFirstPage.jsonld"), - writeFile = false)).asJsObject + readOrWriteTextFile( + "", + Paths.get("test_data/resourcesR2RV2/NarrenschiffFirstPage.jsonld"), + writeFile = false + ) + ).asJsObject assert(receivedJson.fields("@context") == expectedJson.fields("@context"), "@context incorrect") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala index 9aca7c615f..48717c1811 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala @@ -37,15 +37,15 @@ import spray.json.{JsValue, JsonParser} import scala.concurrent.ExecutionContextExecutor /** - * End-to-end test specification for the lists endpoint. This specification uses the Spray Testkit as documented - * here: http://spray.io/documentation/1.2.2/spray-testkit/ - */ + * End-to-end test specification for the lists endpoint. This specification uses the Spray Testkit as documented + * here: http://spray.io/documentation/1.2.2/spray-testkit/ + */ class ListsRouteV2R2RSpec extends R2RSpec { override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin private val listsPath = new ListsRouteV2(routeData).knoraApiPath diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/MarkupHeader.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/MarkupHeader.scala index 30d4516386..1f0ddcf56e 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/MarkupHeader.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/MarkupHeader.scala @@ -25,11 +25,11 @@ import org.knora.webapi.routing.RouteUtilV2 import scala.util.Try /** - * A custom Akka HTTP header representing [[RouteUtilV2.MARKUP_HEADER]], which a client can send to specify - * how text markup should be returned in an API response. - * - * The definition follows [[https://doc.akka.io/docs/akka-http/current/common/http-model.html#custom-headers]]. - */ + * A custom Akka HTTP header representing [[RouteUtilV2.MARKUP_HEADER]], which a client can send to specify + * how text markup should be returned in an API response. + * + * The definition follows [[https://doc.akka.io/docs/akka-http/current/common/http-model.html#custom-headers]]. + */ final class MarkupHeader(token: String) extends ModeledCustomHeader[MarkupHeader] { override def renderInRequests = true override def renderInResponses = true diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/MetadataRouteV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/MetadataRouteV2E2ESpec.scala index 267c2815c2..461a99b928 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/MetadataRouteV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/MetadataRouteV2E2ESpec.scala @@ -30,8 +30,10 @@ class MetadataRouteV2E2ESpec extends E2ESpec { "The metadata v2 endpoint" should { "perform a put request for the metadata of beol project given as Turtle" in { - val request = Put(s"$baseApiUrl/v2/metadata/${URLEncoder.encode(beolProjectIRI, "UTF-8")}", - HttpEntity(RdfMediaTypes.`text/turtle`, metadataAsTurtle)) ~> + val request = Put( + s"$baseApiUrl/v2/metadata/${URLEncoder.encode(beolProjectIRI, "UTF-8")}", + HttpEntity(RdfMediaTypes.`text/turtle`, metadataAsTurtle) + ) ~> addCredentials(BasicHttpCredentials(beolUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) @@ -41,8 +43,10 @@ class MetadataRouteV2E2ESpec extends E2ESpec { } "perform a put request for the metadata of beol project given as JSON-LD" in { - val request = Put(s"$baseApiUrl/v2/metadata/${URLEncoder.encode(beolProjectIRI, "UTF-8")}", - HttpEntity(RdfMediaTypes.`application/json`, metadataAsFlatJsonLD)) ~> + val request = Put( + s"$baseApiUrl/v2/metadata/${URLEncoder.encode(beolProjectIRI, "UTF-8")}", + HttpEntity(RdfMediaTypes.`application/json`, metadataAsFlatJsonLD) + ) ~> addCredentials(BasicHttpCredentials(beolUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) @@ -91,9 +95,9 @@ class MetadataRouteV2E2ESpec extends E2ESpec { } "not create metadata for an invalid project IRI" in { - val request = Put(s"$baseApiUrl/v2/metadata/invalid-projectIRI", - HttpEntity(RdfMediaTypes.`text/turtle`, metadataAsTurtle)) ~> - addCredentials(BasicHttpCredentials(beolUserEmail, password)) + val request = + Put(s"$baseApiUrl/v2/metadata/invalid-projectIRI", HttpEntity(RdfMediaTypes.`text/turtle`, metadataAsTurtle)) ~> + addCredentials(BasicHttpCredentials(beolUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status.isFailure()) @@ -103,8 +107,8 @@ class MetadataRouteV2E2ESpec extends E2ESpec { val turtleType = RdfMediaTypes.`text/turtle`.toString() val header = RawHeader("Accept", turtleType) val request = Get( - s"$baseApiUrl/v2/metadata/${URLEncoder.encode(SharedTestDataADM.INCUNABULA_PROJECT_IRI, "UTF-8")}") ~> addHeader( - header) + s"$baseApiUrl/v2/metadata/${URLEncoder.encode(SharedTestDataADM.INCUNABULA_PROJECT_IRI, "UTF-8")}" + ) ~> addHeader(header) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.NotFound) } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala index 909d6f9464..7df355afd9 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyModels.scala @@ -37,7 +37,7 @@ object CreateClassRequest { comment: LangString ): CreateClassRequest = { val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" - val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" + val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" val value = s"""{ | "@id" : "$ontologyId", @@ -101,8 +101,8 @@ object CreatePropertyRequest { comment: LangString ): CreatePropertyRequest = { val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" - val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" - val value = s"""{ + val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" + val value = s"""{ | "@id" : "$ontologyId", | "@type" : "owl:Ontology", | "knora-api:lastModificationDate" : { @@ -151,7 +151,7 @@ sealed trait CardinalityRestriction { object CardinalityRestriction { case object MaxCardinalityOne extends CardinalityRestriction { val cardinality = "owl:maxCardinality" - val value = 1 + val value = 1 } } @@ -186,10 +186,10 @@ object AddCardinalitiesRequest { className: String, restrictions: List[Restriction] ): AddCardinalitiesRequest = { - val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" - val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" + val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" + val ontologyId = LocalHost_Ontology + s"/0001/$ontologyName/v2" val restrictionsString: String = stringifyRestrictions(restrictions) - val value = s""" + val value = s""" |{ | "@id" : "$ontologyId", | "@type" : "owl:Ontology", diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala index ee16ff4bcb..dcbc2efa63 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala @@ -25,10 +25,10 @@ import scala.concurrent.ExecutionContextExecutor object OntologyV2R2RSpec { private val anythingUserProfile = SharedTestDataADM.anythingAdminUser - private val anythingUsername = anythingUserProfile.email + private val anythingUsername = anythingUserProfile.email private val superUserProfile = SharedTestDataADM.superUser - private val superUsername = superUserProfile.email + private val superUsername = superUserProfile.email private val password = SharedTestDataADM.testPass } @@ -49,7 +49,7 @@ class OntologyV2R2RSpec extends R2RSpec { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance private val ontologiesPath = DSPApiDirectives.handleErrors(system)(new OntologiesRouteV2(routeData).knoraApiPath) - private val resourcesPath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) + private val resourcesPath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) @@ -112,7 +112,7 @@ class OntologyV2R2RSpec extends R2RSpec { // Per default only read access is allowed in the bazel sandbox. // This workaround allows to save test output. val testOutputDir = Paths.get(sys.env("TEST_UNDECLARED_OUTPUTS_DIR")) - val file = makeFile(mediaType) + val file = makeFile(mediaType) val newOutputFile = testOutputDir.resolve(file) Files.createDirectories(newOutputFile.getParent) FileUtil.writeTextFile(newOutputFile, responseStr) @@ -149,9 +149,9 @@ class OntologyV2R2RSpec extends R2RSpec { } // URL-encoded IRIs for use as URL segments in HTTP GET tests. - private val anythingProjectSegment = URLEncoder.encode(SharedTestDataADM.ANYTHING_PROJECT_IRI, "UTF-8") + private val anythingProjectSegment = URLEncoder.encode(SharedTestDataADM.ANYTHING_PROJECT_IRI, "UTF-8") private val incunabulaProjectSegment = URLEncoder.encode(SharedTestDataADM.INCUNABULA_PROJECT_IRI, "UTF-8") - private val beolProjectSegment = URLEncoder.encode(SharedTestDataADM.BEOL_PROJECT_IRI, "UTF-8") + private val beolProjectSegment = URLEncoder.encode(SharedTestDataADM.BEOL_PROJECT_IRI, "UTF-8") private val knoraApiSimpleOntologySegment = URLEncoder.encode(OntologyConstants.KnoraApiV2Simple.KnoraApiOntologyIri, "UTF-8") private val knoraApiWithValueObjectsOntologySegment = @@ -333,15 +333,15 @@ class OntologyV2R2RSpec extends R2RSpec { RdfMediaTypes.`application/rdf+xml` ) - private val fooIri = new MutableTestIri - private val barIri = new MutableTestIri + private val fooIri = new MutableTestIri + private val barIri = new MutableTestIri private var fooLastModDate: Instant = Instant.now private var barLastModDate: Instant = Instant.now private var anythingLastModDate: Instant = Instant.parse("2017-12-19T15:23:42.166Z") private var freetestLastModDate: Instant = Instant.parse("2012-12-12T12:12:12.12Z") - private val uselessIri = new MutableTestIri + private val uselessIri = new MutableTestIri private var uselessLastModDate: Instant = Instant.now private def getPropertyIrisFromResourceClassResponse(responseJsonDoc: JsonLDDocument): Set[SmartIri] = { @@ -381,7 +381,7 @@ class OntologyV2R2RSpec extends R2RSpec { val existingFile: Path = httpGetTest.makeFile(mediaType) if (Files.exists(existingFile)) { - val parsedResponse: RdfModel = parseRdfXml(responseStr) + val parsedResponse: RdfModel = parseRdfXml(responseStr) val parsedExistingFile: RdfModel = parseRdfXml(httpGetTest.readFile(mediaType)) if (parsedResponse != parsedExistingFile) { @@ -470,8 +470,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://0.0.0.0:3333/ontology/0001/foo/v2") fooIri.set(ontologyIri) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(label)) @@ -498,7 +498,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "create an empty ontology called 'bar' with a comment" in { - val label = "The bar ontology" + val label = "The bar ontology" val comment = "some comment" val params = @@ -532,8 +532,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://0.0.0.0:3333/ontology/0001/bar/v2") assert( metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString( @@ -562,7 +562,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "create an empty ontology called 'test' with a comment that has a special character" in { - val label = "The test ontology" + val label = "The test ontology" val comment = "some \\\"test\\\" comment" val params = @@ -585,8 +585,8 @@ class OntologyV2R2RSpec extends R2RSpec { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val responseJsonDoc = JsonLDUtil.parseJsonLD(responseStr) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://0.0.0.0:3333/ontology/0001/test/v2") assert( metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString( @@ -597,7 +597,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "change the metadata of 'foo'" in { - val newLabel = "The modified foo ontology" + val newLabel = "The modified foo ontology" val newComment = "new comment" val params = @@ -632,8 +632,8 @@ class OntologyV2R2RSpec extends R2RSpec { ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == fooIri.get) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(newLabel)) assert(metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString(newComment)) @@ -672,8 +672,8 @@ class OntologyV2R2RSpec extends R2RSpec { ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == barIri.get) assert( metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString( @@ -693,7 +693,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the comment from 'foo'" in { - val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") + val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") val lastModificationDate = URLEncoder.encode(fooLastModDate.toString, "UTF-8") Delete(s"/v2/ontologies/comment/$fooIriEncoded?lastModificationDate=$lastModificationDate") ~> addCredentials( @@ -701,8 +701,8 @@ class OntologyV2R2RSpec extends R2RSpec { ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == fooIri.get) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString("The modified foo ontology")) assert(!metadata.value.contains(OntologyConstants.Rdfs.Comment)) @@ -744,7 +744,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the 'foo' ontology" in { - val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") + val fooIriEncoded = URLEncoder.encode(fooIri.get, "UTF-8") val lastModificationDate = URLEncoder.encode(fooLastModDate.toString, "UTF-8") Delete(s"/v2/ontologies/$fooIriEncoded?lastModificationDate=$lastModificationDate") ~> addCredentials( @@ -1789,7 +1789,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the property anything:hasOtherNothing" in { - val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothing", "UTF-8") + val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherNothing", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") Delete( @@ -2141,7 +2141,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the property anything:hasNothingness" in { - val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasNothingness", "UTF-8") + val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasNothingness", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") Delete( @@ -2222,7 +2222,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the property anything:hasEmptiness" in { - val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasEmptiness", "UTF-8") + val propertySegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#hasEmptiness", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") Delete( @@ -2259,7 +2259,7 @@ class OntologyV2R2RSpec extends R2RSpec { } "delete the class anything:Nothing" in { - val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Nothing", "UTF-8") + val classSegment = URLEncoder.encode("http://0.0.0.0:3333/ontology/0001/anything/v2#Nothing", "UTF-8") val lastModificationDate = URLEncoder.encode(anythingLastModDate.toString, "UTF-8") Delete(s"/v2/ontologies/classes/$classSegment?lastModificationDate=$lastModificationDate") ~> addCredentials( @@ -2306,8 +2306,8 @@ class OntologyV2R2RSpec extends R2RSpec { ) ~> ontologiesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) - val metadata = responseJsonDoc.body - val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value + val metadata = responseJsonDoc.body + val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value assert(ontologyIri == "http://api.knora.org/ontology/shared/useless/v2") uselessIri.set(ontologyIri) assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(label)) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ProjectHeader.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ProjectHeader.scala index ef3157234a..f1df7881b1 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ProjectHeader.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ProjectHeader.scala @@ -25,11 +25,11 @@ import org.knora.webapi.routing.RouteUtilV2 import scala.util.Try /** - * A custom Akka HTTP header representing [[RouteUtilV2.PROJECT_HEADER]], which a client can send to specify - * a project from which results should be returned. - * - * The definition follows [[https://doc.akka.io/docs/akka-http/current/common/http-model.html#custom-headers]]. - */ + * A custom Akka HTTP header representing [[RouteUtilV2.PROJECT_HEADER]], which a client can send to specify + * a project from which results should be returned. + * + * The definition follows [[https://doc.akka.io/docs/akka-http/current/common/http-model.html#custom-headers]]. + */ class ProjectHeader(token: String) extends ModeledCustomHeader[ProjectHeader] { override def renderInRequests = true override def renderInResponses = true diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala index 882f24ca9a..e9ffc73da0 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala @@ -49,8 +49,8 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContextExecutor} /** - * Tests the API v2 resources route. - */ + * Tests the API v2 resources route. + */ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -83,16 +83,18 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { private def successResponse(message: String): String = s"""{ - | "knora-api:result" : "$message", - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#" - | } - |}""".stripMargin + | "knora-api:result" : "$message", + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#" + | } + |}""".stripMargin - private def updateResourceMetadataResponse(newLastModificationDate: Instant, - maybeNewLabel: String, - resourceIri: String, - maybeNewPermissions: String): String = + private def updateResourceMetadataResponse( + newLastModificationDate: Instant, + maybeNewLabel: String, + resourceIri: String, + maybeNewPermissions: String + ): String = s"""{ | "knora-api:lastModificationDate": { | "@value": "$newLastModificationDate", @@ -118,9 +120,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -134,9 +138,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "perform a resource request for the book 'Reise ins Heilige Land' using the complex schema in Turtle" in { // Test correct handling of q values in the Accept header. val acceptHeader: Accept = Accept( - MediaRange.One(RdfMediaTypes.`application/ld+json`, 0.5F), - MediaRange.One(RdfMediaTypes.`text/turtle`, 0.8F), - MediaRange.One(RdfMediaTypes.`application/rdf+xml`, 0.2F) + MediaRange.One(RdfMediaTypes.`application/ld+json`, 0.5f), + MediaRange.One(RdfMediaTypes.`text/turtle`, 0.8f), + MediaRange.One(RdfMediaTypes.`application/rdf+xml`, 0.2f) ) val request = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0803/2a6221216701", "UTF-8")}") @@ -144,9 +148,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerTurtle = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.ttl"), - writeTestDataFiles) + val expectedAnswerTurtle = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.ttl"), + writeTestDataFiles + ) assert(parseTurtle(responseAsString) == parseTurtle(expectedAnswerTurtle)) } @@ -156,9 +162,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerRdfXml = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.rdf"), - writeTestDataFiles) + val expectedAnswerRdfXml = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.rdf"), + writeTestDataFiles + ) assert(parseRdfXml(responseAsString) == parseRdfXml(expectedAnswerRdfXml)) } @@ -169,9 +177,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandPreview.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandPreview.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } @@ -203,9 +213,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -224,9 +236,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerTurtle = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.ttl"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.ttl"), + writeTestDataFiles + ) assert(parseTurtle(responseAsString) == parseTurtle(expectedAnswerTurtle)) } @@ -238,9 +252,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerRdfXml = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.rdf"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.rdf"), + writeTestDataFiles + ) assert(parseRdfXml(responseAsString) == parseRdfXml(expectedAnswerRdfXml)) } @@ -252,22 +268,27 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimplePreview.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimplePreview.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } "perform a resource request for the book 'Reise ins Heilige Land' using the simple schema (specified by a URL parameter)" in { val request = Get( - s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0803/2a6221216701", "UTF-8")}?${RouteUtilV2.SCHEMA_PARAM}=${RouteUtilV2.SIMPLE_SCHEMA_NAME}") + s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0803/2a6221216701", "UTF-8")}?${RouteUtilV2.SCHEMA_PARAM}=${RouteUtilV2.SIMPLE_SCHEMA_NAME}" + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLandSimple.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } @@ -276,9 +297,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/NarrenschiffFirstPage.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/NarrenschiffFirstPage.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } @@ -288,9 +311,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithBCEDate.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithBCEDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -307,9 +332,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithBCEDate2.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithBCEDate2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -326,9 +353,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithListValue.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithListValue.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -347,9 +376,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithListValueSimple.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithListValueSimple.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -366,9 +397,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithLinkComplex.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithLinkComplex.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -386,9 +419,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithLinkSimple.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithLinkSimple.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -401,14 +436,17 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "perform a full resource request for a resource with a Text language (in the complex schema)" in { val request = Get( - s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", "UTF-8")}") + s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", "UTF-8")}" + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithTextLangComplex.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithTextLangComplex.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -421,15 +459,18 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "perform a full resource request for a resource with a Text language (in the simple schema)" in { val request = Get( - s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", "UTF-8")}") + s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0001/a-thing-with-text-valuesLanguage", "UTF-8")}" + ) .addHeader(new SchemaHeader(RouteUtilV2.SIMPLE_SCHEMA_NAME)) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithTextLangSimple.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithTextLangSimple.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -475,9 +516,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithPicture.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithPicture.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) clientTestDataCollector.addFile( @@ -507,9 +550,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithOneHiddenResource.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithOneHiddenResource.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -528,9 +573,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithOneDeletedResource.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithOneDeletedResource.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) // Check that the resource corresponds to the ontology. @@ -544,28 +591,34 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "perform a full resource request for a past version of a resource, using a URL-encoded xsd:dateTimeStamp" in { val request = Get( s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0001/thing-with-history", "UTF-8")}?version=${URLEncoder - .encode("2019-02-12T08:05:10.351Z", "UTF-8")}") + .encode("2019-02-12T08:05:10.351Z", "UTF-8")}" + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithVersionHistory.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithVersionHistory.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } "perform a full resource request for a past version of a resource, using a Knora ARK timestamp" in { val request = Get( s"$baseApiUrl/v2/resources/${URLEncoder.encode("http://rdfh.ch/0001/thing-with-history", "UTF-8")}?version=${URLEncoder - .encode("20190212T080510351Z", "UTF-8")}") + .encode("20190212T080510351Z", "UTF-8")}" + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingWithVersionHistory.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingWithVersionHistory.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } @@ -576,9 +629,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/CompleteVersionHistory.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/CompleteVersionHistory.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourceHistoryResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } @@ -590,9 +645,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/PartialVersionHistory.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/PartialVersionHistory.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourceHistoryResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } @@ -620,11 +677,15 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val versionResponseAsString = responseToString(versionResponse) assert(versionResponse.status == StatusCodes.OK, versionResponseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(versionResponseAsString, - Paths.get(s"test_data/resourcesR2RV2/ThingWithVersionHistory$arkTimestamp.jsonld"), - writeTestDataFiles) - compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, - receivedJSONLD = versionResponseAsString) + readOrWriteTextFile( + versionResponseAsString, + Paths.get(s"test_data/resourcesR2RV2/ThingWithVersionHistory$arkTimestamp.jsonld"), + writeTestDataFiles + ) + compareJSONLDForResourcesResponse( + expectedJSONLD = expectedAnswerJSONLD, + receivedJSONLD = versionResponseAsString + ) case other => throw AssertionException(s"Expected JsonLDObject, got $other") } @@ -669,9 +730,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(response.status == StatusCodes.OK, responseAsString) val parsedReceivedJsonLD = JsonLDUtil.parseJsonLD(responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingGraphBoth.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingGraphBoth.jsonld"), + writeTestDataFiles + ) val parsedExpectedJsonLD = JsonLDUtil.parseJsonLD(expectedAnswerJSONLD) assert(parsedReceivedJsonLD == parsedExpectedJsonLD) @@ -685,9 +748,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(response.status == StatusCodes.OK, responseAsString) val parsedReceivedJsonLD = JsonLDUtil.parseJsonLD(responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingGraphOutbound.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingGraphOutbound.jsonld"), + writeTestDataFiles + ) val parsedExpectedJsonLD = JsonLDUtil.parseJsonLD(expectedAnswerJSONLD) assert(parsedReceivedJsonLD == parsedExpectedJsonLD) @@ -701,9 +766,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(response.status == StatusCodes.OK, responseAsString) val parsedReceivedJsonLD = JsonLDUtil.parseJsonLD(responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingGraphInbound.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingGraphInbound.jsonld"), + writeTestDataFiles + ) val parsedExpectedJsonLD = JsonLDUtil.parseJsonLD(expectedAnswerJSONLD) assert(parsedReceivedJsonLD == parsedExpectedJsonLD) @@ -712,16 +779,19 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "return a graph of resources reachable via links to/from a given resource, excluding a specified property" in { val request = Get( s"$baseApiUrl/v2/graph/${URLEncoder.encode("http://rdfh.ch/0001/start", "UTF-8")}?direction=both&excludeProperty=${URLEncoder - .encode("http://0.0.0.0:3333/ontology/0001/anything/v2#isPartOfOtherThing", "UTF-8")}") + .encode("http://0.0.0.0:3333/ontology/0001/anything/v2#isPartOfOtherThing", "UTF-8")}" + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) val parsedReceivedJsonLD = JsonLDUtil.parseJsonLD(responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingGraphBothWithExcludedProp.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingGraphBothWithExcludedProp.jsonld"), + writeTestDataFiles + ) val parsedExpectedJsonLD = JsonLDUtil.parseJsonLD(expectedAnswerJSONLD) assert(parsedReceivedJsonLD == parsedExpectedJsonLD) @@ -736,9 +806,11 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val parsedReceivedJsonLD = JsonLDUtil.parseJsonLD(responseAsString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/ThingGraphBothWithDepth.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/ThingGraphBothWithDepth.jsonld"), + writeTestDataFiles + ) val parsedExpectedJsonLD = JsonLDUtil.parseJsonLD(expectedAnswerJSONLD) assert(parsedReceivedJsonLD == parsedExpectedJsonLD) @@ -761,7 +833,8 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "not accept a graph request with an invalid depth (> max)" in { val request = Get( - s"$baseApiUrl/v2/graph/${URLEncoder.encode("http://rdfh.ch/0001/start", "UTF-8")}?depth=${settings.maxGraphBreadth + 1}") + s"$baseApiUrl/v2/graph/${URLEncoder.encode("http://rdfh.ch/0001/start", "UTF-8")}?depth=${settings.maxGraphBreadth + 1}" + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -778,123 +851,126 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "return resources from a project" in { val resourceClass = URLEncoder.encode("http://0.0.0.0:3333/ontology/0803/incunabula/v2#book", "UTF-8") val orderByProperty = URLEncoder.encode("http://0.0.0.0:3333/ontology/0803/incunabula/v2#title", "UTF-8") - val request = Get( - s"$baseApiUrl/v2/resources?resourceClass=$resourceClass&orderByProperty=$orderByProperty&page=0") - .addHeader(new ProjectHeader(SharedTestDataADM.incunabulaProject.id)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.incunabulaProjectAdminUser.email, password)) + val request = + Get(s"$baseApiUrl/v2/resources?resourceClass=$resourceClass&orderByProperty=$orderByProperty&page=0") + .addHeader(new ProjectHeader(SharedTestDataADM.incunabulaProject.id)) ~> addCredentials( + BasicHttpCredentials(SharedTestDataADM.incunabulaProjectAdminUser.email, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.OK, responseAsString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAsString, - Paths.get("test_data/resourcesR2RV2/BooksFromIncunabula.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAsString, + Paths.get("test_data/resourcesR2RV2/BooksFromIncunabula.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAsString) } "create a resource with values" in { val createResourceWithValues: String = """{ - | "@type" : "anything:Thing", - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "anything:hasColor" : { - | "@type" : "knora-api:ColorValue", - | "knora-api:colorValueAsColor" : "#ff3333" - | }, - | "anything:hasDate" : { - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "GREGORIAN", - | "knora-api:dateValueHasEndEra" : "CE", - | "knora-api:dateValueHasEndYear" : 1489, - | "knora-api:dateValueHasStartEra" : "CE", - | "knora-api:dateValueHasStartYear" : 1489 - | }, - | "anything:hasDecimal" : { - | "@type" : "knora-api:DecimalValue", - | "knora-api:decimalValueAsDecimal" : { - | "@type" : "xsd:decimal", - | "@value" : "100000000000000.000000000000001" - | } - | }, - | "anything:hasGeometry" : { - | "@type" : "knora-api:GeomValue", - | "knora-api:geometryValueAsGeometry" : "{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.08098591549295775,\"y\":0.16741071428571427},{\"x\":0.7394366197183099,\"y\":0.7299107142857143}],\"type\":\"rectangle\",\"original_index\":0}" - | }, - | "anything:hasGeoname" : { - | "@type" : "knora-api:GeonameValue", - | "knora-api:geonameValueAsGeonameCode" : "2661604" - | }, - | "anything:hasInteger" : [ { - | "@type" : "knora-api:IntValue", - | "knora-api:hasPermissions" : "CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher", - | "knora-api:intValueAsInt" : 5, - | "knora-api:valueHasComment" : "this is the number five" - | }, { - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : 6 - | } ], - | "anything:hasInterval" : { - | "@type" : "knora-api:IntervalValue", - | "knora-api:intervalValueHasEnd" : { - | "@type" : "xsd:decimal", - | "@value" : "3.4" - | }, - | "knora-api:intervalValueHasStart" : { - | "@type" : "xsd:decimal", - | "@value" : "1.2" - | } - | }, - | "anything:hasTimeStamp" : { - | "@type" : "knora-api:TimeValue", - | "knora-api:timeValueAsTimeStamp" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2020-01-24T08:47:10.307068Z" - | } - | }, - | "anything:hasListItem" : { - | "@type" : "knora-api:ListValue", - | "knora-api:listValueAsListNode" : { - | "@id" : "http://rdfh.ch/lists/0001/treeList03" - | } - | }, - | "anything:hasOtherThingValue" : { - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "http://rdfh.ch/0001/a-thing" - | } - | }, - | "anything:hasRichtext" : { - | "@type" : "knora-api:TextValue", - | "knora-api:textValueAsXml" : "\n

this is text

with standoff
", - | "knora-api:textValueHasMapping" : { - | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping" - | } - | }, - | "anything:hasText" : { - | "@type" : "knora-api:TextValue", - | "knora-api:valueAsString" : "this is text without standoff" - | }, - | "anything:hasUri" : { - | "@type" : "knora-api:UriValue", - | "knora-api:uriValueAsUri" : { - | "@type" : "xsd:anyURI", - | "@value" : "https://www.knora.org" - | } - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "anything:hasColor" : { + | "@type" : "knora-api:ColorValue", + | "knora-api:colorValueAsColor" : "#ff3333" + | }, + | "anything:hasDate" : { + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "GREGORIAN", + | "knora-api:dateValueHasEndEra" : "CE", + | "knora-api:dateValueHasEndYear" : 1489, + | "knora-api:dateValueHasStartEra" : "CE", + | "knora-api:dateValueHasStartYear" : 1489 + | }, + | "anything:hasDecimal" : { + | "@type" : "knora-api:DecimalValue", + | "knora-api:decimalValueAsDecimal" : { + | "@type" : "xsd:decimal", + | "@value" : "100000000000000.000000000000001" + | } + | }, + | "anything:hasGeometry" : { + | "@type" : "knora-api:GeomValue", + | "knora-api:geometryValueAsGeometry" : "{\"status\":\"active\",\"lineColor\":\"#ff3333\",\"lineWidth\":2,\"points\":[{\"x\":0.08098591549295775,\"y\":0.16741071428571427},{\"x\":0.7394366197183099,\"y\":0.7299107142857143}],\"type\":\"rectangle\",\"original_index\":0}" + | }, + | "anything:hasGeoname" : { + | "@type" : "knora-api:GeonameValue", + | "knora-api:geonameValueAsGeonameCode" : "2661604" + | }, + | "anything:hasInteger" : [ { + | "@type" : "knora-api:IntValue", + | "knora-api:hasPermissions" : "CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher", + | "knora-api:intValueAsInt" : 5, + | "knora-api:valueHasComment" : "this is the number five" + | }, { + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : 6 + | } ], + | "anything:hasInterval" : { + | "@type" : "knora-api:IntervalValue", + | "knora-api:intervalValueHasEnd" : { + | "@type" : "xsd:decimal", + | "@value" : "3.4" + | }, + | "knora-api:intervalValueHasStart" : { + | "@type" : "xsd:decimal", + | "@value" : "1.2" + | } + | }, + | "anything:hasTimeStamp" : { + | "@type" : "knora-api:TimeValue", + | "knora-api:timeValueAsTimeStamp" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2020-01-24T08:47:10.307068Z" + | } + | }, + | "anything:hasListItem" : { + | "@type" : "knora-api:ListValue", + | "knora-api:listValueAsListNode" : { + | "@id" : "http://rdfh.ch/lists/0001/treeList03" + | } + | }, + | "anything:hasOtherThingValue" : { + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "http://rdfh.ch/0001/a-thing" + | } + | }, + | "anything:hasRichtext" : { + | "@type" : "knora-api:TextValue", + | "knora-api:textValueAsXml" : "\n

this is text

with standoff
", + | "knora-api:textValueHasMapping" : { + | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping" + | } + | }, + | "anything:hasText" : { + | "@type" : "knora-api:TextValue", + | "knora-api:valueAsString" : "this is text without standoff" + | }, + | "anything:hasUri" : { + | "@type" : "knora-api:UriValue", + | "knora-api:uriValueAsUri" : { + | "@type" : "xsd:anyURI", + | "@value" : "https://www.knora.org" + | } + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -907,9 +983,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, createResourceWithValues)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, createResourceWithValues) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -918,8 +995,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(resourceIri.toSmartIri.isKnoraDataIri) // Request the newly created resource in the complex schema, and check that it matches the ontology. - val resourceComplexGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceComplexGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceComplexGetResponse: HttpResponse = singleAwaitingRequest(resourceComplexGetRequest) val resourceComplexGetResponseAsString = responseToString(resourceComplexGetResponse) @@ -932,7 +1010,8 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { // Request the newly created resource in the simple schema, and check that it matches the ontology. val resourceSimpleGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") .addHeader(new SchemaHeader(RouteUtilV2.SIMPLE_SCHEMA_NAME)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + BasicHttpCredentials(anythingUserEmail, password) + ) val resourceSimpleGetResponse: HttpResponse = singleAwaitingRequest(resourceSimpleGetRequest) val resourceSimpleGetResponseAsString = responseToString(resourceSimpleGetResponse) @@ -952,8 +1031,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "create a resource whose label contains a Unicode escape and quotation marks" in { val jsonLDEntity: String = FileUtil.readTextFile(Paths.get("test_data/resourcesR2RV2/ThingWithUnicodeEscape.jsonld")) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -967,27 +1048,27 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity: String = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "rdfs:label" : "test thing", - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$creationDate" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "rdfs:label" : "test thing", + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$creationDate" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1000,8 +1081,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1018,27 +1101,26 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(savedCreationDate == creationDate) } - def createResourceWithCustomIRI(iri: IRI): String = { + def createResourceWithCustomIRI(iri: IRI): String = s"""{ - | "@id" : "$iri", - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } + | "@id" : "$iri", + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin "create a resource with a custom IRI" in { val customIRI: IRI = SharedTestDataADM.customResourceIRI @@ -1055,8 +1137,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1068,8 +1152,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "not create a resource with an invalid custom IRI" in { val customIRI: IRI = "http://rdfh.ch/invalid-resource-IRI" val jsonLDEntity = createResourceWithCustomIRI(customIRI) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) println(responseToString(response)) assert(response.status == StatusCodes.BadRequest, response.toString) @@ -1078,8 +1164,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "not create a resource with a custom IRI containing the wrong project code" in { val customIRI: IRI = "http://rdfh.ch/0803/a-thing-with-IRI" val jsonLDEntity = createResourceWithCustomIRI(customIRI) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) println(responseToString(response)) assert(response.status == StatusCodes.BadRequest, response.toString) @@ -1090,27 +1178,29 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { // duplicate resource IRI val params = s"""{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "rdfs:label" : "test thing with duplicate iri", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "rdfs:label" : "test thing with duplicate iri", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val request = + Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) @@ -1120,27 +1210,26 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { invalidIri should be(true) } - def createResourceWithCustomValueIRI(valueIRI: IRI): String = { + def createResourceWithCustomValueIRI(valueIRI: IRI): String = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@id" : "$valueIRI", - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "rdfs:label" : "test thing with value IRI", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@id" : "$valueIRI", + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "rdfs:label" : "test thing with value IRI", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin "create a resource with random IRI and a custom value IRI" in { val customValueIRI: IRI = SharedTestDataADM.customValueIRI @@ -1157,8 +1246,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1166,8 +1257,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) // Request the newly created resource. - val resourceGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceGetResponse: HttpResponse = singleAwaitingRequest(resourceGetRequest, duration = settings.triplestoreUpdateTimeout) val resourceGetResponseAsString = responseToString(resourceGetResponse) @@ -1185,24 +1277,24 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true, - | "knora-api:valueHasUUID" : "$customValueUUID" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true, + | "knora-api:valueHasUUID" : "$customValueUUID" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1215,8 +1307,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1224,8 +1318,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) // Request the newly created resource. - val resourceGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceGetResponse: HttpResponse = singleAwaitingRequest(resourceGetRequest, duration = settings.triplestoreUpdateTimeout) val resourceGetResponseAsString = responseToString(resourceGetResponse) @@ -1244,27 +1339,27 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : false, - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$creationDate" - | } - | }, - | "rdfs:label" : "test thing with value has creation date", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : false, + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$creationDate" + | } + | }, + | "rdfs:label" : "test thing with value has creation date", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1277,8 +1372,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1286,8 +1383,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) // Request the newly created resource. - val resourceGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceGetResponse: HttpResponse = singleAwaitingRequest(resourceGetRequest, duration = settings.triplestoreUpdateTimeout) val resourceGetResponseAsString = responseToString(resourceGetResponse) @@ -1313,30 +1411,30 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""{ - | "@id" : "$customResourceIRI", - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@id": "$customValueIRI", - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true, - | "knora-api:valueHasUUID" : "$customValueUUID" - | }, - | "rdfs:label" : "test thing", - | "knora-api:creationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$customCreationDate" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$customResourceIRI", + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@id": "$customValueIRI", + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true, + | "knora-api:valueHasUUID" : "$customValueUUID" + | }, + | "rdfs:label" : "test thing", + | "knora-api:creationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$customCreationDate" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1349,8 +1447,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) @@ -1360,8 +1460,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(resourceIri == customResourceIRI) // Request the newly created resource. - val resourceGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceGetResponse: HttpResponse = singleAwaitingRequest(resourceGetRequest, duration = settings.triplestoreUpdateTimeout) val resourceGetResponseAsString = responseToString(resourceGetResponse) @@ -1401,26 +1502,26 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "create a resource as another user" in { val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "rdfs:label" : "test thing", - | "knora-api:attachedToUser" : { - | "@id" : "${SharedTestDataADM.anythingUser1.id}" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "rdfs:label" : "test thing", + | "knora-api:attachedToUser" : { + | "@id" : "${SharedTestDataADM.anythingUser1.id}" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1433,8 +1534,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1442,37 +1545,41 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) assert(resourceIri.toSmartIri.isKnoraDataIri) val savedAttachedToUser: IRI = - responseJsonDoc.body.requireIriInObject(OntologyConstants.KnoraApiV2Complex.AttachedToUser, - stringFormatter.validateAndEscapeIri) + responseJsonDoc.body.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.AttachedToUser, + stringFormatter.validateAndEscapeIri + ) assert(savedAttachedToUser == SharedTestDataADM.anythingUser1.id) } "not create a resource as another user if the requesting user is an ordinary user" in { val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : true - | }, - | "rdfs:label" : "test thing", - | "knora-api:attachedToUser" : { - | "@id" : "${SharedTestDataADM.anythingUser1.id}" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.anythingUser2.email, password)) + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : true + | }, + | "rdfs:label" : "test thing", + | "knora-api:attachedToUser" : { + | "@id" : "${SharedTestDataADM.anythingUser1.id}" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingUser2.email, password)) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.Forbidden, responseAsString) @@ -1480,8 +1587,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "create a resource containing escaped text" in { val jsonLDEntity = FileUtil.readTextFile(Paths.get("test_data/resourcesR2RV2/CreateResourceWithEscape.jsonld")) - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1498,22 +1607,22 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""|{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "rdfs:label" : "$newLabel", - | "knora-api:hasPermissions" : "$newPermissions", - | "knora-api:newModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$newModificationDate" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "rdfs:label" : "$newLabel", + | "knora-api:hasPermissions" : "$newPermissions", + | "knora-api:newModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$newModificationDate" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1526,18 +1635,23 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val updateRequest = Put(s"$baseApiUrl/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val updateRequest = Put( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val updateResponse: HttpResponse = singleAwaitingRequest(updateRequest) val updateResponseAsString: String = responseToString(updateResponse) assert(updateResponse.status == StatusCodes.OK, updateResponseAsString) assert( JsonParser(updateResponseAsString) == JsonParser( - updateResourceMetadataResponse(resourceIri = resourceIri, - maybeNewLabel = newLabel, - newLastModificationDate = newModificationDate, - maybeNewPermissions = newPermissions))) + updateResourceMetadataResponse( + resourceIri = resourceIri, + maybeNewLabel = newLabel, + newLastModificationDate = newModificationDate, + maybeNewPermissions = newPermissions + ) + ) + ) clientTestDataCollector.addFile( TestDataFileContent( @@ -1550,8 +1664,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val previewRequest = Get(s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val previewRequest = Get( + s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val previewResponse: HttpResponse = singleAwaitingRequest(previewRequest) val previewResponseAsString = responseToString(previewResponse) assert(previewResponse.status == StatusCodes.OK, previewResponseAsString) @@ -1561,7 +1676,8 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(updatedLabel == newLabel) val updatedPermissions: String = previewJsonLD.requireString(OntologyConstants.KnoraApiV2Complex.HasPermissions) assert( - PermissionUtilADM.parsePermissions(updatedPermissions) == PermissionUtilADM.parsePermissions(newPermissions)) + PermissionUtilADM.parsePermissions(updatedPermissions) == PermissionUtilADM.parsePermissions(newPermissions) + ) val lastModificationDate: Instant = previewJsonLD.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -1581,26 +1697,26 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""|{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "rdfs:label" : "$newLabel", - | "knora-api:hasPermissions" : "$newPermissions", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$aThingLastModificationDate" - | }, - | "knora-api:newModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$newModificationDate" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "rdfs:label" : "$newLabel", + | "knora-api:hasPermissions" : "$newPermissions", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$aThingLastModificationDate" + | }, + | "knora-api:newModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$newModificationDate" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1613,18 +1729,23 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val updateRequest = Put(s"$baseApiUrl/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val updateRequest = Put( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val updateResponse: HttpResponse = singleAwaitingRequest(updateRequest) val updateResponseAsString: String = responseToString(updateResponse) assert(updateResponse.status == StatusCodes.OK, updateResponseAsString) assert( JsonParser(updateResponseAsString) == JsonParser( - updateResourceMetadataResponse(resourceIri = resourceIri, - maybeNewLabel = newLabel, - newLastModificationDate = newModificationDate, - maybeNewPermissions = newPermissions))) + updateResourceMetadataResponse( + resourceIri = resourceIri, + maybeNewLabel = newLabel, + newLastModificationDate = newModificationDate, + maybeNewPermissions = newPermissions + ) + ) + ) clientTestDataCollector.addFile( TestDataFileContent( @@ -1637,8 +1758,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val previewRequest = Get(s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val previewRequest = Get( + s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val previewResponse: HttpResponse = singleAwaitingRequest(previewRequest) val previewResponseAsString = responseToString(previewResponse) assert(previewResponse.status == StatusCodes.OK, previewResponseAsString) @@ -1648,7 +1770,8 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(updatedLabel == newLabel) val updatedPermissions: String = previewJsonLD.requireString(OntologyConstants.KnoraApiV2Complex.HasPermissions) assert( - PermissionUtilADM.parsePermissions(updatedPermissions) == PermissionUtilADM.parsePermissions(newPermissions)) + PermissionUtilADM.parsePermissions(updatedPermissions) == PermissionUtilADM.parsePermissions(newPermissions) + ) val lastModificationDate: Instant = previewJsonLD.requireDatatypeValueInObject( key = OntologyConstants.KnoraApiV2Complex.LastModificationDate, @@ -1665,21 +1788,21 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""|{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$aThingLastModificationDate" - | }, - | "knora-api:deleteComment" : "This resource is too boring.", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$aThingLastModificationDate" + | }, + | "knora-api:deleteComment" : "This resource is too boring.", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1692,9 +1815,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val updateRequest = Post(s"$baseApiUrl/v2/resources/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val updateRequest = Post( + s"$baseApiUrl/v2/resources/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val updateResponse: HttpResponse = singleAwaitingRequest(updateRequest) val updateResponseAsString: String = responseToString(updateResponse) assert(updateResponse.status == StatusCodes.OK, updateResponseAsString) @@ -1711,8 +1835,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val previewRequest = Get(s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val previewRequest = Get( + s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val previewResponse: HttpResponse = singleAwaitingRequest(previewRequest) val previewResponseAsString = responseToString(previewResponse) assert(previewResponse.status == StatusCodes.NotFound, previewResponseAsString) @@ -1724,21 +1849,21 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""|{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "knora-api:deleteComment" : "This resource is too boring.", - | "knora-api:deleteDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$deleteDate" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "knora-api:deleteComment" : "This resource is too boring.", + | "knora-api:deleteDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$deleteDate" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1751,16 +1876,18 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val updateRequest = Post(s"$baseApiUrl/v2/resources/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.superUser.email, password)) + val updateRequest = Post( + s"$baseApiUrl/v2/resources/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.superUser.email, password)) val updateResponse: HttpResponse = singleAwaitingRequest(updateRequest) val updateResponseAsString: String = responseToString(updateResponse) assert(updateResponse.status == StatusCodes.OK, updateResponseAsString) assert(JsonParser(updateResponseAsString) == JsonParser(successResponse("Resource marked as deleted"))) - val previewRequest = Get(s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val previewRequest = Get( + s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val previewResponse: HttpResponse = singleAwaitingRequest(previewRequest) val previewResponseAsString = responseToString(previewResponse) assert(previewResponse.status == StatusCodes.NotFound, previewResponseAsString) @@ -1773,38 +1900,41 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "anything:hasRichtext" : { - | "@type" : "knora-api:TextValue", - | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(hamletXml)}, - | "knora-api:textValueHasMapping" : { - | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping" - | } - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val resourceCreateRequest = Post(s"$baseApiUrl/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type" : "anything:Thing", + | "anything:hasRichtext" : { + | "@type" : "knora-api:TextValue", + | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(hamletXml)}, + | "knora-api:textValueHasMapping" : { + | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping" + | } + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val resourceCreateRequest = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceCreateResponse: HttpResponse = singleAwaitingRequest(request = resourceCreateRequest, duration = settings.triplestoreUpdateTimeout) assert(resourceCreateResponse.status == StatusCodes.OK, resourceCreateResponse.toString) val resourceCreateResponseAsJsonLD: JsonLDDocument = responseToJsonLDDocument(resourceCreateResponse) val resourceIri: IRI = - resourceCreateResponseAsJsonLD.body.requireStringWithValidation(JsonLDKeywords.ID, - stringFormatter.validateAndEscapeIri) + resourceCreateResponseAsJsonLD.body.requireStringWithValidation( + JsonLDKeywords.ID, + stringFormatter.validateAndEscapeIri + ) assert(resourceIri.toSmartIri.isKnoraDataIri) hamletResourceIri.set(resourceIri) } @@ -1813,8 +1943,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val hamletXml = FileUtil.readTextFile(Paths.get("test_data/resourcesR2RV2/hamlet.xml")) // Request the newly created resource. - val resourceGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(hamletResourceIri.get, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(hamletResourceIri.get, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceGetResponse: HttpResponse = singleAwaitingRequest(resourceGetRequest, duration = settings.triplestoreUpdateTimeout) val resourceGetResponseAsString = responseToString(resourceGetResponse) @@ -1842,7 +1973,8 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { // Get the resource without markup. val resourceGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(hamletResourceIri.get, "UTF-8")}") .addHeader(new MarkupHeader(RouteUtilV2.MARKUP_STANDOFF)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + BasicHttpCredentials(anythingUserEmail, password) + ) val resourceGetResponse: HttpResponse = singleAwaitingRequest(resourceGetRequest) val resourceGetResponseAsString = responseToString(resourceGetResponse) @@ -1873,8 +2005,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { while (hasMoreStandoff) { // Get a page of standoff. - val standoffGetRequest = Get(s"$baseApiUrl/v2/standoff/$resourceIriEncoded/$textValueIriEncoded/$offset") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val standoffGetRequest = Get( + s"$baseApiUrl/v2/standoff/$resourceIriEncoded/$textValueIriEncoded/$offset" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val standoffGetResponse: HttpResponse = singleAwaitingRequest(standoffGetRequest) val standoffGetResponseAsJsonLD: JsonLDObject = responseToJsonLDDocument(standoffGetResponse).body @@ -1916,20 +2049,20 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val jsonLDEntity = s"""|{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$resourceLastModificationDate" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$resourceLastModificationDate" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1942,15 +2075,17 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { ) ) - val updateRequest = Post(s"$baseApiUrl/v2/resources/erase", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, password)) + val updateRequest = Post( + s"$baseApiUrl/v2/resources/erase", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, password)) val updateResponse: HttpResponse = singleAwaitingRequest(updateRequest) val updateResponseAsString = responseToString(updateResponse) assert(updateResponse.status == StatusCodes.OK, updateResponseAsString) - val previewRequest = Get(s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val previewRequest = Get( + s"$baseApiUrl/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val previewResponse: HttpResponse = singleAwaitingRequest(previewRequest) val previewResponseAsString = responseToString(previewResponse) assert(previewResponse.status == StatusCodes.NotFound, previewResponseAsString) @@ -1959,28 +2094,30 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "create a resource containing a text value with a standoff link" in { val jsonLDEntity = """{ - | "@type": "anything:Thing", - | "anything:hasText": { - | "@type": "knora-api:TextValue", - | "knora-api:textValueAsXml": "\n\n This text links to another resource.\n", - | "knora-api:textValueHasMapping": { - | "@id": "http://rdfh.ch/standoff/mappings/StandardMapping" - | } - | }, - | "knora-api:attachedToProject": { - | "@id": "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label": "obj_inst1", - | "@context": { - | "anything": "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - | "knora-api": "http://api.knora.org/ontology/knora-api/v2#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type": "anything:Thing", + | "anything:hasText": { + | "@type": "knora-api:TextValue", + | "knora-api:textValueAsXml": "\n\n This text links to another resource.\n", + | "knora-api:textValueHasMapping": { + | "@id": "http://rdfh.ch/standoff/mappings/StandardMapping" + | } + | }, + | "knora-api:attachedToProject": { + | "@id": "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label": "obj_inst1", + | "@context": { + | "anything": "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + | "knora-api": "http://api.knora.org/ontology/knora-api/v2#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1989,8 +2126,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(resourceIri.toSmartIri.isKnoraDataIri) // Request the newly created resource in the complex schema, and check that it matches the ontology. - val resourceComplexGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceComplexGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceComplexGetResponse: HttpResponse = singleAwaitingRequest(resourceComplexGetRequest) val resourceComplexGetResponseAsString = responseToString(resourceComplexGetResponse) @@ -2028,8 +2166,10 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { | } | }""".stripMargin - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2038,8 +2178,9 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { assert(resourceIri.toSmartIri.isKnoraDataIri) // Request the newly created resource in the complex schema, and check that it matches the ontology. - val resourceComplexGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val resourceComplexGetRequest = Get( + s"$baseApiUrl/v2/resources/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val resourceComplexGetResponse: HttpResponse = singleAwaitingRequest(resourceComplexGetRequest) val resourceComplexGetResponseAsString = responseToString(resourceComplexGetResponse) @@ -2062,7 +2203,8 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { val responseJson: JsValue = JsonParser(responseStr) val expectedJson: JsValue = JsonParser( - readOrWriteTextFile(responseStr, Paths.get("test_data/resourcesR2RV2/IIIFManifest.jsonld"), writeTestDataFiles)) + readOrWriteTextFile(responseStr, Paths.get("test_data/resourcesR2RV2/IIIFManifest.jsonld"), writeTestDataFiles) + ) assert(responseJson == expectedJson) } @@ -2071,7 +2213,7 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { object ResourcesRouteV2E2ESpec { val config: Config = ConfigFactory.parseString("""akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" - |app.triplestore.profile-queries = false + |akka.stdout-loglevel = "DEBUG" + |app.triplestore.profile-queries = false """.stripMargin) } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2.scala index c7d9375b44..c235dd43df 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2.scala @@ -31,40 +31,36 @@ object ResponseCheckerV2 { private val noPropertyKeys: Set[IRI] = Set(JsonLDKeywords.ID, JsonLDKeywords.TYPE, OntologyConstants.Rdfs.Label) /** - * Converts a single JSON-LD element to a JSON-LD array with one entry. - * If given element is already an array, it will be returned unchanged. - * - * @param element the element to be turned into an array. - * @return a JSON-LD array. - */ - private def elementToArray(element: JsonLDValue): JsonLDArray = { + * Converts a single JSON-LD element to a JSON-LD array with one entry. + * If given element is already an array, it will be returned unchanged. + * + * @param element the element to be turned into an array. + * @return a JSON-LD array. + */ + private def elementToArray(element: JsonLDValue): JsonLDArray = element match { case obj: JsonLDObject if obj.value.isEmpty => JsonLDArray(Seq.empty[JsonLDValue]) case array: JsonLDArray => array case other: JsonLDValue => JsonLDArray(Seq(other)) } - } /** - * Compare two value objects. - * - * @param expectedValue expected value. - * @param receivedValue received value. - */ - private def compareValues(expectedValue: JsonLDValue, receivedValue: JsonLDValue): Unit = { - + * Compare two value objects. + * + * @param expectedValue expected value. + * @param receivedValue received value. + */ + private def compareValues(expectedValue: JsonLDValue, receivedValue: JsonLDValue): Unit = assert(expectedValue == receivedValue, s"expected $expectedValue, received $receivedValue") - // TODO: recurse over target resource if it is a LinkValue - - } + // TODO: recurse over target resource if it is a LinkValue /** - * Compares the received resource to the expected. - * - * @param expectedResource the expected resource. - * @param receivedResource the received resource. - */ + * Compares the received resource to the expected. + * + * @param expectedResource the expected resource. + * @param receivedResource the received resource. + */ private def compareResources(expectedResource: JsonLDObject, receivedResource: JsonLDObject): Unit = { def sortPropertyValues(values: JsonLDArray): JsonLDArray = { // Sort by the value object IRI if available, otherwise sort by the value itself. @@ -100,46 +96,53 @@ object ResponseCheckerV2 { .value(JsonLDKeywords.ID)}: expected ${expectedResource.value.keySet -- noPropertyKeys}, received ${receivedResource.value.keySet -- noPropertyKeys}" ) - (expectedResource.value -- noPropertyKeys).foreach { - case (propIri: IRI, expectedValuesForProp: JsonLDValue) => - // make sure that the property Iri exists in the received resource - assert(receivedResource.value.contains(propIri), - s"Property $propIri not found in received resource ${receivedResource.value(JsonLDKeywords.ID)}") - - val sortedExpectedPropertyValues: JsonLDArray = sortPropertyValues(elementToArray(expectedValuesForProp)) - val sortedReceivedPropertyValues: JsonLDArray = - sortPropertyValues(elementToArray(receivedResource.value(propIri))) - - // this check is necessary because zip returns a sequence of the length of the smaller of the two lists to be combined. - // https://www.scala-lang.org/api/current/scala/collection/Seq.html#zip[B](that:scala.collection.GenIterable[B]):Seq[(A,B)] - assert(sortedExpectedPropertyValues.value.size == sortedReceivedPropertyValues.value.size, - "number of values is not equal") - - sortedExpectedPropertyValues.value.zip(sortedReceivedPropertyValues.value).foreach { - case (expectedVal, receivedVal) => - compareValues(expectedVal, receivedVal) - } + (expectedResource.value -- noPropertyKeys).foreach { case (propIri: IRI, expectedValuesForProp: JsonLDValue) => + // make sure that the property Iri exists in the received resource + assert( + receivedResource.value.contains(propIri), + s"Property $propIri not found in received resource ${receivedResource.value(JsonLDKeywords.ID)}" + ) + + val sortedExpectedPropertyValues: JsonLDArray = sortPropertyValues(elementToArray(expectedValuesForProp)) + val sortedReceivedPropertyValues: JsonLDArray = + sortPropertyValues(elementToArray(receivedResource.value(propIri))) + + // this check is necessary because zip returns a sequence of the length of the smaller of the two lists to be combined. + // https://www.scala-lang.org/api/current/scala/collection/Seq.html#zip[B](that:scala.collection.GenIterable[B]):Seq[(A,B)] + assert( + sortedExpectedPropertyValues.value.size == sortedReceivedPropertyValues.value.size, + "number of values is not equal" + ) + + sortedExpectedPropertyValues.value.zip(sortedReceivedPropertyValues.value).foreach { + case (expectedVal, receivedVal) => + compareValues(expectedVal, receivedVal) + } } } /** - * Compares the received to the expected response. - * - * @param expectedResponse expected response. - * @param receivedResponse received response. - */ - def compareParsedJSONLDForResourcesResponse(expectedResponse: JsonLDDocument, - receivedResponse: JsonLDDocument): Unit = { + * Compares the received to the expected response. + * + * @param expectedResponse expected response. + * @param receivedResponse received response. + */ + def compareParsedJSONLDForResourcesResponse( + expectedResponse: JsonLDDocument, + receivedResponse: JsonLDDocument + ): Unit = { // returns a list even if there is only one element val expectedResourcesAsArray: JsonLDArray = elementToArray( - expectedResponse.body.value.getOrElse(JsonLDKeywords.GRAPH, expectedResponse.body)) + expectedResponse.body.value.getOrElse(JsonLDKeywords.GRAPH, expectedResponse.body) + ) // returns a list even if there is only one element val receivedResourcesAsArray: JsonLDArray = elementToArray( - receivedResponse.body.value.getOrElse(JsonLDKeywords.GRAPH, receivedResponse.body)) + receivedResponse.body.value.getOrElse(JsonLDKeywords.GRAPH, receivedResponse.body) + ) // check that the actual amount of resources returned is correct // this check is necessary because zip returns a sequence of the length of the smaller of the two lists to be combined. @@ -161,81 +164,92 @@ object ResponseCheckerV2 { } /** - * Compares the received JSON response to the expected JSON. - * - * @param expectedJSONLD expected answer from Knora API V2 as JSONLD. - * @param receivedJSONLD received answer from Knora Api V2 as JSONLD. - */ + * Compares the received JSON response to the expected JSON. + * + * @param expectedJSONLD expected answer from Knora API V2 as JSONLD. + * @param receivedJSONLD received answer from Knora Api V2 as JSONLD. + */ def compareJSONLDForResourcesResponse(expectedJSONLD: String, receivedJSONLD: String): Unit = { val expectedJsonLDDocument = JsonLDUtil.parseJsonLD(expectedJSONLD) val receivedJsonLDDocument = JsonLDUtil.parseJsonLD(receivedJSONLD) - compareParsedJSONLDForResourcesResponse(expectedResponse = expectedJsonLDDocument, - receivedResponse = receivedJsonLDDocument) + compareParsedJSONLDForResourcesResponse( + expectedResponse = expectedJsonLDDocument, + receivedResponse = receivedJsonLDDocument + ) } /** - * Checks the response to a count query. - * - * @param receivedJSONLD the response sent back by the search route. - * @param expectedNumber the expected number of results for the query. - * @return an assertion that the actual amount of results corresponds with the expected number of results. - */ + * Checks the response to a count query. + * + * @param receivedJSONLD the response sent back by the search route. + * @param expectedNumber the expected number of results for the query. + * @return an assertion that the actual amount of results corresponds with the expected number of results. + */ def checkCountResponse(receivedJSONLD: String, expectedNumber: Int): Unit = { val receivedJsonLDDocument = JsonLDUtil.parseJsonLD(receivedJSONLD) // make sure the indicated amount of results is correct val receivedNumber = receivedJsonLDDocument.body.value(numberOfItemsMember).asInstanceOf[JsonLDInt].value - assert(receivedNumber == expectedNumber, - s"$numberOfItemsMember is incorrect (expected $expectedNumber, received $receivedNumber)") + assert( + receivedNumber == expectedNumber, + s"$numberOfItemsMember is incorrect (expected $expectedNumber, received $receivedNumber)" + ) } /** - * Checks the number of results in a search response. - * - * @param receivedJSONLD the response sent back by the search route. - * @param expectedNumber the expected number of results for the query. - * @return an assertion that the actual amount of results corresponds with the expected number of results. - */ + * Checks the number of results in a search response. + * + * @param receivedJSONLD the response sent back by the search route. + * @param expectedNumber the expected number of results for the query. + * @return an assertion that the actual amount of results corresponds with the expected number of results. + */ def checkSearchResponseNumberOfResults(receivedJSONLD: String, expectedNumber: Int): Unit = { val receivedJsonLDDocument = JsonLDUtil.parseJsonLD(receivedJSONLD) val receivedResourcesAsArray: JsonLDArray = elementToArray( - receivedJsonLDDocument.body.value.getOrElse(JsonLDKeywords.GRAPH, receivedJsonLDDocument.body)) + receivedJsonLDDocument.body.value.getOrElse(JsonLDKeywords.GRAPH, receivedJsonLDDocument.body) + ) val numberOfResultsReceived = receivedResourcesAsArray.value.size - assert(numberOfResultsReceived == expectedNumber, - s"Expected $expectedNumber results, received $numberOfResultsReceived") + assert( + numberOfResultsReceived == expectedNumber, + s"Expected $expectedNumber results, received $numberOfResultsReceived" + ) } /** - * Checks the response to a mapping creation request. - * - * @param expectedJSONLD the expected response as JSON-LD. - * @param receivedJSONLD the received response as JSON-LD. - */ + * Checks the response to a mapping creation request. + * + * @param expectedJSONLD the expected response as JSON-LD. + * @param receivedJSONLD the received response as JSON-LD. + */ def compareJSONLDForMappingCreationResponse(expectedJSONLD: String, receivedJSONLD: String): Unit = { val expectedJsonLDDocument = JsonLDUtil.parseJsonLD(expectedJSONLD) val receivedJsonLDDocument = JsonLDUtil.parseJsonLD(receivedJSONLD) - assert(expectedJsonLDDocument == receivedJsonLDDocument, - "Mapping creation response did not match expected response") + assert( + expectedJsonLDDocument == receivedJsonLDDocument, + "Mapping creation response did not match expected response" + ) } /** - * Checks the response to a resource history request. - * - * @param expectedJSONLD the expected response as JSON-LD. - * @param receivedJSONLD the received response as JSON-LD. - */ + * Checks the response to a resource history request. + * + * @param expectedJSONLD the expected response as JSON-LD. + * @param receivedJSONLD the received response as JSON-LD. + */ def compareJSONLDForResourceHistoryResponse(expectedJSONLD: String, receivedJSONLD: String): Unit = { val expectedJsonLDDocument = JsonLDUtil.parseJsonLD(expectedJSONLD) val receivedJsonLDDocument = JsonLDUtil.parseJsonLD(receivedJSONLD) - assert(expectedJsonLDDocument == receivedJsonLDDocument, - "Resource history response did not match expected response") + assert( + expectedJsonLDDocument == receivedJsonLDDocument, + "Resource history response did not match expected response" + ) } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2Spec.scala index 9226a02584..5fd5877bda 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResponseCheckerV2Spec.scala @@ -25,8 +25,8 @@ import org.knora.webapi.CoreSpec import org.knora.webapi.util.FileUtil /** - * Tests [[ResponseCheckerV2]]. - */ + * Tests [[ResponseCheckerV2]]. + */ class ResponseCheckerV2Spec extends CoreSpec() { "ResponseCheckerV2" should { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/SchemaHeader.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/SchemaHeader.scala index 6fa1ae7a5b..0fdea3f49d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/SchemaHeader.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/SchemaHeader.scala @@ -25,11 +25,11 @@ import org.knora.webapi.routing.RouteUtilV2 import scala.util.Try /** - * A custom Akka HTTP header representing [[RouteUtilV2.SCHEMA_HEADER]], which a client can send to specify - * which ontology schema should be used in an API response. - * - * The definition follows [[https://doc.akka.io/docs/akka-http/current/common/http-model.html#custom-headers]]. - */ + * A custom Akka HTTP header representing [[RouteUtilV2.SCHEMA_HEADER]], which a client can send to specify + * which ontology schema should be used in an API response. + * + * The definition follows [[https://doc.akka.io/docs/akka-http/current/common/http-model.html#custom-headers]]. + */ final class SchemaHeader(token: String) extends ModeledCustomHeader[SchemaHeader] { override def renderInRequests = true override def renderInResponses = true diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala index b102c75ec3..bf0fc65de4 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala @@ -48,22 +48,22 @@ import spray.json.JsString import scala.concurrent.ExecutionContextExecutor /** - * End-to-end test specification for the search endpoint. This specification uses the Spray Testkit as documented - * here: http://spray.io/documentation/1.2.2/spray-testkit/ - */ + * End-to-end test specification for the search endpoint. This specification uses the Spray Testkit as documented + * here: http://spray.io/documentation/1.2.2/spray-testkit/ + */ class SearchRouteV2R2RSpec extends R2RSpec { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin - private val searchPath = DSPApiDirectives.handleErrors(system) { new SearchRouteV2(routeData).knoraApiPath } - private val resourcePath = DSPApiDirectives.handleErrors(system) { new ResourcesRouteV2(routeData).knoraApiPath } - private val standoffPath = DSPApiDirectives.handleErrors(system) { new StandoffRouteV2(routeData).knoraApiPath } - private val valuesPath = DSPApiDirectives.handleErrors(system) { new ValuesRouteV1(routeData).knoraApiPath } + private val searchPath = DSPApiDirectives.handleErrors(system)(new SearchRouteV2(routeData).knoraApiPath) + private val resourcePath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) + private val standoffPath = DSPApiDirectives.handleErrors(system)(new StandoffRouteV2(routeData).knoraApiPath) + private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) @@ -95,12 +95,18 @@ class SearchRouteV2R2RSpec extends R2RSpec { RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything"), RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), RdfDataObject(path = "test_data/all_data/beol-data.ttl", name = "http://www.knora.org/data/0801/beol"), - RdfDataObject(path = "test_data/e2e.v2.SearchRouteV2R2RSpec/gravsearchtest1-admin.ttl", - name = "http://www.knora.org/data/admin"), - RdfDataObject(path = "test_data/e2e.v2.SearchRouteV2R2RSpec/gravsearchtest1-onto.ttl", - name = "http://www.knora.org/ontology/0666/gravsearchtest1"), - RdfDataObject(path = "test_data/e2e.v2.SearchRouteV2R2RSpec/gravsearchtest1-data.ttl", - name = "http://www.knora.org/data/0666/gravsearchtest1") + RdfDataObject( + path = "test_data/e2e.v2.SearchRouteV2R2RSpec/gravsearchtest1-admin.ttl", + name = "http://www.knora.org/data/admin" + ), + RdfDataObject( + path = "test_data/e2e.v2.SearchRouteV2R2RSpec/gravsearchtest1-onto.ttl", + name = "http://www.knora.org/ontology/0666/gravsearchtest1" + ), + RdfDataObject( + path = "test_data/e2e.v2.SearchRouteV2R2RSpec/gravsearchtest1-data.ttl", + name = "http://www.knora.org/data/0666/gravsearchtest1" + ) ) "The Search v2 Endpoint" should { @@ -110,9 +116,11 @@ class SearchRouteV2R2RSpec extends R2RSpec { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/NarrFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/NarrFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -139,9 +147,11 @@ class SearchRouteV2R2RSpec extends R2RSpec { // the response involves forbidden resource val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/searchResponseWithHiddenResource.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/searchResponseWithHiddenResource.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -149,13 +159,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { } "perform a fulltext search for 'Dinge' (in the complex schema)" in { - Get("/v2/search/Dinge") ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + Get("/v2/search/Dinge") ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get(s"test_data/searchR2RV2/DingeFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get(s"test_data/searchR2RV2/DingeFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -164,14 +178,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a fulltext search for 'Dinge' (in the simple schema)" in { Get("/v2/search/Dinge").addHeader(new SchemaHeader(RouteUtilV2.SIMPLE_SCHEMA_NAME)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get(s"test_data/searchR2RV2/DingeFulltextSearchSimple.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get(s"test_data/searchR2RV2/DingeFulltextSearchSimple.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -179,7 +196,9 @@ class SearchRouteV2R2RSpec extends R2RSpec { } "perform a count query for a fulltext search for 'Dinge'" in { - Get("/v2/search/count/Dinge") ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + Get("/v2/search/count/Dinge") ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -189,13 +208,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { } "perform a fulltext query for a search value containing a single character wildcard" in { - Get("/v2/search/Unif%3Frm") ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + Get("/v2/search/Unif%3Frm") ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingUniform.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingUniform.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -203,13 +226,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { } "perform a fulltext query for a search value containing a multiple character wildcard" in { - Get("/v2/search/Unif*m") ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + Get("/v2/search/Unif*m") ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingUniform.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingUniform.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -223,9 +250,11 @@ class SearchRouteV2R2RSpec extends R2RSpec { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/FulltextSearchWithImage.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/FulltextSearchWithImage.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -240,7 +269,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { val responseStr = responseAs[String] assert(status == StatusCodes.BAD_REQUEST, responseStr) assert( - responseStr.contains("It looks like you are submitting a Gravsearch request to a full-text search route")) + responseStr.contains("It looks like you are submitting a Gravsearch request to a full-text search route") + ) } } @@ -251,40 +281,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a anything:Thing . - | - | OPTIONAL { - | ?thing anything:hasDate ?date . - | anything:hasDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | anything:hasInteger knora-api:objectType xsd:integer . - | ?intVal a xsd:integer . - | FILTER(?intVal = 123454321 || ?intVal = 999999999) - | } - |} - |ORDER BY DESC(?date) - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a anything:Thing . + | + | OPTIONAL { + | ?thing anything:hasDate ?date . + | anything:hasDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | anything:hasInteger knora-api:objectType xsd:integer . + | ?intVal a xsd:integer . + | FILTER(?intVal = 123454321 || ?intVal = 999999999) + | } + |} + |ORDER BY DESC(?date) + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val expectedAnswerJSONLD = - readOrWriteTextFile(responseStr, - Paths.get("test_data/searchR2RV2/thingWithOptionalDateSortedDesc.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseStr, + Paths.get("test_data/searchR2RV2/thingWithOptionalDateSortedDesc.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -295,32 +330,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasDate ?date . - |} WHERE { - | ?thing a knora-api:Resource . - | ?thing a anything:Thing . - | - | OPTIONAL { - | ?thing anything:hasDate ?date . - | anything:hasDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | anything:hasInteger knora-api:objectType xsd:integer . - | ?intVal a xsd:integer . - | FILTER(?intVal = 123454321 || ?intVal = 999999999) - | } - |} - |ORDER BY DESC(?date) - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasDate ?date . + |} WHERE { + | ?thing a knora-api:Resource . + | ?thing a anything:Thing . + | + | OPTIONAL { + | ?thing anything:hasDate ?date . + | anything:hasDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | anything:hasInteger knora-api:objectType xsd:integer . + | ?intVal a xsd:integer . + | FILTER(?intVal = 123454321 || ?intVal = 999999999) + | } + |} + |ORDER BY DESC(?date) + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -333,36 +371,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (in the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -373,37 +416,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the dcterms:title 'Zeitglöcklein des Lebens' returning the title in the answer (in the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX dcterms: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book dcterms:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book dcterms:title ?title . - | dcterms:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX dcterms: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book dcterms:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book dcterms:title ?title . + | dcterms:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -414,26 +462,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (in the simple schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } """.stripMargin Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) @@ -444,7 +492,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { val expectedAnswerJSONLD = readOrWriteTextFile( responseAs[String], Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswerSimple.jsonld"), - writeTestDataFiles) + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -455,27 +504,27 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the dcterms:title 'Zeitglöcklein des Lebens' returning the title in the answer (in the simple schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX dcterms: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book dcterms:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book dcterms:title ?title . - | dcterms:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } + |PREFIX knora-api: + |PREFIX dcterms: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book dcterms:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book dcterms:title ?title . + | dcterms:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } """.stripMargin Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) @@ -486,7 +535,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { val expectedAnswerJSONLD = readOrWriteTextFile( responseAs[String], Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswerSimple.jsonld"), - writeTestDataFiles) + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -497,29 +547,32 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch count query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -532,34 +585,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' not returning the title in the answer" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchNoTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchNoTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -570,36 +628,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that do not have the title 'Zeitglöcklein des Lebens'" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/NotZeitgloeckleinExtendedSearch.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/NotZeitgloeckleinExtendedSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -610,29 +673,32 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch count query for books that do not have the title 'Zeitglöcklein des Lebens'" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -651,42 +717,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | FILTER(?seqnum = 10) - | - | ?seqnum a xsd:integer . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | FILTER(?seqnum = 10) + | + | ?seqnum a xsd:integer . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageWithSeqnum10WithSeqnumAndLinkValueInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageWithSeqnum10WithSeqnumAndLinkValueInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -698,40 +769,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | FILTER(?seqnum = 10) - | - | ?seqnum a xsd:integer . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | FILTER(?seqnum = 10) + | + | ?seqnum a xsd:integer . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageWithSeqnum10OnlySeqnuminAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageWithSeqnum10OnlySeqnuminAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -743,42 +819,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | FILTER(?seqnum <= 10) - | - | ?seqnum a xsd:integer . - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | FILTER(?seqnum <= 10) + | + | ?seqnum a xsd:integer . + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/pagesOfLatinNarrenschiffWithSeqnumLowerEquals10.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/pagesOfLatinNarrenschiffWithSeqnumLowerEquals10.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -790,40 +871,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | ?seqnum a xsd:integer . - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | ?seqnum a xsd:integer . + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnum.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnum.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -835,41 +921,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | ?seqnum a xsd:integer . - | - | } ORDER BY ?seqnum - | OFFSET 1 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | ?seqnum a xsd:integer . + | + | } ORDER BY ?seqnum + | OFFSET 1 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnumNextOffset.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnumNextOffset.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -880,31 +971,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published on the first of March 1497 (Julian Calendar)" ignore { // literals are not supported val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -917,41 +1011,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published on the first of March 1497 (Julian Calendar) (2)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | - | FILTER(?pubdate = "JULIAN:1497-03-01"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | + | FILTER(?pubdate = "JULIAN:1497-03-01"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedOnDate.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -962,42 +1061,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have not been published on the first of March 1497 (Julian Calendar)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | - | FILTER(?pubdate != "JULIAN:1497-03-01"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | + | FILTER(?pubdate != "JULIAN:1497-03-01"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1011,42 +1115,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have not been published on the first of March 1497 (Julian Calendar) 2" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | - | FILTER(?pubdate < "JULIAN:1497-03-01"^^knora-api:Date || ?pubdate > "JULIAN:1497-03-01"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | + | FILTER(?pubdate < "JULIAN:1497-03-01"^^knora-api:Date || ?pubdate > "JULIAN:1497-03-01"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1060,41 +1169,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published before 1497 (Julian Calendar)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | FILTER(?pubdate < "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | FILTER(?pubdate < "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBeforeDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBeforeDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1108,41 +1222,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published 1497 or later (Julian Calendar)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | FILTER(?pubdate >= "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | FILTER(?pubdate >= "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedAfterOrOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedAfterOrOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1156,41 +1275,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published after 1497 (Julian Calendar)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | FILTER(?pubdate > "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | FILTER(?pubdate > "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedAfterDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedAfterDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1204,41 +1328,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published 1497 or before (Julian Calendar)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | FILTER(?pubdate <= "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | FILTER(?pubdate <= "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBeforeOrOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBeforeOrOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1252,42 +1381,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published after 1486 and before 1491 (Julian Calendar)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | ?book incunabula:pubdate ?pubdate . - | incunabula:pubdate knora-api:objectType knora-api:Date . - | - | ?pubdate a knora-api:Date . - | - | FILTER(?pubdate > "JULIAN:1486"^^knora-api:Date && ?pubdate < "JULIAN:1491"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | ?book incunabula:pubdate ?pubdate . + | incunabula:pubdate knora-api:objectType knora-api:Date . + | + | ?pubdate a knora-api:Date . + | + | FILTER(?pubdate > "JULIAN:1486"^^knora-api:Date && ?pubdate < "JULIAN:1491"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBetweenDates.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBetweenDates.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1300,53 +1434,58 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get the regions belonging to a page" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?region knora-api:isMainResource true . - | - | ?region knora-api:isRegionOf . - | - | ?region knora-api:hasGeometry ?geom . - | - | ?region knora-api:hasComment ?comment . - | - | ?region knora-api:hasColor ?color . - | } WHERE { - | - | ?region a knora-api:Region . - | ?region a knora-api:Resource . - | - | ?region knora-api:isRegionOf . - | knora-api:isRegionOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | ?region knora-api:hasGeometry ?geom . - | knora-api:hasGeometry knora-api:objectType knora-api:Geom . - | - | ?geom a knora-api:Geom . - | - | ?region knora-api:hasComment ?comment . - | knora-api:hasComment knora-api:objectType xsd:string . - | - | ?comment a xsd:string . - | - | ?region knora-api:hasColor ?color . - | knora-api:hasColor knora-api:objectType knora-api:Color . - | - | ?color a knora-api:Color . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/RegionsForPage.jsonld"), - writeTestDataFiles) + | PREFIX knora-api: + | + | CONSTRUCT { + | ?region knora-api:isMainResource true . + | + | ?region knora-api:isRegionOf . + | + | ?region knora-api:hasGeometry ?geom . + | + | ?region knora-api:hasComment ?comment . + | + | ?region knora-api:hasColor ?color . + | } WHERE { + | + | ?region a knora-api:Region . + | ?region a knora-api:Resource . + | + | ?region knora-api:isRegionOf . + | knora-api:isRegionOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | ?region knora-api:hasGeometry ?geom . + | knora-api:hasGeometry knora-api:objectType knora-api:Geom . + | + | ?geom a knora-api:Geom . + | + | ?region knora-api:hasComment ?comment . + | knora-api:hasComment knora-api:objectType xsd:string . + | + | ?comment a xsd:string . + | + | ?region knora-api:hasColor ?color . + | knora-api:hasColor knora-api:objectType knora-api:Color . + | + | ?color a knora-api:Color . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/RegionsForPage.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1359,57 +1498,62 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a book a page points to and include the page in the results (all properties present in WHERE clause)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFile ?file . - | - |} WHERE { - | - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | knora-api:isPartOf ?book . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | knora-api:seqnum ?seqnum . - | knora-api:seqnum knora-api:objectType xsd:integer . - | - | ?seqnum a xsd:integer . - | - | knora-api:hasStillImageFile ?file . - | knora-api:hasStillImageFile knora-api:objectType knora-api:File . - | - | ?file a knora-api:File . - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFile ?file . + | + |} WHERE { + | + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | knora-api:isPartOf ?book . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | knora-api:seqnum ?seqnum . + | knora-api:seqnum knora-api:objectType xsd:integer . + | + | ?seqnum a xsd:integer . + | + | knora-api:hasStillImageFile ?file . + | knora-api:hasStillImageFile knora-api:objectType knora-api:File . + | + | ?file a knora-api:File . + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/bookWithIncomingPagesWithAllRequestedProps.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/bookWithIncomingPagesWithAllRequestedProps.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1420,53 +1564,58 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a book a page points to and only include the page's partOf link in the results (none of the other properties)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - |} WHERE { - | - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | knora-api:isPartOf ?book . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - | - | knora-api:seqnum ?seqnum . - | knora-api:seqnum knora-api:objectType xsd:integer . - | - | ?seqnum a xsd:integer . - | - | knora-api:hasStillImageFile ?file . - | knora-api:hasStillImageFile knora-api:objectType knora-api:File . - | - | ?file a knora-api:File . - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + |} WHERE { + | + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | knora-api:isPartOf ?book . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + | + | knora-api:seqnum ?seqnum . + | knora-api:seqnum knora-api:objectType xsd:integer . + | + | ?seqnum a xsd:integer . + | + | knora-api:hasStillImageFile ?file . + | knora-api:hasStillImageFile knora-api:objectType knora-api:File . + | + | ?file a knora-api:File . + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/bookWithIncomingPagesOnlyLink.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/bookWithIncomingPagesOnlyLink.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1477,45 +1626,50 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get incoming links pointing to an incunbaula:book, excluding isPartOf and isRegionOf" in { val gravsearchQuery = """ - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?incomingRes knora-api:isMainResource true . - | - | ?incomingRes ?incomingProp . - | - |} WHERE { - | - | ?incomingRes a knora-api:Resource . - | - | ?incomingRes ?incomingProp . - | - | a knora-api:Resource . - | - | ?incomingProp knora-api:objectType knora-api:Resource . - | - | knora-api:isRegionOf knora-api:objectType knora-api:Resource . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | FILTER NOT EXISTS { - | ?incomingRes knora-api:isRegionOf . - | } - | - | FILTER NOT EXISTS { - | ?incomingRes knora-api:isPartOf . - | } - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?incomingRes knora-api:isMainResource true . + | + | ?incomingRes ?incomingProp . + | + |} WHERE { + | + | ?incomingRes a knora-api:Resource . + | + | ?incomingRes ?incomingProp . + | + | a knora-api:Resource . + | + | ?incomingProp knora-api:objectType knora-api:Resource . + | + | knora-api:isRegionOf knora-api:objectType knora-api:Resource . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | FILTER NOT EXISTS { + | ?incomingRes knora-api:isRegionOf . + | } + | + | FILTER NOT EXISTS { + | ?incomingRes knora-api:isPartOf . + | } + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/IncomingLinksForBook.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/IncomingLinksForBook.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1526,27 +1680,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value of 2.1" ignore { // literals are not supported val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal "2.1"^^xsd:decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasDecimal "2.1"^^xsd:decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal "2.1"^^xsd:decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasDecimal "2.1"^^xsd:decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1559,36 +1715,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value of 2.1 2" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | - | FILTER(?decimal = "2.1"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | + | FILTER(?decimal = "2.1"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1601,36 +1761,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value bigger than 2.0" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | - | FILTER(?decimal > "2"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | + | FILTER(?decimal > "2"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingBiggerThanDecimal.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingBiggerThanDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1643,37 +1807,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value smaller than 3.0" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | - | FILTER(?decimal < "3"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | + | FILTER(?decimal < "3"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingSmallerThanDecimal.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingSmallerThanDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1686,33 +1854,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a specific URI value" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasUri ?uri . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasUri ?uri . - | - | FILTER(?uri = "http://www.google.ch"^^xsd:anyURI) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasUri ?uri . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasUri ?uri . + | + | FILTER(?uri = "http://www.google.ch"^^xsd:anyURI) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingWithURI.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingWithURI.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1725,27 +1897,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a Boolean value that is true" ignore { // literals are not supported val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean true - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasBoolean true . - | anything:hasBoolean knora-api:objectType xsd:boolean . - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean true + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasBoolean true . + | anything:hasBoolean knora-api:objectType xsd:boolean . + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -1758,37 +1932,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a Boolean value that is true 2" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | ?thing anything:hasBoolean ?boolean . - | anything:hasBoolean knora-api:objectType xsd:boolean . - | - | ?boolean a xsd:boolean . - | - | FILTER(?boolean = true) - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | ?thing anything:hasBoolean ?boolean . + | anything:hasBoolean knora-api:objectType xsd:boolean . + | + | ?boolean a xsd:boolean . + | + | FILTER(?boolean = true) + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBoolean.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBoolean.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1800,45 +1978,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that may have a Boolean value that is true" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | - | ?thing anything:hasBoolean ?boolean . - | anything:hasBoolean knora-api:objectType xsd:boolean . - | - | ?boolean a xsd:boolean . - | - | FILTER(?boolean = true) - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | anything:hasInteger knora-api:objectType xsd:integer . - | ?intVal a xsd:integer . - | FILTER(?intVal = 123454321 || ?intVal = 999999999) - | } - |} OFFSET 0""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | + | ?thing anything:hasBoolean ?boolean . + | anything:hasBoolean knora-api:objectType xsd:boolean . + | + | ?boolean a xsd:boolean . + | + | FILTER(?boolean = true) + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | anything:hasInteger knora-api:objectType xsd:integer . + | ?intVal a xsd:integer . + | FILTER(?intVal = 123454321 || ?intVal = 999999999) + | } + |} OFFSET 0""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset0.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset0.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1852,46 +2034,50 @@ class SearchRouteV2R2RSpec extends R2RSpec { // set OFFSET to 1 to get "Testding for extended search" val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | - | ?thing anything:hasBoolean ?boolean . - | anything:hasBoolean knora-api:objectType xsd:boolean . - | - | ?boolean a xsd:boolean . - | - | FILTER(?boolean = true) - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | anything:hasInteger knora-api:objectType xsd:integer . - | ?intVal a xsd:integer . - | FILTER(?intVal = 123454321 || ?intVal = 999999999) - | } - |} OFFSET 1 - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | + | ?thing anything:hasBoolean ?boolean . + | anything:hasBoolean knora-api:objectType xsd:boolean . + | + | ?boolean a xsd:boolean . + | + | FILTER(?boolean = true) + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | anything:hasInteger knora-api:objectType xsd:integer . + | ?intVal a xsd:integer . + | FILTER(?intVal = 123454321 || ?intVal = 999999999) + | } + |} OFFSET 1 + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset1.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset1.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1905,49 +2091,53 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | { - | ?thing anything:hasBoolean ?boolean . - | anything:hasBoolean knora-api:objectType xsd:boolean . - | - | ?boolean a xsd:boolean . - | - | FILTER(?boolean = true) - | } UNION { - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | - | FILTER(?decimal = "2.1"^^xsd:decimal) - | } - | - |} OFFSET 0 - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | { + | ?thing anything:hasBoolean ?boolean . + | anything:hasBoolean knora-api:objectType xsd:boolean . + | + | ?boolean a xsd:boolean . + | + | FILTER(?boolean = true) + | } UNION { + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | + | FILTER(?decimal = "2.1"^^xsd:decimal) + | } + | + |} OFFSET 0 + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOrDecimal.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOrDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -1961,38 +2151,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX knora-api: - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes ?propVal0 . - | - | } WHERE { - | - | ?mainRes a knora-api:Resource . - | - | ?mainRes a . - | - | - | ?mainRes ?propVal0 . - | knora-api:objectType . - | ?propVal0 a . - | - | FILTER regex(?propVal0, "Zeit", "i") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX knora-api: + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes ?propVal0 . + | + | } WHERE { + | + | ?mainRes a knora-api:Resource . + | + | ?mainRes a . + | + | + | ?mainRes ?propVal0 . + | knora-api:objectType . + | ?propVal0 a . + | + | FILTER regex(?propVal0, "Zeit", "i") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeit.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeit.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2006,37 +2200,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX knora-api: - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes ?propVal0 . - | - | } WHERE { - | - | ?mainRes a knora-api:Resource . - | - | ?mainRes a . - | - | ?mainRes ?propVal0 . - | knora-api:objectType . - | ?propVal0 a . - | - | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX knora-api: + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes ?propVal0 . + | + | } WHERE { + | + | ?mainRes a knora-api:Resource . + | + | ?mainRes a . + | + | ?mainRes ?propVal0 . + | knora-api:objectType . + | ?propVal0 a . + | + | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2050,37 +2248,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX knora-api: - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes ?propVal0 . - | - | } WHERE { - | - | ?mainRes a knora-api:Resource . - | - | ?mainRes a . - | - | ?mainRes ?propVal0 . - | knora-api:objectType . - | ?propVal0 a . - | - | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein AND Lebens") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX knora-api: + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes ?propVal0 . + | + | } WHERE { + | + | ?mainRes a knora-api:Resource . + | + | ?mainRes a . + | + | ?mainRes ?propVal0 . + | knora-api:objectType . + | ?propVal0 a . + | + | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein AND Lebens") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2094,37 +2296,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - | PREFIX dcterms: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book dcterms:title ?title . - | - | } WHERE { - | ?book a knora-api:Resource . - | - | ?book dcterms:title ?title . - | - | dcterms:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = 'Zeitglöcklein des Lebens und Leidens Christi') - | - | } OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | PREFIX dcterms: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book dcterms:title ?title . + | + | } WHERE { + | ?book a knora-api:Resource . + | + | ?book dcterms:title ?title . + | + | dcterms:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = 'Zeitglöcklein des Lebens und Leidens Christi') + | + | } OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2138,34 +2344,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing a knora-api:Resource . - | - | ?thing anything:hasListItem ?listItem . - | - | anything:hasListItem knora-api:objectType knora-api:ListNode . - | - | ?listItem a knora-api:ListNode . - | - | } OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing a knora-api:Resource . + | + | ?thing anything:hasListItem ?listItem . + | + | anything:hasListItem knora-api:objectType knora-api:ListNode . + | + | ?listItem a knora-api:ListNode . + | + | } OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithListValue.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithListValue.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2179,36 +2389,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a knora-api:Resource . - | - | ?thing a anything:Thing . - | - | ?thing anything:hasText ?text . - | - | anything:hasText knora-api:objectType xsd:string . - | - | ?text a xsd:string . - | - | FILTER(lang(?text) = "fr") - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a knora-api:Resource . + | + | ?thing a anything:Thing . + | + | ?thing anything:hasText ?text . + | + | anything:hasText knora-api:objectType xsd:string . + | + | ?text a xsd:string . + | + | FILTER(lang(?text) = "fr") + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) @@ -2219,36 +2433,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a knora-api:Resource . - | - | ?thing a anything:Thing . - | - | ?thing anything:hasText ?text . - | - | anything:hasText knora-api:objectType xsd:string . - | - | ?text a xsd:string . - | - | FILTER(lang(?text) = "fr" && ?text = "Bonjour") - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a knora-api:Resource . + | + | ?thing a anything:Thing . + | + | ?thing anything:hasText ?text . + | + | anything:hasText knora-api:objectType xsd:string . + | + | ?text a xsd:string . + | + | FILTER(lang(?text) = "fr" && ?text = "Bonjour") + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) @@ -2256,12 +2474,16 @@ class SearchRouteV2R2RSpec extends R2RSpec { } "perform a fulltext search for 'Bonjour'" in { - Get("/v2/search/Bonjour") ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + Get("/v2/search/Bonjour") ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -2271,14 +2493,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/text?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffParagraphTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffParagraphTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2290,7 +2515,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/count/text?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffParagraphTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffParagraphTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2304,14 +2530,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/text?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2323,7 +2552,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/count/text?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2336,14 +2566,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/interesting%20text?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2355,7 +2588,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/interesting%20text?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2368,7 +2602,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { Get( "/v2/search/interesting%20boring?limitToStandoffClass=" + URLEncoder - .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8")) ~> searchPath ~> check { + .encode("http://api.knora.org/ontology/standoff/v2#StandoffItalicTag", "UTF-8") + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2393,41 +2628,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?linkObj knora-api:isMainResource true . - | - | ?linkObj knora-api:hasLinkTo ?book . - | - |} WHERE { - | ?linkObj a knora-api:Resource . - | ?linkObj a knora-api:LinkObj . - | - | ?linkObj knora-api:hasLinkTo ?book . - | knora-api:hasLinkTo knora-api:objectType knora-api:Resource . - | - | ?book a knora-api:Resource . - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LinkObjectsToBooks.jsonld"), - writeTestDataFiles) + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?linkObj knora-api:isMainResource true . + | + | ?linkObj knora-api:hasLinkTo ?book . + | + |} WHERE { + | ?linkObj a knora-api:Resource . + | ?linkObj a knora-api:LinkObj . + | + | ?linkObj knora-api:hasLinkTo ?book . + | knora-api:hasLinkTo knora-api:objectType knora-api:Resource . + | + | ?book a knora-api:Resource . + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LinkObjectsToBooks.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2441,48 +2680,52 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 . - | - | - | } WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | # testperson2 - | ?letter ?linkingProp1 . - | - | a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithAuthor.jsonld"), - writeTestDataFiles) + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 . + | + | + | } WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | # testperson2 + | ?letter ?linkingProp1 . + | + | a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithAuthor.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2495,42 +2738,44 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 . - | - | - | } WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | # testperson2 - | ?letter ?linkingProp1 . - | - | a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 . + | + | + | } WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | # testperson2 + | ?letter ?linkingProp1 . + | + | a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2543,58 +2788,62 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | ?person1 beol:hasFamilyName ?name . - | - | beol:hasFamilyName knora-api:objectType xsd:string . - | ?name a xsd:string . - | - | FILTER(?name = "Meier") - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | ?person1 beol:hasFamilyName ?name . + | + | beol:hasFamilyName knora-api:objectType xsd:string . + | ?name a xsd:string . + | + | FILTER(?name = "Meier") + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2607,51 +2856,53 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | ?person1 beol:hasFamilyName ?name . - | - | beol:hasFamilyName knora-api:objectType xsd:string . - | ?name a xsd:string . - | - | FILTER(?name = "Meier") - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | ?person1 beol:hasFamilyName ?name . + | + | beol:hasFamilyName knora-api:objectType xsd:string . + | ?name a xsd:string . + | + | FILTER(?name = "Meier") + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -2664,57 +2915,62 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | ?person1 beol:hasFamilyName ?name . - | - | beol:hasFamilyName knora-api:objectType xsd:string . - | ?name a xsd:string . - | - | FILTER(?name = "Muster") - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | ?person1 beol:hasFamilyName ?name . + | + | beol:hasFamilyName knora-api:objectType xsd:string . + | ?name a xsd:string . + | + | FILTER(?name = "Muster") + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2727,44 +2983,48 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX beol: - | PREFIX knora-api: - | PREFIX foaf: - | - | CONSTRUCT { - | ?person knora-api:isMainResource true . - | - | ?person foaf:familyName ?familyName . - | - | ?person foaf:givenName ?givenName . - | - | } WHERE { - | ?person a knora-api:Resource . - | ?person a foaf:Person . - | - | ?person foaf:familyName ?familyName . - | foaf:familyName knora-api:objectType xsd:string . - | - | ?familyName a xsd:string . - | - | ?person foaf:givenName ?givenName . - | foaf:givenName knora-api:objectType xsd:string . - | - | ?givenName a xsd:string . - | - | FILTER(?familyName = "Meier") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/foafPerson.jsonld"), - writeTestDataFiles) + | PREFIX beol: + | PREFIX knora-api: + | PREFIX foaf: + | + | CONSTRUCT { + | ?person knora-api:isMainResource true . + | + | ?person foaf:familyName ?familyName . + | + | ?person foaf:givenName ?givenName . + | + | } WHERE { + | ?person a knora-api:Resource . + | ?person a foaf:Person . + | + | ?person foaf:familyName ?familyName . + | foaf:familyName knora-api:objectType xsd:string . + | + | ?familyName a xsd:string . + | + | ?person foaf:givenName ?givenName . + | foaf:givenName knora-api:objectType xsd:string . + | + | ?givenName a xsd:string . + | + | FILTER(?familyName = "Meier") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/foafPerson.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -2777,37 +3037,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "run a Gravsearch query that searches for a single resource specified by its IRI" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true ; - | anything:hasText ?text ; - | anything:hasInteger ?integer . - | - |} WHERE { - | BIND( AS ?thing) - | - | ?thing a knora-api:Resource . - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | anything:hasText knora-api:objectType xsd:string . - | ?text a xsd:string . - | ?thing anything:hasInteger ?integer . - | anything:hasInteger knora-api:objectType xsd:integer . - | ?integer a xsd:integer. - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true ; + | anything:hasText ?text ; + | anything:hasInteger ?integer . + | + |} WHERE { + | BIND( AS ?thing) + | + | ?thing a knora-api:Resource . + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | anything:hasText knora-api:objectType xsd:string . + | ?text a xsd:string . + | ?thing anything:hasInteger ?integer . + | anything:hasInteger knora-api:objectType xsd:integer . + | ?integer a xsd:integer. + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingByIriWithRequestedValues.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingByIriWithRequestedValues.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -2815,57 +3079,62 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query for a letter and get information about the persons associated with it" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?familyName . - | - | - | } WHERE { - | BIND( AS ?letter) - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | # testperson2 - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | ?person1 beol:hasFamilyName ?familyName . - | beol:hasFamilyName knora-api:objectType xsd:string . - | - | ?familyName a xsd:string . - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?familyName . + | + | + | } WHERE { + | BIND( AS ?letter) + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | # testperson2 + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | ?person1 beol:hasFamilyName ?familyName . + | beol:hasFamilyName knora-api:objectType xsd:string . + | + | ?familyName a xsd:string . + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithAuthorWithInformation.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithAuthorWithInformation.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -2875,46 +3144,50 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | - | ?page knora-api:isPartOf ?book ; - | incunabula:seqnum ?seqnum . - | } WHERE { - | BIND( AS ?book) - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | ?title a xsd:string . - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf ?book . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | FILTER(?seqnum <= 10) - | - | ?seqnum a xsd:integer . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/incomingPagesForBook.jsonld"), - writeTestDataFiles) + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | + | ?page knora-api:isPartOf ?book ; + | incunabula:seqnum ?seqnum . + | } WHERE { + | BIND( AS ?book) + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | ?title a xsd:string . + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf ?book . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | FILTER(?seqnum <= 10) + | + | ?seqnum a xsd:integer . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/incomingPagesForBook.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -2923,40 +3196,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query containing a statement whose subject is not the main resource and whose object is used in ORDER BY" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | - | ?page knora-api:isPartOf ?book ; - | incunabula:seqnum ?seqnum . - | } WHERE { - | BIND( AS ?book) - | ?book a knora-api:Resource . - | - | ?book incunabula:title ?title . - | incunabula:title knora-api:objectType xsd:string . - | ?title a xsd:string . - | - | ?page a incunabula:page . - | ?page a knora-api:Resource . - | - | ?page knora-api:isPartOf ?book . - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | ?page incunabula:seqnum ?seqnum . - | incunabula:seqnum knora-api:objectType xsd:integer . - | - | FILTER(?seqnum <= 10) - | - | ?seqnum a xsd:integer . - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | + | ?page knora-api:isPartOf ?book ; + | incunabula:seqnum ?seqnum . + | } WHERE { + | BIND( AS ?book) + | ?book a knora-api:Resource . + | + | ?book incunabula:title ?title . + | incunabula:title knora-api:objectType xsd:string . + | ?title a xsd:string . + | + | ?page a incunabula:page . + | ?page a knora-api:Resource . + | + | ?page knora-api:isPartOf ?book . + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | ?page incunabula:seqnum ?seqnum . + | incunabula:seqnum knora-api:objectType xsd:integer . + | + | FILTER(?seqnum <= 10) + | + | ?seqnum a xsd:integer . + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -2967,56 +3242,60 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?region knora-api:isMainResource true . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book incunabula:title ?title . - | - |} WHERE { - | ?region a knora-api:Resource . - | ?region a knora-api:Region . - | - | ?region knora-api:isRegionOf ?page . - | - | knora-api:isRegionOf knora-api:objectType knora-api:Resource . - | - | ?page a knora-api:Resource . - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | knora-api:isPartOf knora-api:objectType knora-api:Resource . - | - | ?book a knora-api:Resource . - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | incunabula:title knora-api:objectType xsd:string . - | - | ?title a xsd:string . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?region knora-api:isMainResource true . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book incunabula:title ?title . + | + |} WHERE { + | ?region a knora-api:Resource . + | ?region a knora-api:Region . + | + | ?region knora-api:isRegionOf ?page . + | + | knora-api:isRegionOf knora-api:objectType knora-api:Resource . + | + | ?page a knora-api:Resource . + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | knora-api:isPartOf knora-api:objectType knora-api:Resource . + | + | ?book a knora-api:Resource . + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | incunabula:title knora-api:objectType xsd:string . + | + | ?title a xsd:string . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/regionsOfZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/regionsOfZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -3025,33 +3304,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query containing a UNION nested in an OPTIONAL" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX gravsearchtest1: - | - |CONSTRUCT { - | ?Project knora-api:isMainResource true . - | ?isInProject gravsearchtest1:isInProject ?Project . - |} WHERE { - | ?Project a knora-api:Resource . - | ?Project a gravsearchtest1:Project . - | - | OPTIONAL { - | ?isInProject gravsearchtest1:isInProject ?Project . - | gravsearchtest1:isInProject knora-api:objectType knora-api:Resource . - | ?isInProject a knora-api:Resource . - | { ?isInProject a gravsearchtest1:BibliographicNotice . } UNION { ?isInProject a gravsearchtest1:Person . } - | } - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX gravsearchtest1: + | + |CONSTRUCT { + | ?Project knora-api:isMainResource true . + | ?isInProject gravsearchtest1:isInProject ?Project . + |} WHERE { + | ?Project a knora-api:Resource . + | ?Project a gravsearchtest1:Project . + | + | OPTIONAL { + | ?isInProject gravsearchtest1:isInProject ?Project . + | gravsearchtest1:isInProject knora-api:objectType knora-api:Resource . + | ?isInProject a knora-api:Resource . + | { ?isInProject a gravsearchtest1:BibliographicNotice . } UNION { ?isInProject a gravsearchtest1:Person . } + | } + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ProjectsWithOptionalPersonOrBiblio.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ProjectsWithOptionalPersonOrBiblio.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -3063,39 +3347,44 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query in which 'rdf:type knora-api:Resource' is inferred from a more specific rdf:type (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | ?letter ?linkingProp1 . - | a beol:person . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | beol:hasFamilyName ?name . - | beol:hasFamilyName knora-api:objectType xsd:string . - | ?name a xsd:string . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | ?letter ?linkingProp1 . + | a beol:person . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | beol:hasFamilyName ?name . + | beol:hasFamilyName knora-api:objectType xsd:string . + | ?name a xsd:string . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3107,37 +3396,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query in which the object types of property IRIs are inferred (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | ?date a knora-api:Date . - | ?letter ?linkingProp1 . - | a beol:person . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | beol:hasFamilyName ?name . - | ?name a xsd:string . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | ?date a knora-api:Date . + | ?letter ?linkingProp1 . + | a beol:person . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | beol:hasFamilyName ?name . + | ?name a xsd:string . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3149,35 +3443,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query in which the types of property objects are inferred from the knora-api:objectType of each property (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | a beol:person . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | beol:hasFamilyName ?name . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | a beol:person . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | beol:hasFamilyName ?name . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3189,34 +3488,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query in which a property's knora-api:objectType is inferred from its object (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | a beol:person . - | beol:hasFamilyName ?name . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | a beol:person . + | beol:hasFamilyName ?name . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3228,34 +3532,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query in which the types of property subjects are inferred from the knora-api:subjectType of each property (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3267,31 +3576,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query in which the knora-api:objectType of a property variable is inferred from a FILTER (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - |} WHERE { - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + |} WHERE { + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithoutName.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithoutName.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3303,33 +3617,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query that finds all the books that have a page with seqnum 100, inferring types (with type inference)" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?page incunabula:partOf ?book ; - | incunabula:seqnum ?seqnum . - | - |} WHERE { - | - | ?page incunabula:partOf ?book ; - | incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum = 100) - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?page incunabula:partOf ?book ; + | incunabula:seqnum ?seqnum . + | + |} WHERE { + | + | ?page incunabula:partOf ?book ; + | incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum = 100) + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/booksWithPage100.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/booksWithPage100.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3339,37 +3658,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query that finds all the letters sent by someone called Meier, ordered by date, inferring types (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | - | ?letter knora-api:isMainResource true ; - | beol:creationDate ?date ; - | beol:hasAuthor ?author . - | - | ?author beol:hasFamilyName ?name . - | - |} WHERE { - | - | ?letter beol:hasAuthor ?author ; - | beol:creationDate ?date . - | - | ?author beol:hasFamilyName ?name . - | - | FILTER(?name = "Meier") - | - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | + | ?letter knora-api:isMainResource true ; + | beol:creationDate ?date ; + | beol:hasAuthor ?author . + | + | ?author beol:hasFamilyName ?name . + | + |} WHERE { + | + | ?letter beol:hasAuthor ?author ; + | beol:creationDate ?date . + | + | ?author beol:hasFamilyName ?name . + | + | FILTER(?name = "Meier") + | + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/lettersByMeier.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/lettersByMeier.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3379,31 +3703,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (in the complex schema) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3414,33 +3743,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the dcterms:title 'Zeitglöcklein des Lebens' returning the title in the answer (in the complex schema) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX dcterms: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book dcterms:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book dcterms:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX dcterms: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book dcterms:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book dcterms:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3451,22 +3785,22 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (in the simple schema) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } """.stripMargin Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) @@ -3477,7 +3811,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { val expectedAnswerJSONLD = readOrWriteTextFile( responseAs[String], Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswerSimple.jsonld"), - writeTestDataFiles) + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3488,23 +3823,23 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the dcterms:title 'Zeitglöcklein des Lebens' returning the title in the answer (in the simple schema) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX dcterms: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book dcterms:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book dcterms:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } + |PREFIX knora-api: + |PREFIX dcterms: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book dcterms:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book dcterms:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } """.stripMargin Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) @@ -3515,7 +3850,8 @@ class SearchRouteV2R2RSpec extends R2RSpec { val expectedAnswerJSONLD = readOrWriteTextFile( responseAs[String], Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswerSimple.jsonld"), - writeTestDataFiles) + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3526,25 +3862,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch count query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -3557,30 +3896,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' not returning the title in the answer (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchNoTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchNoTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3591,32 +3935,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that do not have the title 'Zeitglöcklein des Lebens' (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/NotZeitgloeckleinExtendedSearch.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/NotZeitgloeckleinExtendedSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3627,25 +3976,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch count query for books that do not have the title 'Zeitglöcklein des Lebens' (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title != "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -3664,35 +4016,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum = 10) - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum = 10) + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageWithSeqnum10WithSeqnumAndLinkValueInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageWithSeqnum10WithSeqnumAndLinkValueInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3704,28 +4061,31 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum = 10) - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum = 10) + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -3739,33 +4099,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum = 10) - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum = 10) + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageWithSeqnum10OnlySeqnuminAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageWithSeqnum10OnlySeqnuminAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3777,35 +4142,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum <= 10) - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum <= 10) + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/pagesOfLatinNarrenschiffWithSeqnumLowerEquals10.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/pagesOfLatinNarrenschiffWithSeqnumLowerEquals10.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3817,33 +4187,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnum.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnum.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3855,34 +4230,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | } ORDER BY ?seqnum - | OFFSET 1 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | } ORDER BY ?seqnum + | OFFSET 1 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnumNextOffset.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnumNextOffset.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3893,26 +4273,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published on the first of March 1497 (Julian Calendar) (with type inference)" ignore { // literals are not supported val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate "JULIAN:1497-03-01"^^knora-api:Date . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -3925,34 +4308,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published on the first of March 1497 (Julian Calendar) (2) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate = "JULIAN:1497-03-01"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate = "JULIAN:1497-03-01"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedOnDate.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -3963,35 +4351,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have not been published on the first of March 1497 (Julian Calendar) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate != "JULIAN:1497-03-01"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate != "JULIAN:1497-03-01"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4005,35 +4398,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have not been published on the first of March 1497 (Julian Calendar) 2 (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate < "JULIAN:1497-03-01"^^knora-api:Date || ?pubdate > "JULIAN:1497-03-01"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate < "JULIAN:1497-03-01"^^knora-api:Date || ?pubdate > "JULIAN:1497-03-01"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4047,35 +4445,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published before 1497 (Julian Calendar) (with type inference)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate < "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate < "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBeforeDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBeforeDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4089,35 +4492,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published 1497 or later (Julian Calendar) (with type inference)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate >= "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate >= "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedAfterOrOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedAfterOrOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4131,35 +4539,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published after 1497 (Julian Calendar) (with type inference)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate > "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate > "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedAfterDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedAfterDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4173,35 +4586,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published 1497 or before (Julian Calendar) (with type inference)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate <= "JULIAN:1497"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate <= "JULIAN:1497"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBeforeOrOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBeforeOrOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4215,35 +4633,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published after 1486 and before 1491 (Julian Calendar) (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate > "JULIAN:1486"^^knora-api:Date && ?pubdate < "JULIAN:1491"^^knora-api:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate > "JULIAN:1486"^^knora-api:Date && ?pubdate < "JULIAN:1491"^^knora-api:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBetweenDates.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBetweenDates.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4256,40 +4679,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get the regions belonging to a page (with type inference)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | ?region knora-api:isMainResource true . - | - | ?region knora-api:isRegionOf . - | - | ?region knora-api:hasGeometry ?geom . - | - | ?region knora-api:hasComment ?comment . - | - | ?region knora-api:hasColor ?color . - | } WHERE { - | - | ?region a knora-api:Region . - | - | ?region knora-api:isRegionOf . - | - | ?region knora-api:hasGeometry ?geom . - | - | ?region knora-api:hasComment ?comment . - | - | ?region knora-api:hasColor ?color . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/RegionsForPage.jsonld"), - writeFile = false) + | PREFIX knora-api: + | + | CONSTRUCT { + | ?region knora-api:isMainResource true . + | + | ?region knora-api:isRegionOf . + | + | ?region knora-api:hasGeometry ?geom . + | + | ?region knora-api:hasComment ?comment . + | + | ?region knora-api:hasColor ?color . + | } WHERE { + | + | ?region a knora-api:Region . + | + | ?region knora-api:isRegionOf . + | + | ?region knora-api:hasGeometry ?geom . + | + | ?region knora-api:hasComment ?comment . + | + | ?region knora-api:hasColor ?color . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/RegionsForPage.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4302,44 +4730,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a book a page points to and include the page in the results (all properties present in WHERE clause) (with type inference)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFile ?file . - | - |} WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFile ?file . - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFile ?file . + | + |} WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFile ?file . + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/bookWithIncomingPagesWithAllRequestedProps.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/bookWithIncomingPagesWithAllRequestedProps.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4350,40 +4783,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a book a page points to and only include the page's partOf link in the results (none of the other properties) (with type inference)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - |} WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFile ?file . - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + |} WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFile ?file . + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/bookWithIncomingPagesOnlyLink.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/bookWithIncomingPagesOnlyLink.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4394,36 +4832,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get incoming links pointing to an incunbaula:book, excluding isPartOf (with type inference)" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?incomingRes knora-api:isMainResource true . - | - | ?incomingRes ?incomingProp . - | - |} WHERE { - | - | ?incomingRes ?incomingProp . - | - | a incunabula:book . - | - | - | FILTER NOT EXISTS { - | ?incomingRes knora-api:isPartOf . - | } - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?incomingRes knora-api:isMainResource true . + | + | ?incomingRes ?incomingProp . + | + |} WHERE { + | + | ?incomingRes ?incomingProp . + | + | a incunabula:book . + | + | + | FILTER NOT EXISTS { + | ?incomingRes knora-api:isPartOf . + | } + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/IncomingLinksForBook.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/IncomingLinksForBook.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4434,25 +4877,27 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value of 2.1 (with type inference)" ignore { // literals are not supported val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal"2.1"^^xsd:decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasDecimal "2.1"^^xsd:decimal . - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal"2.1"^^xsd:decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasDecimal "2.1"^^xsd:decimal . + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -4465,32 +4910,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value of 2.1 2 (with type inference)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasDecimal ?decimal . - | - | FILTER(?decimal = "2.1"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasDecimal ?decimal . + | + | FILTER(?decimal = "2.1"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4503,32 +4952,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value bigger than 2.0 (with type inference)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasDecimal ?decimal . - | - | FILTER(?decimal > "2"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasDecimal ?decimal . + | + | FILTER(?decimal > "2"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingBiggerThanDecimal.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingBiggerThanDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4541,33 +4994,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value smaller than 3.0 (with type inference)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasDecimal ?decimal . - | - | FILTER(?decimal < "3"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasDecimal ?decimal . + | + | FILTER(?decimal < "3"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingSmallerThanDecimal.jsonld"), - writeFile = false) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingSmallerThanDecimal.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4580,25 +5037,27 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a Boolean value that is true (with type inference)" ignore { // literals are not supported val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean true - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasBoolean true . - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean true + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasBoolean true . + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -4611,33 +5070,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a Boolean value that is true 2 (with type inference)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasBoolean ?boolean . - | - | FILTER(?boolean = true) - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasBoolean ?boolean . + | + | FILTER(?boolean = true) + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBoolean.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBoolean.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4650,37 +5113,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { // set OFFSET to 1 to get "Testding for extended search" val gravsearchQuery = """PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasBoolean ?boolean . - | FILTER(?boolean = true) - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | FILTER(?intVal = 123454321 || ?intVal = 999999999) - | } - |} OFFSET 1""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasBoolean ?boolean . + | FILTER(?boolean = true) + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | FILTER(?intVal = 123454321 || ?intVal = 999999999) + | } + |} OFFSET 1""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset1.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset1.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4694,43 +5161,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | { - | ?thing anything:hasBoolean ?boolean . - | - | FILTER(?boolean = true) - | } UNION { - | ?thing anything:hasDecimal ?decimal . - | - | FILTER(?decimal = "2.1"^^xsd:decimal) - | } - | - |} OFFSET 0 - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | { + | ?thing anything:hasBoolean ?boolean . + | + | FILTER(?boolean = true) + | } UNION { + | ?thing anything:hasDecimal ?decimal . + | + | FILTER(?decimal = "2.1"^^xsd:decimal) + | } + | + |} OFFSET 0 + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOrDecimal.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOrDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4744,35 +5215,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?propVal0 . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes incunabula:title ?propVal0 . - | - | FILTER regex(?propVal0, "Zeit", "i") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?propVal0 . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes incunabula:title ?propVal0 . + | + | FILTER regex(?propVal0, "Zeit", "i") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeit.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeit.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4786,35 +5261,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?propVal0 . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes incunabula:title ?propVal0 . - | - | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?propVal0 . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes incunabula:title ?propVal0 . + | + | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4828,35 +5307,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?propVal0 . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes incunabula:title ?propVal0 . - | - | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein AND Lebens") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?propVal0 . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes incunabula:title ?propVal0 . + | + | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein AND Lebens") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4870,33 +5353,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX knora-api: - | PREFIX dcterms: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book dcterms:title ?title . - | - | } WHERE { - | ?book a knora-api:Resource . - | - | ?book dcterms:title ?title . - | - | FILTER(?title = 'Zeitglöcklein des Lebens und Leidens Christi') - | - | } OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX knora-api: + | PREFIX dcterms: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book dcterms:title ?title . + | + | } WHERE { + | ?book a knora-api:Resource . + | + | ?book dcterms:title ?title . + | + | FILTER(?title = 'Zeitglöcklein des Lebens und Leidens Christi') + | + | } OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4910,30 +5397,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing a anything:Thing . - | - | ?thing anything:hasListItem ?listItem . - | - | } OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing a anything:Thing . + | + | ?thing anything:hasListItem ?listItem . + | + | } OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithListValue.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithListValue.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -4947,30 +5438,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | - | ?thing anything:hasText ?text . - | - | FILTER(lang(?text) = "fr") - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | + | ?thing anything:hasText ?text . + | + | FILTER(lang(?text) = "fr") + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) @@ -4981,30 +5476,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | - | ?thing anything:hasText ?text . - | - | FILTER(lang(?text) = "fr" && ?text = "Bonjour") - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | + | ?thing anything:hasText ?text . + | + | FILTER(lang(?text) = "fr" && ?text = "Bonjour") + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) @@ -5015,34 +5514,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?linkObj knora-api:isMainResource true . - | - | ?linkObj knora-api:hasLinkTo ?book . - | - |} WHERE { - | ?linkObj a knora-api:LinkObj . - | - | ?linkObj knora-api:hasLinkTo ?book . - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?linkObj knora-api:isMainResource true . + | + | ?linkObj knora-api:hasLinkTo ?book . + | + |} WHERE { + | ?linkObj a knora-api:LinkObj . + | + | ?linkObj knora-api:hasLinkTo ?book . + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LinkObjectsToBooks.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LinkObjectsToBooks.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5056,39 +5559,43 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 . - | - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | # testperson2 - | ?letter ?linkingProp1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithAuthor.jsonld"), - writeTestDataFiles) + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 . + | + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | # testperson2 + | ?letter ?linkingProp1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithAuthor.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5101,45 +5608,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | ?person1 beol:hasFamilyName ?name . - | - | FILTER(?name = "Meier") - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | ?person1 beol:hasFamilyName ?name . + | + | FILTER(?name = "Meier") + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5152,44 +5663,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | ?person1 beol:hasFamilyName ?name . - | - | FILTER(?name = "Muster") - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | ?person1 beol:hasFamilyName ?name . + | + | FILTER(?name = "Muster") + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5202,42 +5718,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX beol: - | PREFIX knora-api: - | PREFIX foaf: - | - | CONSTRUCT { - | ?person knora-api:isMainResource true . - | - | ?person foaf:familyName ?familyName . - | - | ?person foaf:givenName ?givenName . - | - | } WHERE { - | ?person a knora-api:Resource . - | ?person a foaf:Person . - | - | ?person foaf:familyName ?familyName . - | - | ?familyName a xsd:string . - | - | ?person foaf:givenName ?givenName . - | - | ?givenName a xsd:string . - | - | FILTER(?familyName = "Meier") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/foafPerson.jsonld"), - writeTestDataFiles) + | PREFIX beol: + | PREFIX knora-api: + | PREFIX foaf: + | + | CONSTRUCT { + | ?person knora-api:isMainResource true . + | + | ?person foaf:familyName ?familyName . + | + | ?person foaf:givenName ?givenName . + | + | } WHERE { + | ?person a knora-api:Resource . + | ?person a foaf:Person . + | + | ?person foaf:familyName ?familyName . + | + | ?familyName a xsd:string . + | + | ?person foaf:givenName ?givenName . + | + | ?givenName a xsd:string . + | + | FILTER(?familyName = "Meier") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/foafPerson.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5250,32 +5770,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "run a Gravsearch query that searches for a single resource specified by its IRI (with type inference)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true ; - | anything:hasText ?text ; - | anything:hasInteger ?integer . - | - |} WHERE { - | BIND( AS ?thing) - | - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?thing anything:hasInteger ?integer . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true ; + | anything:hasText ?text ; + | anything:hasInteger ?integer . + | + |} WHERE { + | BIND( AS ?thing) + | + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?thing anything:hasInteger ?integer . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingByIriWithRequestedValues.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingByIriWithRequestedValues.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -5283,43 +5807,48 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query for a letter and get information about the persons associated with it (with type inference)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?familyName . - | - | - | } WHERE { - | BIND( AS ?letter) - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | # testperson2 - | ?letter ?linkingProp1 ?person1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | ?person1 beol:hasFamilyName ?familyName . - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?familyName . + | + | + | } WHERE { + | BIND( AS ?letter) + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | # testperson2 + | ?letter ?linkingProp1 ?person1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | ?person1 beol:hasFamilyName ?familyName . + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithAuthorWithInformation.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithAuthorWithInformation.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -5329,39 +5858,43 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | - | ?page knora-api:isPartOf ?book ; - | incunabula:seqnum ?seqnum . - | } WHERE { - | BIND( AS ?book) - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?page incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum <= 10) - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | + | ?page knora-api:isPartOf ?book ; + | incunabula:seqnum ?seqnum . + | } WHERE { + | BIND( AS ?book) + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?page incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum <= 10) + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/incomingPagesForBook.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/incomingPagesForBook.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -5370,32 +5903,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query containing a statement whose subject is not the main resource and whose object is used in ORDER BY (with type inference)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | - | ?page knora-api:isPartOf ?book ; - | incunabula:seqnum ?seqnum . - | } WHERE { - | BIND( AS ?book) - | - | ?book incunabula:title ?title . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?page incunabula:seqnum ?seqnum . - | - | FILTER(?seqnum <= 10) - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | + | ?page knora-api:isPartOf ?book ; + | incunabula:seqnum ?seqnum . + | } WHERE { + | BIND( AS ?book) + | + | ?book incunabula:title ?title . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?page incunabula:seqnum ?seqnum . + | + | FILTER(?seqnum <= 10) + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -5406,45 +5941,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?region knora-api:isMainResource true . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book incunabula:title ?title . - | - |} WHERE { - | ?region a knora-api:Region . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?region knora-api:isMainResource true . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book incunabula:title ?title . + | + |} WHERE { + | ?region a knora-api:Region . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/regionsOfZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/regionsOfZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -5453,30 +5992,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query containing a UNION nested in an OPTIONAL (with type inference)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX gravsearchtest1: - | - |CONSTRUCT { - | ?Project knora-api:isMainResource true . - | ?isInProject gravsearchtest1:isInProject ?Project . - |} WHERE { - | ?Project a gravsearchtest1:Project . - | - | OPTIONAL { - | ?isInProject gravsearchtest1:isInProject ?Project . - | { ?isInProject a gravsearchtest1:BibliographicNotice . } UNION { ?isInProject a gravsearchtest1:Person . } - | } - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX gravsearchtest1: + | + |CONSTRUCT { + | ?Project knora-api:isMainResource true . + | ?isInProject gravsearchtest1:isInProject ?Project . + |} WHERE { + | ?Project a gravsearchtest1:Project . + | + | OPTIONAL { + | ?isInProject gravsearchtest1:isInProject ?Project . + | { ?isInProject a gravsearchtest1:BibliographicNotice . } UNION { ?isInProject a gravsearchtest1:Person . } + | } + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ProjectsWithOptionalPersonOrBiblio.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ProjectsWithOptionalPersonOrBiblio.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -5485,33 +6029,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query that searches for a list node (with type inference)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | - |?mainRes knora-api:isMainResource true . - | - |?mainRes anything:hasListItem ?propVal0 . - | - |} WHERE { - | - |?mainRes anything:hasListItem ?propVal0 . - | - |FILTER(?propVal0 = "Tree list node 02"^^knora-api:ListNode) - | - |} - | - |OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | + |?mainRes knora-api:isMainResource true . + | + |?mainRes anything:hasListItem ?propVal0 . + | + |} WHERE { + | + |?mainRes anything:hasListItem ?propVal0 . + | + |FILTER(?propVal0 = "Tree list node 02"^^knora-api:ListNode) + | + |} + | + |OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithListNodeLabel.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithListNodeLabel.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -5520,27 +6069,30 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch count query that searches for a list node (with type inference)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | - |?mainRes knora-api:isMainResource true . - | - |?mainRes anything:hasListItem ?propVal0 . - | - |} WHERE { - | - |?mainRes anything:hasListItem ?propVal0 . - | - |FILTER(?propVal0 = "Tree list node 02"^^knora-api:ListNode) - | - |} - | - |OFFSET 0 - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | + |?mainRes knora-api:isMainResource true . + | + |?mainRes anything:hasListItem ?propVal0 . + | + |} WHERE { + | + |?mainRes anything:hasListItem ?propVal0 . + | + |FILTER(?propVal0 = "Tree list node 02"^^knora-api:ListNode) + | + |} + | + |OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -5556,41 +6108,46 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a anything:Thing . - | - | OPTIONAL { - | ?thing anything:hasDate ?date . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 123454321 . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 999999999 . - | } - |} - |ORDER BY DESC(?date) - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a anything:Thing . + | + | OPTIONAL { + | ?thing anything:hasDate ?date . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 123454321 . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 999999999 . + | } + |} + |ORDER BY DESC(?date) + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingWithOptionalDateSortedDesc.jsonld"), - writeFile = false) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingWithOptionalDateSortedDesc.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5602,34 +6159,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a anything:Thing . - | - | OPTIONAL { - | ?thing anything:hasDate ?date . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 123454321 . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 999999999 . - | } - |} - |ORDER BY DESC(?date) - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a anything:Thing . + | + | OPTIONAL { + | ?thing anything:hasDate ?date . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 123454321 . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 999999999 . + | } + |} + |ORDER BY DESC(?date) + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -5643,44 +6203,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | ?decimal knora-api:decimalValueAsDecimal ?decimalVal . - | FILTER(?decimalVal > "1"^^xsd:decimal) - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 123454321 . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 999999999 . - | } - |} ORDER BY DESC(?decimal) - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | ?decimal knora-api:decimalValueAsDecimal ?decimalVal . + | FILTER(?decimalVal > "1"^^xsd:decimal) + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 123454321 . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 999999999 . + | } + |} ORDER BY DESC(?decimal) + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingsWithOptionalDecimalGreaterThan1.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingsWithOptionalDecimalGreaterThan1.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5691,33 +6256,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query that finds all the books that have a page with seqnum 100 (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?page incunabula:partOf ?book ; - | incunabula:seqnum ?seqnum . - | - |} WHERE { - | - | ?page incunabula:partOf ?book ; - | incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt 100 . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?page incunabula:partOf ?book ; + | incunabula:seqnum ?seqnum . + | + |} WHERE { + | + | ?page incunabula:partOf ?book ; + | incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt 100 . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/booksWithPage100.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/booksWithPage100.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5727,37 +6297,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query that finds all the letters sent by someone called Meier, ordered by date (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | - | ?letter knora-api:isMainResource true ; - | beol:creationDate ?date ; - | beol:hasAuthor ?author . - | - | ?author beol:hasFamilyName ?name . - | - |} WHERE { - | - | ?letter beol:hasAuthor ?author ; - | beol:creationDate ?date . - | - | ?author beol:hasFamilyName ?name . - | - | ?name knora-api:valueAsString "Meier" . - | - |} ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | + | ?letter knora-api:isMainResource true ; + | beol:creationDate ?date ; + | beol:hasAuthor ?author . + | + | ?author beol:hasFamilyName ?name . + | + |} WHERE { + | + | ?letter beol:hasAuthor ?author ; + | beol:creationDate ?date . + | + | ?author beol:hasFamilyName ?name . + | + | ?name knora-api:valueAsString "Meier" . + | + |} ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/lettersByMeier.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/lettersByMeier.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5767,31 +6342,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchWithTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5802,25 +6382,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch count query for books that have the title 'Zeitglöcklein des Lebens' returning the title in the answer (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -5833,30 +6416,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have the title 'Zeitglöcklein des Lebens' not returning the title in the answer (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchNoTitleInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ZeitgloeckleinExtendedSearchNoTitleInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5867,34 +6455,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that do not have the title 'Zeitglöcklein des Lebens' (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString ?titleStr . - | - | FILTER(?titleStr != "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString ?titleStr . + | + | FILTER(?titleStr != "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/NotZeitgloeckleinExtendedSearch.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/NotZeitgloeckleinExtendedSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5905,27 +6498,30 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch count query for books that do not have the title 'Zeitglöcklein des Lebens' (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString ?titleStr . - | - | FILTER(?titleStr != "Zeitglöcklein des Lebens und Leidens Christi") - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString ?titleStr . + | + | FILTER(?titleStr != "Zeitglöcklein des Lebens und Leidens Christi") + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -5944,35 +6540,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt 10 . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt 10 . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageWithSeqnum10WithSeqnumAndLinkValueInAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageWithSeqnum10WithSeqnumAndLinkValueInAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -5984,27 +6585,30 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt 10 . - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt 10 . + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -6018,33 +6622,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt 10 . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt 10 . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageWithSeqnum10OnlySeqnuminAnswer.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageWithSeqnum10OnlySeqnuminAnswer.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6056,37 +6665,42 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt ?seqnumInt . - | - | FILTER(?seqnumInt <= 10) - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt ?seqnumInt . + | + | FILTER(?seqnumInt <= 10) + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/pagesOfLatinNarrenschiffWithSeqnumLowerEquals10.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/pagesOfLatinNarrenschiffWithSeqnumLowerEquals10.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6098,33 +6712,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnum.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnum.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6136,34 +6755,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?page knora-api:isMainResource true . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | } WHERE { - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf . - | - | ?page incunabula:seqnum ?seqnum . - | - | } ORDER BY ?seqnum - | OFFSET 1 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?page knora-api:isMainResource true . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | } WHERE { + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf . + | + | ?page incunabula:seqnum ?seqnum . + | + | } ORDER BY ?seqnum + | OFFSET 1 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnumNextOffset.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PagesOfNarrenschiffOrderedBySeqnumNextOffset.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6174,35 +6798,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published on the first of March 1497 (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) = "JULIAN:1497-03-01"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) = "JULIAN:1497-03-01"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedOnDate.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6213,36 +6842,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have not been published on the first of March 1497 (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) != "JULIAN:1497-03-01"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) != "JULIAN:1497-03-01"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6256,36 +6890,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have not been published on the first of March 1497 (Julian Calendar) 2 (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) < "JULIAN:1497-03-01"^^knora-api-simple:Date || knora-api:toSimpleDate(?pubdate) > "JULIAN:1497-03-01"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) < "JULIAN:1497-03-01"^^knora-api-simple:Date || knora-api:toSimpleDate(?pubdate) > "JULIAN:1497-03-01"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksNotPublishedOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6299,36 +6938,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published before 1497 (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) < "JULIAN:1497"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) < "JULIAN:1497"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBeforeDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBeforeDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6342,36 +6986,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published 1497 or later (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) >= "JULIAN:1497"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) >= "JULIAN:1497"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedAfterOrOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedAfterOrOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6385,36 +7034,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published after 1497 (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) > "JULIAN:1497"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) > "JULIAN:1497"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedAfterDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedAfterDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6428,36 +7082,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published 1497 or before (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """ PREFIX incunabula: - | PREFIX knora-api: - | PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) <= "JULIAN:1497"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + | PREFIX knora-api: + | PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) <= "JULIAN:1497"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBeforeOrOnDate.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBeforeOrOnDate.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6471,36 +7130,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a Gravsearch query for books that have been published after 1486 and before 1491 (Julian Calendar) (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - |PREFIX knora-api-simple: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | } WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?book incunabula:pubdate ?pubdate . - | - | FILTER(knora-api:toSimpleDate(?pubdate) > "JULIAN:1486"^^knora-api-simple:Date && knora-api:toSimpleDate(?pubdate) < "JULIAN:1491"^^knora-api-simple:Date) - | - | } ORDER BY ?pubdate - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX knora-api-simple: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | } WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?book incunabula:pubdate ?pubdate . + | + | FILTER(knora-api:toSimpleDate(?pubdate) > "JULIAN:1486"^^knora-api-simple:Date && knora-api:toSimpleDate(?pubdate) < "JULIAN:1491"^^knora-api-simple:Date) + | + | } ORDER BY ?pubdate + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksPublishedBetweenDates.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksPublishedBetweenDates.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6513,29 +7177,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get the regions belonging to a page (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?region knora-api:isMainResource true . - | ?region knora-api:isRegionOf . - | ?region knora-api:hasGeometry ?geom . - | ?region knora-api:hasComment ?comment . - | ?region knora-api:hasColor ?color . - |} WHERE { - | ?region a knora-api:Region . - | ?region knora-api:isRegionOf . - | ?region knora-api:hasGeometry ?geom . - | ?region knora-api:hasComment ?comment . - | ?region knora-api:hasColor ?color . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + | + |CONSTRUCT { + | ?region knora-api:isMainResource true . + | ?region knora-api:isRegionOf . + | ?region knora-api:hasGeometry ?geom . + | ?region knora-api:hasComment ?comment . + | ?region knora-api:hasColor ?color . + |} WHERE { + | ?region a knora-api:Region . + | ?region knora-api:isRegionOf . + | ?region knora-api:hasGeometry ?geom . + | ?region knora-api:hasComment ?comment . + | ?region knora-api:hasColor ?color . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/RegionsForPage.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/RegionsForPage.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6548,44 +7217,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a book a page points to and include the page in the results (all properties present in WHERE clause) (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFileValue ?file . - | - |} WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFileValue ?file . - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFileValue ?file . + | + |} WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFileValue ?file . + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/bookWithIncomingPagesWithAllRequestedProps.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/bookWithIncomingPagesWithAllRequestedProps.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6596,40 +7270,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a book a page points to and only include the page's partOf link in the results (none of the other properties) (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - |} WHERE { - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | knora-api:isPartOf ?book . - | - | knora-api:seqnum ?seqnum . - | - | knora-api:hasStillImageFileValue ?file . - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + |} WHERE { + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | knora-api:isPartOf ?book . + | + | knora-api:seqnum ?seqnum . + | + | knora-api:hasStillImageFileValue ?file . + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/bookWithIncomingPagesOnlyLink.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/bookWithIncomingPagesOnlyLink.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6640,35 +7319,40 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get incoming links pointing to an incunbaula:book, excluding isPartOf (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | - | ?incomingRes knora-api:isMainResource true . - | - | ?incomingRes ?incomingProp . - | - |} WHERE { - | - | ?incomingRes ?incomingProp . - | - | a incunabula:book . - | - | FILTER NOT EXISTS { - | ?incomingRes knora-api:isPartOf . - | } - | - |} OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | + | ?incomingRes knora-api:isMainResource true . + | + | ?incomingRes ?incomingProp . + | + |} WHERE { + | + | ?incomingRes ?incomingProp . + | + | a incunabula:book . + | + | FILTER NOT EXISTS { + | ?incomingRes knora-api:isPartOf . + | } + | + |} OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/IncomingLinksForBook.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/IncomingLinksForBook.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6679,32 +7363,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value of 2.1 (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasDecimal ?decimal . - | - | ?decimal knora-api:decimalValueAsDecimal "2.1"^^xsd:decimal . - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasDecimal ?decimal . + | + | ?decimal knora-api:decimalValueAsDecimal "2.1"^^xsd:decimal . + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6717,24 +7405,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value of 2.1 (submitting the complex schema), without inference" in { val gravsearchQuery = """PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | knora-api:GravsearchOptions knora-api:useInference false . - | ?thing a anything:Thing . - | ?thing anything:hasDecimal ?decimal . - | ?decimal knora-api:decimalValueAsDecimal "2.1"^^xsd:decimal . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), - writeTestDataFiles) + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | knora-api:GravsearchOptions knora-api:useInference false . + | ?thing a anything:Thing . + | ?thing anything:hasDecimal ?decimal . + | ?decimal knora-api:decimalValueAsDecimal "2.1"^^xsd:decimal . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + assert(status == StatusCodes.OK, response.toString) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingEqualsDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) } @@ -6743,34 +7435,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value bigger than 2.0 (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasDecimal ?decimal . - | - | ?decimal knora-api:decimalValueAsDecimal ?decimalDec . - | - | FILTER(?decimalDec > "2"^^xsd:decimal) - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasDecimal ?decimal . + | + | ?decimal knora-api:decimalValueAsDecimal ?decimalDec . + | + | FILTER(?decimalDec > "2"^^xsd:decimal) + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingBiggerThanDecimal.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingBiggerThanDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6783,27 +7479,31 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a decimal value smaller than 3.0 (submitting the complex schema)" in { val gravsearchQuery = """PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasDecimal ?decimal . - | ?decimal knora-api:decimalValueAsDecimal ?decimalDec . - | FILTER(?decimalDec < "3"^^xsd:decimal) - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasDecimal ?decimal . + | ?decimal knora-api:decimalValueAsDecimal ?decimalDec . + | FILTER(?decimalDec < "3"^^xsd:decimal) + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD: String = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingSmallerThanDecimal.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingSmallerThanDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6826,25 +7526,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a link to a specified other thing" in { val gravsearchQuery = """PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasOtherThing . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasOtherThing . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasOtherThing . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasOtherThing . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD: String = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithLinkToStart.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithLinkToStart.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6866,22 +7570,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { "return a page of anything:Thing resources" in { val gravsearchQuery = """PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing a anything:Thing . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing a anything:Thing . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD: String = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/PageOfThings.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD: String = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/PageOfThings.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6901,33 +7609,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing that has a Boolean value that is true (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | - | ?thing anything:hasBoolean ?boolean . - | - | ?boolean knora-api:booleanValueAsBoolean true . - | - |} - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | + | ?thing anything:hasBoolean ?boolean . + | + | ?boolean knora-api:booleanValueAsBoolean true . + | + |} + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBoolean.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBoolean.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6940,43 +7652,47 @@ class SearchRouteV2R2RSpec extends R2RSpec { // set OFFSET to 1 to get "Testding for extended search" val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - |} WHERE { - | - | ?thing a anything:Thing . - | - | OPTIONAL { - | ?thing anything:hasBoolean ?boolean . - | ?boolean knora-api:booleanValueAsBoolean true . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 123454321 . - | } - | - | MINUS { - | ?thing anything:hasInteger ?intVal . - | ?intVal knora-api:intValueAsInt 999999999 . - | } - | - |} OFFSET 1""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + |} WHERE { + | + | ?thing a anything:Thing . + | + | OPTIONAL { + | ?thing anything:hasBoolean ?boolean . + | ?boolean knora-api:booleanValueAsBoolean true . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 123454321 . + | } + | + | MINUS { + | ?thing anything:hasInteger ?intVal . + | ?intVal knora-api:intValueAsInt 999999999 . + | } + | + |} OFFSET 1""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset1.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOptionalOffset1.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -6990,44 +7706,48 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasBoolean ?boolean . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | - | { - | ?thing anything:hasBoolean ?boolean . - | - | ?boolean knora-api:booleanValueAsBoolean ?booleanBool . - | - | FILTER(?booleanBool = true) - | } UNION { - | ?thing anything:hasDecimal ?decimal . - | - | ?decimal knora-api:decimalValueAsDecimal "2.1"^^xsd:decimal . - | } - | - |} OFFSET 0 - | - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasBoolean ?boolean . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | + | { + | ?thing anything:hasBoolean ?boolean . + | + | ?boolean knora-api:booleanValueAsBoolean ?booleanBool . + | + | FILTER(?booleanBool = true) + | } UNION { + | ?thing anything:hasDecimal ?decimal . + | + | ?decimal knora-api:decimalValueAsDecimal "2.1"^^xsd:decimal . + | } + | + |} OFFSET 0 + | + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithBooleanOrDecimal.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithBooleanOrDecimal.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7041,37 +7761,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?title . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes incunabula:title ?title . - | - | ?title knora-api:valueAsString ?titleStr . - | - | FILTER regex(?titleStr, "Zeit", "i") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?title . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes incunabula:title ?title . + | + | ?title knora-api:valueAsString ?titleStr . + | + | FILTER regex(?titleStr, "Zeit", "i") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeit.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeit.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7085,35 +7809,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?title . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes incunabula:title ?title . - | - | FILTER knora-api:matchText(?title, "Zeitglöcklein") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?title . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes incunabula:title ?title . + | + | FILTER knora-api:matchText(?title, "Zeitglöcklein") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7127,35 +7855,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?title . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes incunabula:title ?title . - | - | FILTER knora-api:matchText(?title, "Zeitglöcklein AND Lebens") - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?title . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes incunabula:title ?title . + | + | FILTER knora-api:matchText(?title, "Zeitglöcklein AND Lebens") + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/BooksWithTitleContainingZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7169,30 +7901,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing a anything:Thing . - | - | ?thing anything:hasListItem ?listItem . - | - | } OFFSET 0 - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing a anything:Thing . + | + | ?thing anything:hasListItem ?listItem . + | + | } OFFSET 0 + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithListValue.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithListValue.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7206,30 +7942,34 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | - | ?thing anything:hasText ?text . - | - | ?text knora-api:textValueHasLanguage "fr" . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | + | ?thing anything:hasText ?text . + | + | ?text knora-api:textValueHasLanguage "fr" . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) @@ -7240,32 +7980,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | - | ?thing anything:hasText ?text . - | - | ?text knora-api:valueAsString "Bonjour" . - | - | ?text knora-api:textValueHasLanguage "fr" . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | + | ?thing anything:hasText ?text . + | + | ?text knora-api:valueAsString "Bonjour" . + | + | ?text knora-api:textValueHasLanguage "fr" . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LanguageFulltextSearch.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) checkSearchResponseNumberOfResults(responseAs[String], 1) @@ -7276,34 +8020,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?linkObj knora-api:isMainResource true . - | - | ?linkObj knora-api:hasLinkTo ?book . - | - |} WHERE { - | ?linkObj a knora-api:LinkObj . - | - | ?linkObj knora-api:hasLinkTo ?book . - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?linkObj knora-api:isMainResource true . + | + | ?linkObj knora-api:hasLinkTo ?book . + | + |} WHERE { + | ?linkObj a knora-api:LinkObj . + | + | ?linkObj knora-api:hasLinkTo ?book . + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/LinkObjectsToBooks.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/LinkObjectsToBooks.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7317,39 +8065,43 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 . - | - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | # testperson2 - | ?letter ?linkingProp1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { - - assert(status == StatusCodes.OK, response.toString) - - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithAuthor.jsonld"), - writeTestDataFiles) + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 . + | + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | # testperson2 + | ?letter ?linkingProp1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + + assert(status == StatusCodes.OK, response.toString) + + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithAuthor.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7362,44 +8114,48 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | ?person1 beol:hasFamilyName ?name . - | - | ?name knora-api:valueAsString "Meier" . - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | ?person1 beol:hasFamilyName ?name . + | + | ?name knora-api:valueAsString "Meier" . + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7412,43 +8168,48 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | ?person1 beol:hasFamilyName ?name . - | - | ?name knora-api:valueAsString "Muster" . - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | ?person1 beol:hasFamilyName ?name . + | + | ?name knora-api:valueAsString "Muster" . + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithPersonWithName2.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) @@ -7460,32 +8221,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "run a Gravsearch query that searches for a single resource specified by its IRI (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true ; - | anything:hasText ?text ; - | anything:hasInteger ?integer . - | - |} WHERE { - | BIND( AS ?thing) - | - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?thing anything:hasInteger ?integer . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true ; + | anything:hasText ?text ; + | anything:hasInteger ?integer . + | + |} WHERE { + | BIND( AS ?thing) + | + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?thing anything:hasInteger ?integer . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingByIriWithRequestedValues.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingByIriWithRequestedValues.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -7493,43 +8258,48 @@ class SearchRouteV2R2RSpec extends R2RSpec { "do a Gravsearch query for a letter and get information about the persons associated with it (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX beol: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?familyName . - | - | - | } WHERE { - | BIND( AS ?letter) - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | # testperson2 - | ?letter ?linkingProp1 ?person1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | ?person1 beol:hasFamilyName ?familyName . - | - | } ORDER BY ?date - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX beol: + |PREFIX knora-api: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?familyName . + | + | + | } WHERE { + | BIND( AS ?letter) + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | # testperson2 + | ?letter ?linkingProp1 ?person1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | ?person1 beol:hasFamilyName ?familyName . + | + | } ORDER BY ?date + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithAuthorWithInformation.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithAuthorWithInformation.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -7539,41 +8309,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | - | ?page knora-api:isPartOf ?book ; - | incunabula:seqnum ?seqnum . - | } WHERE { - | BIND( AS ?book) - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?page incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt ?seqnumInt . - | - | FILTER(?seqnumInt <= 10) - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | + | ?page knora-api:isPartOf ?book ; + | incunabula:seqnum ?seqnum . + | } WHERE { + | BIND( AS ?book) + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?page incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt ?seqnumInt . + | + | FILTER(?seqnumInt <= 10) + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/incomingPagesForBook.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/incomingPagesForBook.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -7582,34 +8356,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query containing a statement whose subject is not the main resource and whose object is used in ORDER BY (submitting the complex schema)" in { val gravsearchQuery = """PREFIX incunabula: - |PREFIX knora-api: - | - | CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | - | ?page knora-api:isPartOf ?book ; - | incunabula:seqnum ?seqnum . - | } WHERE { - | BIND( AS ?book) - | - | ?book incunabula:title ?title . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?page incunabula:seqnum ?seqnum . - | - | ?seqnum knora-api:intValueAsInt ?seqnumInt . - | - | FILTER(?seqnumInt <= 10) - | - | } ORDER BY ?seqnum - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + | + | CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | + | ?page knora-api:isPartOf ?book ; + | incunabula:seqnum ?seqnum . + | } WHERE { + | BIND( AS ?book) + | + | ?book incunabula:title ?title . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?page incunabula:seqnum ?seqnum . + | + | ?seqnum knora-api:intValueAsInt ?seqnumInt . + | + | FILTER(?seqnumInt <= 10) + | + | } ORDER BY ?seqnum + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -7620,45 +8396,49 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?region knora-api:isMainResource true . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book incunabula:title ?title . - | - |} WHERE { - | ?region a knora-api:Region . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?region knora-api:isMainResource true . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book incunabula:title ?title . + | + |} WHERE { + | ?region a knora-api:Region . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/regionsOfZeitgloecklein.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/regionsOfZeitgloecklein.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -7667,39 +8447,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query in the complex schema that uses knora-api:isMainResource in the simple schema" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - |PREFIX knora-api-simple: - | - |CONSTRUCT { - | ?region knora-api-simple:isMainResource true . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book incunabula:title ?title . - | - |} WHERE { - | ?region a knora-api:Region . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + |PREFIX knora-api-simple: + | + |CONSTRUCT { + | ?region knora-api-simple:isMainResource true . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book incunabula:title ?title . + | + |} WHERE { + | ?region a knora-api:Region . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -7709,39 +8491,41 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query in the complex schema that uses a Knora property in the simple schema" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX incunabula-simple: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?region knora-api:isMainResource true . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book incunabula:title ?title . - | - |} WHERE { - | ?region a knora-api:Region . - | - | ?region knora-api:isRegionOf ?page . - | - | ?page a incunabula:page . - | - | ?page knora-api:isPartOf ?book . - | - | ?book a incunabula:book . - | - | ?book incunabula-simple:title ?title . - | - | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX incunabula-simple: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?region knora-api:isMainResource true . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book incunabula:title ?title . + | + |} WHERE { + | ?region a knora-api:Region . + | + | ?region knora-api:isRegionOf ?page . + | + | ?page a incunabula:page . + | + | ?page knora-api:isPartOf ?book . + | + | ?book a incunabula:book . + | + | ?book incunabula-simple:title ?title . + | + | FILTER(?title = "Zeitglöcklein des Lebens und Leidens Christi") + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -7752,29 +8536,31 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query that uses a string literal in the CONSTRUCT clause" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - | - |} WHERE { - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + | + |} WHERE { + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString "Zeitglöcklein des Lebens und Leidens Christi" . + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -7784,31 +8570,33 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a Gravsearch query in the complex schema with a variable in the CONSTRUCT clause referring to a non-property entity that isn't a resource or value" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString ?titleStr . - | - | - |} WHERE { - | ?book a incunabula:book . - | - | ?book incunabula:title ?title . - | - | ?title knora-api:valueAsString ?titleStr . - | - | FILTER(?titleStr = "Zeitglöcklein des Lebens und Leidens Christi") - | - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString ?titleStr . + | + | + |} WHERE { + | ?book a incunabula:book . + | + | ?book incunabula:title ?title . + | + | ?title knora-api:valueAsString ?titleStr . + | + | FILTER(?titleStr = "Zeitglöcklein des Lebens und Leidens Christi") + | + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.BAD_REQUEST, response.toString) @@ -7818,31 +8606,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a list value that refers to a particular list node (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing anything:hasListItem ?listItem . - | - | ?listItem knora-api:listValueAsListNode . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing anything:hasListItem ?listItem . + | + | ?listItem knora-api:listValueAsListNode . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingReferringToSpecificListNode.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingReferringToSpecificListNode.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -7850,35 +8642,39 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a list value that does not refer to a particular list node (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing anything:hasListItem ?listItem . - | - | FILTER NOT EXISTS { - | - | ?listItem knora-api:listValueAsListNode . - | - | } - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing anything:hasListItem ?listItem . + | + | FILTER NOT EXISTS { + | + | ?listItem knora-api:listValueAsListNode . + | + | } + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingNotReferringToSpecificListNode.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingNotReferringToSpecificListNode.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -7886,28 +8682,30 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a list value that does not refer to a particular list node, performing a count query (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing anything:hasListItem ?listItem . - | - | FILTER NOT EXISTS { - | - | ?listItem knora-api:listValueAsListNode . - | - | } - | - | } - """.stripMargin - - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing anything:hasListItem ?listItem . + | + | FILTER NOT EXISTS { + | + | ?listItem knora-api:listValueAsListNode . + | + | } + | + | } + """.stripMargin + + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -7918,31 +8716,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a list value that refers to a particular list node that has subnodes (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - | CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasListItem ?listItem . - | - | } WHERE { - | ?thing anything:hasListItem ?listItem . - | - | ?listItem knora-api:listValueAsListNode . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + | CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasListItem ?listItem . + | + | } WHERE { + | ?thing anything:hasListItem ?listItem . + | + | ?listItem knora-api:listValueAsListNode . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingReferringToSpecificListNodeWithSubnodes.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingReferringToSpecificListNodeWithSubnodes.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -7950,32 +8752,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a beol:letter with list value that refers to a particular list node (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX beol: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:hasSubject ?subject . - | - | } WHERE { - | ?letter a beol:letter . - | - | ?letter beol:hasSubject ?subject . - | - | ?subject knora-api:listValueAsListNode . - | - | } - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX beol: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:hasSubject ?subject . + | + | } WHERE { + | ?letter a beol:letter . + | + | ?letter beol:hasSubject ?subject . + | + | ?subject knora-api:listValueAsListNode . + | + | } + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) - val expectedAnswerJSONLD = readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/letterWithSubject.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/letterWithSubject.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -7983,32 +8789,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a standoff link using the knora-api:standoffLink function (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffTag . - | ?standoffTag a knora-api:StandoffLinkTag . - | FILTER knora-api:standoffLink(?thing, ?standoffTag, ?otherThing) - | ?otherThing a anything:Thing . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffTag . + | ?standoffTag a knora-api:StandoffLinkTag . + | FILTER knora-api:standoffLink(?thing, ?standoffTag, ?otherThing) + | ?otherThing a anything:Thing . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingsWithStandoffLinks.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingsWithStandoffLinks.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -8016,33 +8826,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a standoff link using the knora-api:standoffLink function, referring to the target resource in the function call only (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffTag . - | ?standoffTag a knora-api:StandoffLinkTag . - | FILTER knora-api:standoffLink(?thing, ?standoffTag, ?otherThing) - | - | # Note that ?otherThing is only used as a argument in the function, not in any other statement - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffTag . + | ?standoffTag a knora-api:StandoffLinkTag . + | FILTER knora-api:standoffLink(?thing, ?standoffTag, ?otherThing) + | + | # Note that ?otherThing is only used as a argument in the function, not in any other statement + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingsWithStandoffLinks.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingsWithStandoffLinks.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -8050,32 +8864,36 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a standoff link using the knora-api:standoffLink function specifying an Iri for the target resource (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffTag . - | ?standoffTag a knora-api:StandoffLinkTag . - | FILTER knora-api:standoffLink(?thing, ?standoffTag, ) - | a anything:Thing . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffTag . + | ?standoffTag a knora-api:StandoffLinkTag . + | FILTER knora-api:standoffLink(?thing, ?standoffTag, ) + | a anything:Thing . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingsWithStandoffLinksToSpecificThing.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingsWithStandoffLinksToSpecificThing.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -8083,33 +8901,37 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for a standoff link using the knora-api:standoffLink function specifying an Iri for the target resource, referring to the target resource in the function call only (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffTag . - | ?standoffTag a knora-api:StandoffLinkTag . - | FILTER knora-api:standoffLink(?thing, ?standoffTag, ) - | - | # Note that is only used as a argument in the function, not in any other statement - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffTag . + | ?standoffTag a knora-api:StandoffLinkTag . + | FILTER knora-api:standoffLink(?thing, ?standoffTag, ) + | + | # Note that is only used as a argument in the function, not in any other statement + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/thingsWithStandoffLinksToSpecificThing.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/thingsWithStandoffLinksToSpecificThing.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } } @@ -8117,30 +8939,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for matching words in a particular type of standoff tag (submitting the complex schema)" in { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasRichtext ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasRichtext ?text . - | ?text knora-api:textValueHasStandoff ?standoffTag . - | ?standoffTag a standoff:StandoffItalicTag . - | FILTER knora-api:matchTextInStandoff(?text, ?standoffTag, "interesting text") - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasRichtext ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasRichtext ?text . + | ?text knora-api:textValueHasStandoff ?standoffTag . + | ?standoffTag a standoff:StandoffItalicTag . + | FILTER knora-api:matchTextInStandoff(?text, ?standoffTag, "interesting text") + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) val expectedAnswerJSONLD = - readOrWriteTextFile(responseAs[String], - Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + responseAs[String], + Paths.get("test_data/searchR2RV2/ThingWithRichtextWithTermTextInParagraph.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String]) } @@ -8154,17 +8981,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { val paramsCreateHTMLMappingFromXML = s""" - |{ - | "knora-api:mappingHasName": "HTMLMapping", - | "knora-api:attachedToProject": { - | "@id": "$anythingProjectIri" - | }, - | "rdfs:label": "mapping for HTML", - | "@context": { - | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", - | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" - | } - |} + |{ + | "knora-api:mappingHasName": "HTMLMapping", + | "knora-api:attachedToProject": { + | "@id": "$anythingProjectIri" + | }, + | "rdfs:label": "mapping for HTML", + | "@context": { + | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", + | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" + | } + |} """.stripMargin val formDataMapping = Multipart.FormData( @@ -8180,7 +9007,9 @@ class SearchRouteV2R2RSpec extends R2RSpec { ) // send mapping xml to route - Post("/v2/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v2/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { assert(status == StatusCodes.OK, responseAs[String]) @@ -8192,19 +9021,20 @@ class SearchRouteV2R2RSpec extends R2RSpec { val newValueParams = s""" - |{ - | "project_id": "http://rdfh.ch/projects/0001", - | "res_id": "http://rdfh.ch/0001/a-thing", - | "prop": "http://www.knora.org/ontology/0001/anything#hasText", - | "richtext_value": { - | "xml": ${JsString(FileUtil.readTextFile(xmlFileToSend))}, - | "mapping_id": "$anythingProjectIri/mappings/HTMLMapping" - | } - |} + |{ + | "project_id": "http://rdfh.ch/projects/0001", + | "res_id": "http://rdfh.ch/0001/a-thing", + | "prop": "http://www.knora.org/ontology/0001/anything#hasText", + | "richtext_value": { + | "xml": ${JsString(FileUtil.readTextFile(xmlFileToSend))}, + | "mapping_id": "$anythingProjectIri/mappings/HTMLMapping" + | } + |} """.stripMargin Post("/v1/values", HttpEntity(ContentTypes.`application/json`, newValueParams)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK) @@ -8214,24 +9044,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - |PREFIX knora-api-simple: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffEventTag . - | ?standoffEventTag a anything:StandoffEventTag . - | FILTER(knora-api:toSimpleDate(?standoffEventTag) = "GREGORIAN:2016-12 CE"^^knora-api-simple:Date) - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + |PREFIX knora-api-simple: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffEventTag . + | ?standoffEventTag a anything:StandoffEventTag . + | FILTER(knora-api:toSimpleDate(?standoffEventTag) = "GREGORIAN:2016-12 CE"^^knora-api-simple:Date) + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -8245,27 +9077,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - |PREFIX knora-api-simple: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffDateTag . - | ?standoffDateTag a knora-api:StandoffDateTag . - | FILTER(knora-api:toSimpleDate(?standoffDateTag) = "GREGORIAN:2016-12-24 CE"^^knora-api-simple:Date) - | ?standoffDateTag knora-api:standoffTagHasStartAncestor ?standoffParagraphTag . - | ?standoffParagraphTag a standoff:StandoffParagraphTag . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + |PREFIX knora-api-simple: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffDateTag . + | ?standoffDateTag a knora-api:StandoffDateTag . + | FILTER(knora-api:toSimpleDate(?standoffDateTag) = "GREGORIAN:2016-12-24 CE"^^knora-api-simple:Date) + | ?standoffDateTag knora-api:standoffTagHasStartAncestor ?standoffParagraphTag . + | ?standoffParagraphTag a standoff:StandoffParagraphTag . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) @@ -8278,23 +9112,25 @@ class SearchRouteV2R2RSpec extends R2RSpec { "reject a link value property in a query in the simple schema" in { val gravsearchQuery = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?title . - | ?page incunabula:partOfValue ?book . - |} WHERE { - | ?book a incunabula:book . - | ?book incunabula:title ?title . - | ?page a incunabula:page . - | ?page incunabula:partOfValue ?book . - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?title . + | ?page incunabula:partOfValue ?book . + |} WHERE { + | ?book a incunabula:book . + | ?book incunabula:title ?title . + | ?page a incunabula:page . + | ?page incunabula:partOfValue ?book . + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(incunabulaUserEmail, password)) ~> searchPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.NOT_FOUND, responseStr) @@ -8310,35 +9146,38 @@ class SearchRouteV2R2RSpec extends R2RSpec { val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "anything:hasRichtext" : { - | "@type" : "knora-api:TextValue", - | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(hamletXml)}, - | "knora-api:textValueHasMapping" : { - | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping" - | } - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "anything:hasRichtext" : { + | "@type" : "knora-api:TextValue", + | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(hamletXml)}, + | "knora-api:textValueHasMapping" : { + | "@id" : "http://rdfh.ch/standoff/mappings/StandardMapping" + | } + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin Post("/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcePath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcePath ~> check { val resourceCreateResponseStr = responseAs[String] assert(status == StatusCodes.OK, resourceCreateResponseStr) val resourceCreateResponseAsJsonLD: JsonLDDocument = JsonLDUtil.parseJsonLD(resourceCreateResponseStr) val resourceIri: IRI = - resourceCreateResponseAsJsonLD.body.requireStringWithValidation(JsonLDKeywords.ID, - stringFormatter.validateAndEscapeIri) + resourceCreateResponseAsJsonLD.body.requireStringWithValidation( + JsonLDKeywords.ID, + stringFormatter.validateAndEscapeIri + ) assert(resourceIri.toSmartIri.isKnoraDataIri) hamletResourceIri.set(resourceIri) } @@ -8349,20 +9188,22 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = s"""PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasRichtext ?text . - |} WHERE { - | BIND(<${hamletResourceIri.get}> AS ?thing) - | ?thing a anything:Thing . - | ?thing anything:hasRichtext ?text . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasRichtext ?text . + |} WHERE { + | BIND(<${hamletResourceIri.get}> AS ?thing) + | ?thing a anything:Thing . + | ?thing anything:hasRichtext ?text . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) @@ -8383,121 +9224,121 @@ class SearchRouteV2R2RSpec extends R2RSpec { val targetResource: String = """{ - | "@type" : "anything:BlueThing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "blue thing with incoming links", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val targetResourceIri: IRI = Post( - s"/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, targetResource)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcePath ~> check { - val createTargetResourceResponseStr = responseAs[String] - assert(response.status == StatusCodes.OK, createTargetResourceResponseStr) - val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) - responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) - } + | "@type" : "anything:BlueThing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "blue thing with incoming links", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val targetResourceIri: IRI = + Post(s"/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, targetResource)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcePath ~> check { + val createTargetResourceResponseStr = responseAs[String] + assert(response.status == StatusCodes.OK, createTargetResourceResponseStr) + val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) + responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) + } assert(targetResourceIri.toSmartIri.isKnoraDataIri) val sourceResource1: String = s"""{ - | "@type" : "anything:BlueThing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasBlueThingValue" : { - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "$targetResourceIri" - | } - | }, - | "rdfs:label" : "blue thing with link to other blue thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val sourceResource1Iri: IRI = Post( - s"/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, sourceResource1)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcePath ~> check { - val createSourceResource1ResponseStr = responseAs[String] - assert(response.status == StatusCodes.OK, createSourceResource1ResponseStr) - val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) - responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) - } + | "@type" : "anything:BlueThing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasBlueThingValue" : { + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "$targetResourceIri" + | } + | }, + | "rdfs:label" : "blue thing with link to other blue thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val sourceResource1Iri: IRI = + Post(s"/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, sourceResource1)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcePath ~> check { + val createSourceResource1ResponseStr = responseAs[String] + assert(response.status == StatusCodes.OK, createSourceResource1ResponseStr) + val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) + responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) + } assert(sourceResource1Iri.toSmartIri.isKnoraDataIri) val sourceResource2: String = s"""{ - | "@type" : "anything:Thing", - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "anything:hasOtherThingValue" : { - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "$targetResourceIri" - | } - | }, - | "rdfs:label" : "thing with link to blue thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val sourceResource2Iri: IRI = Post( - s"/v2/resources", - HttpEntity(RdfMediaTypes.`application/ld+json`, sourceResource2)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcePath ~> check { - val createSourceResource2ResponseStr = responseAs[String] - assert(response.status == StatusCodes.OK, createSourceResource2ResponseStr) - val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) - responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) - } + | "@type" : "anything:Thing", + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "anything:hasOtherThingValue" : { + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "$targetResourceIri" + | } + | }, + | "rdfs:label" : "thing with link to blue thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val sourceResource2Iri: IRI = + Post(s"/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, sourceResource2)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcePath ~> check { + val createSourceResource2ResponseStr = responseAs[String] + assert(response.status == StatusCodes.OK, createSourceResource2ResponseStr) + val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) + responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) + } assert(sourceResource2Iri.toSmartIri.isKnoraDataIri) val gravsearchQuery = s""" - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX anything: - | - |CONSTRUCT { - | ?targetThing knora-api:isMainResource true . - | ?firstIncoming anything:hasBlueThing ?targetThing . - | ?secondIncoming anything:hasOtherThing ?targetThing . - |} WHERE { - | ?targetThing a anything:BlueThing . - | ?firstIncoming anything:hasBlueThing ?targetThing . - | ?secondIncoming anything:hasOtherThing ?targetThing . - |} + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX anything: + | + |CONSTRUCT { + | ?targetThing knora-api:isMainResource true . + | ?firstIncoming anything:hasBlueThing ?targetThing . + | ?secondIncoming anything:hasOtherThing ?targetThing . + |} WHERE { + | ?targetThing a anything:BlueThing . + | ?firstIncoming anything:hasBlueThing ?targetThing . + | ?secondIncoming anything:hasOtherThing ?targetThing . + |} """.stripMargin val searchResultIri: IRI = Post( "/v2/searchextended", - HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -8510,26 +9351,30 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an anything:Thing with a time value (using the simple schema)" in { val gravsearchQuery = s""" - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasTimeStamp ?timeStamp . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasTimeStamp ?timeStamp . - | FILTER(?timeStamp > "2019-08-30T10:45:26.365863Z"^^xsd:dateTimeStamp) - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasTimeStamp ?timeStamp . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasTimeStamp ?timeStamp . + | FILTER(?timeStamp > "2019-08-30T10:45:26.365863Z"^^xsd:dateTimeStamp) + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ThingWithTimeStamp.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ThingWithTimeStamp.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8537,30 +9382,35 @@ class SearchRouteV2R2RSpec extends R2RSpec { "get a resource with a link to another resource that the user doesn't have permission to see" in { val gravsearchQuery = s"""PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?mainThing knora-api:isMainResource true . - | ?mainThing anything:hasOtherThing ?hiddenThing . - | ?hiddenThing anything:hasInteger ?intValInHiddenThing . - | ?mainThing anything:hasOtherThing ?visibleThing . - | ?visibleThing anything:hasInteger ?intValInVisibleThing . - |} WHERE { - | ?mainThing a anything:Thing . - | ?mainThing anything:hasOtherThing ?hiddenThing . - | ?hiddenThing anything:hasInteger ?intValInHiddenThing . - | ?intValInHiddenThing knora-api:intValueAsInt 123454321 . - | ?mainThing anything:hasOtherThing ?visibleThing . - | ?visibleThing anything:hasInteger ?intValInVisibleThing . - | ?intValInVisibleThing knora-api:intValueAsInt 543212345 . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX anything: + | + |CONSTRUCT { + | ?mainThing knora-api:isMainResource true . + | ?mainThing anything:hasOtherThing ?hiddenThing . + | ?hiddenThing anything:hasInteger ?intValInHiddenThing . + | ?mainThing anything:hasOtherThing ?visibleThing . + | ?visibleThing anything:hasInteger ?intValInVisibleThing . + |} WHERE { + | ?mainThing a anything:Thing . + | ?mainThing anything:hasOtherThing ?hiddenThing . + | ?hiddenThing anything:hasInteger ?intValInHiddenThing . + | ?intValInHiddenThing knora-api:intValueAsInt 123454321 . + | ?mainThing anything:hasOtherThing ?visibleThing . + | ?visibleThing anything:hasInteger ?intValInVisibleThing . + | ?intValInVisibleThing knora-api:intValueAsInt 543212345 . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ThingWithHiddenThing.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ThingWithHiddenThing.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8595,20 +9445,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { val expectedCount = 1 - Post("/v2/searchextended/count", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended/count", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) checkCountResponse(searchResponseStr, expectedCount) } - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) checkSearchResponseNumberOfResults(searchResponseStr, expectedCount) val expectedAnswerJSONLD = - readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ThingFromQueryWithUnion.jsonld"), - writeTestDataFiles) + readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ThingFromQueryWithUnion.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8644,12 +9502,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { |} |ORDER BY (?int)""".stripMargin - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.BAD_REQUEST, responseStr) assert( responseStr.contains( - "Variable ?int is used in ORDER by, but is not bound at the top level of the WHERE clause")) + "Variable ?int is used in ORDER by, but is not bound at the top level of the WHERE clause" + ) + ) } } @@ -8680,12 +9543,17 @@ class SearchRouteV2R2RSpec extends R2RSpec { |} |ORDER BY (?int)""".stripMargin - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.BAD_REQUEST, responseStr) assert( responseStr.contains( - "One or more variables used in a filter have not been bound in the same UNION block: ?richtext")) + "One or more variables used in a filter have not been bound in the same UNION block: ?richtext" + ) + ) } } @@ -8694,42 +9562,45 @@ class SearchRouteV2R2RSpec extends R2RSpec { val xmlStr = """ - | - |

The timestamp for this test is 27 January 2020.

- |
- |""".stripMargin + | + |

The timestamp for this test is 27 January 2020.

+ |
+ |""".stripMargin val jsonLDEntity = s"""{ - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@type" : "knora-api:TextValue", - | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(xmlStr)}, - | "knora-api:textValueHasMapping" : { - | "@id" : "$anythingProjectIri/mappings/HTMLMapping" - | } - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "thing with timestamp in markup", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@type" : "knora-api:TextValue", + | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(xmlStr)}, + | "knora-api:textValueHasMapping" : { + | "@id" : "$anythingProjectIri/mappings/HTMLMapping" + | } + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "thing with timestamp in markup", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin Post(s"/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> resourcePath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> resourcePath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) val resourceCreateResponseAsJsonLD: JsonLDDocument = JsonLDUtil.parseJsonLD(responseStr) val resourceIri: IRI = - resourceCreateResponseAsJsonLD.body.requireStringWithValidation(JsonLDKeywords.ID, - stringFormatter.validateAndEscapeIri) + resourceCreateResponseAsJsonLD.body.requireStringWithValidation( + JsonLDKeywords.ID, + stringFormatter.validateAndEscapeIri + ) assert(resourceIri.toSmartIri.isKnoraDataIri) timeTagResourceIri.set(resourceIri) } @@ -8738,24 +9609,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { val gravsearchQuery = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing anything:hasText ?text . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffTag . - | ?standoffTag a knora-api:StandoffTimeTag . - | ?standoffTag knora-api:timeValueAsTimeStamp ?timeStamp . - | FILTER(?timeStamp > "2020-01-27T08:31:51Z"^^xsd:dateTimeStamp && ?timeStamp < "2020-01-27T08:31:52Z"^^xsd:dateTimeStamp) - |} - """.stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing anything:hasText ?text . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffTag . + | ?standoffTag a knora-api:StandoffTimeTag . + | ?standoffTag knora-api:timeValueAsTimeStamp ?timeStamp . + | FILTER(?timeStamp > "2020-01-27T08:31:51Z"^^xsd:dateTimeStamp && ?timeStamp < "2020-01-27T08:31:52Z"^^xsd:dateTimeStamp) + |} + """.stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> searchPath ~> check { val responseStr = responseAs[String] assert(status == StatusCodes.OK, responseStr) @@ -8778,23 +9651,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using a literal in the simple schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8802,23 +9680,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using a literal in the complex schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8826,24 +9709,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using a variable in the simple schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?label . - | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?label . + | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8851,24 +9739,29 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using a variable in the complex schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?label . - | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?label . + | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8876,23 +9769,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using knora-api:matchLabel in the simple schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | FILTER knora-api:matchLabel(?book, "Zeitglöcklein") - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | FILTER knora-api:matchLabel(?book, "Zeitglöcklein") + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8900,23 +9798,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using knora-api:matchLabel in the complex schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | FILTER knora-api:matchLabel(?book, "Zeitglöcklein") - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | FILTER knora-api:matchLabel(?book, "Zeitglöcklein") + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8924,23 +9827,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using the regex function in the simple schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?bookLabel . - | FILTER regex(?bookLabel, "Zeit", "i") - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?bookLabel . + | FILTER regex(?bookLabel, "Zeit", "i") + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8948,23 +9856,28 @@ class SearchRouteV2R2RSpec extends R2RSpec { "search for an rdfs:label using the regex function in the complex schema" in { val gravsearchQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?bookLabel . - | FILTER regex(?bookLabel, "Zeit", "i") - |}""".stripMargin - - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?bookLabel . + | FILTER regex(?bookLabel, "Zeit", "i") + |}""".stripMargin + + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), - writeFile = false) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/ZeitgloeckleinViaLabel.jsonld"), + writeFile = false + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -8972,28 +9885,33 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a search that compares two variables representing resources (in the simple schema)" in { val gravsearchQuery: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person1 != ?person2) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person1 != ?person2) . + |} + |OFFSET 0""".stripMargin // We should get one result, not including ("letter to self"). - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) - val expectedAnswerJSONLD = readOrWriteTextFile(searchResponseStr, - Paths.get("test_data/searchR2RV2/LetterNotToSelf.jsonld"), - writeTestDataFiles) + val expectedAnswerJSONLD = readOrWriteTextFile( + searchResponseStr, + Paths.get("test_data/searchR2RV2/LetterNotToSelf.jsonld"), + writeTestDataFiles + ) compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = searchResponseStr) } } @@ -9001,23 +9919,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a search that compares two variables representing resources (in the complex schema)" in { val gravsearchQuery: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person1 != ?person2) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person1 != ?person2) . + |} + |OFFSET 0""".stripMargin // We should get one result, not including ("letter to self"). - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) val expectedAnswerJSONLD = @@ -9029,23 +9950,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a search that compares a variable with a resource IRI (in the simple schema)" in { val gravsearchQuery: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person1 != ) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person1 != ) . + |} + |OFFSET 0""".stripMargin // We should get one result, not including ("letter to self"). - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) val expectedAnswerJSONLD = @@ -9057,23 +9981,26 @@ class SearchRouteV2R2RSpec extends R2RSpec { "perform a search that compares a variable with a resource IRI (in the complex schema)" in { val gravsearchQuery: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person1 != ) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person1 != ) . + |} + |OFFSET 0""".stripMargin // We should get one result, not including ("letter to self"). - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> searchPath ~> check { val searchResponseStr = responseAs[String] assert(status == StatusCodes.OK, searchResponseStr) val expectedAnswerJSONLD = diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/StandoffRouteV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/StandoffRouteV2R2RSpec.scala index e7a4c7baee..b251828495 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/StandoffRouteV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/StandoffRouteV2R2RSpec.scala @@ -38,15 +38,15 @@ import org.knora.webapi.util.FileUtil import scala.concurrent.ExecutionContextExecutor /** - * End-to-end test specification for the search endpoint. This specification uses the Spray Testkit as documented - * here: http://spray.io/documentation/1.2.2/spray-testkit/ - */ + * End-to-end test specification for the search endpoint. This specification uses the Spray Testkit as documented + * here: http://spray.io/documentation/1.2.2/spray-testkit/ + */ class StandoffRouteV2R2RSpec extends R2RSpec { override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin private val standoffPath = new StandoffRouteV2(routeData).knoraApiPath @@ -93,17 +93,17 @@ class StandoffRouteV2R2RSpec extends R2RSpec { val mappingParams = s""" - |{ - | "knora-api:mappingHasName": "LetterMapping", - | "knora-api:attachedToProject": { - | "@id": "$ANYTHING_PROJECT_IRI" - | }, - | "rdfs:label": "letter mapping", - | "@context": { - | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", - | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" - | } - |} + |{ + | "knora-api:mappingHasName": "LetterMapping", + | "knora-api:attachedToProject": { + | "@id": "$ANYTHING_PROJECT_IRI" + | }, + | "rdfs:label": "letter mapping", + | "@context": { + | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", + | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" + | } + |} """.stripMargin val formDataMapping = Multipart.FormData( @@ -119,16 +119,22 @@ class StandoffRouteV2R2RSpec extends R2RSpec { ) // create standoff from XML - Post("/v2/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) ~> standoffPath ~> check { + Post("/v2/mapping", formDataMapping) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) ~> standoffPath ~> check { - assert(status == StatusCodes.OK, - "creation of a mapping returned a non successful HTTP status code: " + responseAs[String]) + assert( + status == StatusCodes.OK, + "creation of a mapping returned a non successful HTTP status code: " + responseAs[String] + ) val expectedAnswerJSONLD = FileUtil.readTextFile(Paths.get("test_data/standoffR2RV2/mappingCreationResponse.jsonld")) - compareJSONLDForMappingCreationResponse(expectedJSONLD = expectedAnswerJSONLD, - receivedJSONLD = responseAs[String]) + compareJSONLDForMappingCreationResponse( + expectedJSONLD = expectedAnswerJSONLD, + receivedJSONLD = responseAs[String] + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala index 8c8a885030..6e74674266 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala @@ -137,9 +137,11 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val stillImageFileValueUuid: IRI = "goZ7JFRNSeqF-dNxsqAS7Q" } - private def getResourceWithValues(resourceIri: IRI, - propertyIrisForGravsearch: Seq[SmartIri], - userEmail: String): JsonLDDocument = { + private def getResourceWithValues( + resourceIri: IRI, + propertyIrisForGravsearch: Seq[SmartIri], + userEmail: String + ): JsonLDDocument = { // Make a Gravsearch query from a template. val gravsearchQuery: String = org.knora.webapi.messages.twirl.queries.gravsearch.txt .getResourceWithSpecifiedProperties( @@ -150,64 +152,74 @@ class ValuesRouteV2E2ESpec extends E2ESpec { // Run the query. - val request = Post(baseApiUrl + "/v2/searchextended", - HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val request = Post( + baseApiUrl + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) responseToJsonLDDocument(response) } - private def getValuesFromResource(resource: JsonLDDocument, propertyIriInResult: SmartIri): JsonLDArray = { + private def getValuesFromResource(resource: JsonLDDocument, propertyIriInResult: SmartIri): JsonLDArray = resource.requireArray(propertyIriInResult.toString) - } - private def getValueFromResource(resource: JsonLDDocument, - propertyIriInResult: SmartIri, - expectedValueIri: IRI): JsonLDObject = { + private def getValueFromResource( + resource: JsonLDDocument, + propertyIriInResult: SmartIri, + expectedValueIri: IRI + ): JsonLDObject = { val resourceIri: IRI = resource.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) val propertyValues: JsonLDArray = getValuesFromResource(resource = resource, propertyIriInResult = propertyIriInResult) val matchingValues: Seq[JsonLDObject] = propertyValues.value.collect { case jsonLDObject: JsonLDObject - if jsonLDObject.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) == expectedValueIri => + if jsonLDObject.requireStringWithValidation( + JsonLDKeywords.ID, + stringFormatter.validateAndEscapeIri + ) == expectedValueIri => jsonLDObject } if (matchingValues.isEmpty) { throw AssertionException( - s"Property <$propertyIriInResult> of resource <$resourceIri> does not have value <$expectedValueIri>") + s"Property <$propertyIriInResult> of resource <$resourceIri> does not have value <$expectedValueIri>" + ) } if (matchingValues.size > 1) { throw AssertionException( - s"Property <$propertyIriInResult> of resource <$resourceIri> has more than one value with the IRI <$expectedValueIri>") + s"Property <$propertyIriInResult> of resource <$resourceIri> has more than one value with the IRI <$expectedValueIri>" + ) } matchingValues.head } - private def parseResourceLastModificationDate(resource: JsonLDDocument): Option[Instant] = { + private def parseResourceLastModificationDate(resource: JsonLDDocument): Option[Instant] = resource.maybeObject(OntologyConstants.KnoraApiV2Complex.LastModificationDate).map { jsonLDObject => jsonLDObject.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.validateAndEscapeIri) should ===( - OntologyConstants.Xsd.DateTimeStamp) + OntologyConstants.Xsd.DateTimeStamp + ) jsonLDObject.requireStringWithValidation(JsonLDKeywords.VALUE, stringFormatter.xsdDateTimeStampToInstant) } - } private def getResourceLastModificationDate(resourceIri: IRI, userEmail: String): Option[Instant] = { - val request = Get(baseApiUrl + s"/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}") ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val request = Get( + baseApiUrl + s"/v2/resourcespreview/${URLEncoder.encode(resourceIri, "UTF-8")}" + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val resource: JsonLDDocument = responseToJsonLDDocument(response) parseResourceLastModificationDate(resource) } - private def checkLastModDate(resourceIri: IRI, - maybePreviousLastModDate: Option[Instant], - maybeUpdatedLastModDate: Option[Instant]): Unit = { + private def checkLastModDate( + resourceIri: IRI, + maybePreviousLastModDate: Option[Instant], + maybeUpdatedLastModDate: Option[Instant] + ): Unit = maybeUpdatedLastModDate match { case Some(updatedLastModDate) => maybePreviousLastModDate match { @@ -217,14 +229,15 @@ class ValuesRouteV2E2ESpec extends E2ESpec { case None => throw AssertionException(s"Resource $resourceIri has no knora-api:lastModificationDate") } - } - private def getValue(resourceIri: IRI, - maybePreviousLastModDate: Option[Instant], - propertyIriForGravsearch: SmartIri, - propertyIriInResult: SmartIri, - expectedValueIri: IRI, - userEmail: String): JsonLDObject = { + private def getValue( + resourceIri: IRI, + maybePreviousLastModDate: Option[Instant], + propertyIriForGravsearch: SmartIri, + propertyIriInResult: SmartIri, + expectedValueIri: IRI, + userEmail: String + ): JsonLDObject = { val resource: JsonLDDocument = getResourceWithValues( resourceIri = resourceIri, propertyIrisForGravsearch = Seq(propertyIriForGravsearch), @@ -252,236 +265,330 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) } - private def createTextValueWithoutStandoffRequest(resourceIri: IRI, valueAsString: String): String = { + private def createTextValueWithoutStandoffRequest(resourceIri: IRI, valueAsString: String): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@type" : "knora-api:TextValue", - | "knora-api:valueAsString" : "$valueAsString" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@type" : "knora-api:TextValue", + | "knora-api:valueAsString" : "$valueAsString" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin private val textValue1AsXmlWithStandardMapping: String = """ - | - | This text links to another resource. - | And this strong value is linked by this link - |""".stripMargin + | + | This text links to another resource. + | And this strong value is linked by this link + |""".stripMargin private val standardMappingIri: IRI = "http://rdfh.ch/standoff/mappings/StandardMapping" private val geometryValue1 = """{"status":"active","lineColor":"#ff3333","lineWidth":2,"points":[{"x":0.08098591549295775,"y":0.16741071428571427},{"x":0.7394366197183099,"y":0.7299107142857143}],"type":"rectangle","original_index":0}""" - private def createTextValueWithStandoffRequest(resourceIri: IRI, textValueAsXml: String, mappingIri: String)( - implicit stringFormatter: StringFormatter): String = { + private def createTextValueWithStandoffRequest(resourceIri: IRI, textValueAsXml: String, mappingIri: String)(implicit + stringFormatter: StringFormatter + ): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@type" : "knora-api:TextValue", - | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(textValueAsXml)}, - | "knora-api:textValueHasMapping" : { - | "@id": "$mappingIri" - | } - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - - private def createDateValueWithDayPrecisionRequest(resourceIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartMonth: Int, - dateValueHasStartDay: Int, - dateValueHasStartEra: String, - dateValueHasEndYear: Int, - dateValueHasEndMonth: Int, - dateValueHasEndDay: Int, - dateValueHasEndEra: String): String = { + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@type" : "knora-api:TextValue", + | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString(textValueAsXml)}, + | "knora-api:textValueHasMapping" : { + | "@id": "$mappingIri" + | } + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def createDateValueWithDayPrecisionRequest( + resourceIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartMonth: Int, + dateValueHasStartDay: Int, + dateValueHasStartEra: String, + dateValueHasEndYear: Int, + dateValueHasEndMonth: Int, + dateValueHasEndDay: Int, + dateValueHasEndEra: String + ): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasDate" : { - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, - | "knora-api:dateValueHasStartDay" : $dateValueHasStartDay, - | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, - | "knora-api:dateValueHasEndDay" : $dateValueHasEndDay, - | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - - private def createIslamicDateValueWithDayPrecisionRequest(resourceIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartMonth: Int, - dateValueHasStartDay: Int, - dateValueHasEndYear: Int, - dateValueHasEndMonth: Int, - dateValueHasEndDay: Int): String = { + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, + | "knora-api:dateValueHasStartDay" : $dateValueHasStartDay, + | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, + | "knora-api:dateValueHasEndDay" : $dateValueHasEndDay, + | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def createIslamicDateValueWithDayPrecisionRequest( + resourceIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartMonth: Int, + dateValueHasStartDay: Int, + dateValueHasEndYear: Int, + dateValueHasEndMonth: Int, + dateValueHasEndDay: Int + ): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasDate" : { - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, - | "knora-api:dateValueHasStartDay" : $dateValueHasStartDay, - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, - | "knora-api:dateValueHasEndDay" : $dateValueHasEndDay - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - - private def createDateValueWithMonthPrecisionRequest(resourceIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartMonth: Int, - dateValueHasStartEra: String, - dateValueHasEndYear: Int, - dateValueHasEndMonth: Int, - dateValueHasEndEra: String): String = { + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, + | "knora-api:dateValueHasStartDay" : $dateValueHasStartDay, + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, + | "knora-api:dateValueHasEndDay" : $dateValueHasEndDay + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def createDateValueWithMonthPrecisionRequest( + resourceIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartMonth: Int, + dateValueHasStartEra: String, + dateValueHasEndYear: Int, + dateValueHasEndMonth: Int, + dateValueHasEndEra: String + ): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasDate" : { - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, - | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, - | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - - private def createDateValueWithYearPrecisionRequest(resourceIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartEra: String, - dateValueHasEndYear: Int, - dateValueHasEndEra: String): String = { + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, + | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, + | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def createDateValueWithYearPrecisionRequest( + resourceIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartEra: String, + dateValueHasEndYear: Int, + dateValueHasEndEra: String + ): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasDate" : { - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - - private def updateTextValueWithoutStandoffRequest(resourceIri: IRI, valueIri: IRI, valueAsString: String): String = { + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def updateTextValueWithoutStandoffRequest(resourceIri: IRI, valueIri: IRI, valueAsString: String): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@id" : "$valueIri", - | "@type" : "knora-api:TextValue", - | "knora-api:valueAsString" : "$valueAsString" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:TextValue", + | "knora-api:valueAsString" : "$valueAsString" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin private val textValue2AsXmlWithStandardMapping: String = """ - | - | This updated text links to another resource. - |""".stripMargin - - private def updateTextValueWithCommentRequest(resourceIri: IRI, - valueIri: IRI, - valueAsString: String, - valueHasComment: String): String = { + | + | This updated text links to another resource. + |""".stripMargin + + private def updateTextValueWithCommentRequest( + resourceIri: IRI, + valueIri: IRI, + valueAsString: String, + valueHasComment: String + ): String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@id" : "$valueIri", - | "@type" : "knora-api:TextValue", - | "knora-api:valueAsString" : "$valueAsString", - | "knora-api:valueHasComment" : "$valueHasComment" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - - private def updateDateValueWithDayPrecisionRequest(resourceIri: IRI, - valueIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartMonth: Int, - dateValueHasStartDay: Int, - dateValueHasStartEra: String, - dateValueHasEndYear: Int, - dateValueHasEndMonth: Int, - dateValueHasEndDay: Int, - dateValueHasEndEra: String): String = { + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:TextValue", + | "knora-api:valueAsString" : "$valueAsString", + | "knora-api:valueHasComment" : "$valueHasComment" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def updateDateValueWithDayPrecisionRequest( + resourceIri: IRI, + valueIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartMonth: Int, + dateValueHasStartDay: Int, + dateValueHasStartEra: String, + dateValueHasEndYear: Int, + dateValueHasEndMonth: Int, + dateValueHasEndDay: Int, + dateValueHasEndEra: String + ): String = + s"""{ + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, + | "knora-api:dateValueHasStartDay" : $dateValueHasStartDay, + | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, + | "knora-api:dateValueHasEndDay" : $dateValueHasEndDay, + | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def updateDateValueWithMonthPrecisionRequest( + resourceIri: IRI, + valueIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartMonth: Int, + dateValueHasStartEra: String, + dateValueHasEndYear: Int, + dateValueHasEndMonth: Int, + dateValueHasEndEra: String + ): String = + s"""{ + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, + | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, + | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private def updateDateValueWithYearPrecisionRequest( + resourceIri: IRI, + valueIri: IRI, + dateValueHasCalendar: String, + dateValueHasStartYear: Int, + dateValueHasStartEra: String, + dateValueHasEndYear: Int, + dateValueHasEndEra: String + ): String = s"""{ + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDate" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:DateValue", + | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", + | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, + | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", + | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, + | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + private val geometryValue2 = + """{"status":"active","lineColor":"#ff3344","lineWidth":2,"points":[{"x":0.08098591549295775,"y":0.16741071428571427},{"x":0.7394366197183099,"y":0.7299107142857143}],"type":"rectangle","original_index":0}""" + + private def updateLinkValueRequest( + resourceIri: IRI, + valueIri: IRI, + targetResourceIri: IRI, + comment: Option[String] = None + ): String = + comment match { + case Some(definedComment) => + s"""{ | "@id" : "$resourceIri", | "@type" : "anything:Thing", - | "anything:hasDate" : { + | "anything:hasOtherThingValue" : { | "@id" : "$valueIri", - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, - | "knora-api:dateValueHasStartDay" : $dateValueHasStartDay, - | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, - | "knora-api:dateValueHasEndDay" : $dateValueHasEndDay, - | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "$targetResourceIri" + | }, + | "knora-api:valueHasComment" : "$definedComment" | }, | "@context" : { | "xsd" : "http://www.w3.org/2001/XMLSchema#", @@ -489,30 +596,17 @@ class ValuesRouteV2E2ESpec extends E2ESpec { | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" | } |}""".stripMargin - } - private def updateDateValueWithMonthPrecisionRequest(resourceIri: IRI, - valueIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartMonth: Int, - dateValueHasStartEra: String, - dateValueHasEndYear: Int, - dateValueHasEndMonth: Int, - dateValueHasEndEra: String): String = { - s"""{ + case None => + s"""{ | "@id" : "$resourceIri", | "@type" : "anything:Thing", - | "anything:hasDate" : { + | "anything:hasOtherThingValue" : { | "@id" : "$valueIri", - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartMonth" : $dateValueHasStartMonth, - | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndMonth" : $dateValueHasEndMonth, - | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "$targetResourceIri" + | } | }, | "@context" : { | "xsd" : "http://www.w3.org/2001/XMLSchema#", @@ -520,150 +614,77 @@ class ValuesRouteV2E2ESpec extends E2ESpec { | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" | } |}""".stripMargin - } + } - private def updateDateValueWithYearPrecisionRequest(resourceIri: IRI, - valueIri: IRI, - dateValueHasCalendar: String, - dateValueHasStartYear: Int, - dateValueHasStartEra: String, - dateValueHasEndYear: Int, - dateValueHasEndEra: String): String = { - s"""{ + private def deleteIntValueRequest(resourceIri: IRI, valueIri: IRI, maybeDeleteComment: Option[String]): String = + maybeDeleteComment match { + case Some(deleteComment) => + s"""{ | "@id" : "$resourceIri", | "@type" : "anything:Thing", - | "anything:hasDate" : { + | "anything:hasInteger" : { | "@id" : "$valueIri", - | "@type" : "knora-api:DateValue", - | "knora-api:dateValueHasCalendar" : "$dateValueHasCalendar", - | "knora-api:dateValueHasStartYear" : $dateValueHasStartYear, - | "knora-api:dateValueHasStartEra" : "$dateValueHasStartEra", - | "knora-api:dateValueHasEndYear" : $dateValueHasEndYear, - | "knora-api:dateValueHasEndEra" : "$dateValueHasEndEra" + | "@type" : "knora-api:IntValue", + | "knora-api:deleteComment" : "$deleteComment" | }, | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" | } |}""".stripMargin - } - - private val geometryValue2 = - """{"status":"active","lineColor":"#ff3344","lineWidth":2,"points":[{"x":0.08098591549295775,"y":0.16741071428571427},{"x":0.7394366197183099,"y":0.7299107142857143}],"type":"rectangle","original_index":0}""" - - private def updateLinkValueRequest(resourceIri: IRI, - valueIri: IRI, - targetResourceIri: IRI, - comment: Option[String] = None): String = { - comment match { - case Some(definedComment) => - s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasOtherThingValue" : { - | "@id" : "$valueIri", - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "$targetResourceIri" - | }, - | "knora-api:valueHasComment" : "$definedComment" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - case None => - s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasOtherThingValue" : { - | "@id" : "$valueIri", - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "$targetResourceIri" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - } - - private def deleteIntValueRequest(resourceIri: IRI, valueIri: IRI, maybeDeleteComment: Option[String]): String = { - maybeDeleteComment match { - case Some(deleteComment) => - s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "$valueIri", - | "@type" : "knora-api:IntValue", - | "knora-api:deleteComment" : "$deleteComment" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin case None => s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "$valueIri", - | "@type" : "knora-api:IntValue" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - } - } - - private def updateIntValueWithCustomNewValueVersionIriRequest(resourceIri: IRI, - valueIri: IRI, - intValue: Int, - newValueVersionIri: IRI): String = { - s"""{ | "@id" : "$resourceIri", | "@type" : "anything:Thing", | "anything:hasInteger" : { | "@id" : "$valueIri", - | "@type" : "knora-api:IntValue", - | "knora-api:newValueVersionIri" : { - | "@id" : "$newValueVersionIri" - | }, - | "knora-api:intValueAsInt" : $intValue + | "@type" : "knora-api:IntValue" | }, | "@context" : { | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" | } |}""".stripMargin - } + } + + private def updateIntValueWithCustomNewValueVersionIriRequest( + resourceIri: IRI, + valueIri: IRI, + intValue: Int, + newValueVersionIri: IRI + ): String = + s"""{ + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:IntValue", + | "knora-api:newValueVersionIri" : { + | "@id" : "$newValueVersionIri" + | }, + | "knora-api:intValueAsInt" : $intValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin /** - * Gets a value from a resource by UUID, compares the response to the expected response, and - * adds the response to the client test data. - * - * @param resourceIri the resource IRI. - * @param valueUuid the value UUID. - * @param fileBasename the basename of the test data file. - */ + * Gets a value from a resource by UUID, compares the response to the expected response, and + * adds the response to the client test data. + * + * @param resourceIri the resource IRI. + * @param valueUuid the value UUID. + * @param fileBasename the basename of the test data file. + */ private def testValue(resourceIri: IRI, valueUuid: String, fileBasename: String): Unit = { val resourceIriEncoded = URLEncoder.encode(resourceIri, "UTF-8") val request = Get(s"$baseApiUrl/v2/values/$resourceIriEncoded/$valueUuid") ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.anythingUser1.email, SharedTestDataADM.testPass)) + BasicHttpCredentials(SharedTestDataADM.anythingUser1.email, SharedTestDataADM.testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseStr = responseToString(response) assert(response.status == StatusCodes.OK, responseStr) @@ -705,13 +726,12 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "link-value" -> TestDing.linkValueUuid ) - testDingValues.foreach { - case (valueTypeName, valueUuid) => - testValue( - resourceIri = TestDing.iri, - valueUuid = valueUuid, - fileBasename = s"get-$valueTypeName-response" - ) + testDingValues.foreach { case (valueTypeName, valueUuid) => + testValue( + resourceIri = TestDing.iri, + valueUuid = valueUuid, + fileBasename = s"get-$valueTypeName-response" + ) } testValue( @@ -727,7 +747,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val timestamp = "20190212T090510Z" val request = Get(baseApiUrl + s"/v2/values/$resourceIri/$valueUuid?version=$timestamp") ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -750,17 +771,17 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -773,8 +794,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) val responseStr: String = responseToString(response) assert(response.status == StatusCodes.OK, responseStr) @@ -785,9 +808,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri) - integerValueUUID = - responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, - stringFormatter.validateBase64EncodedUuid) + integerValueUUID = responseJsonDoc.body.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasUUID, + stringFormatter.validateBase64EncodedUuid + ) val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, @@ -819,19 +843,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "$customValueIri", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "$customValueIri", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -844,8 +868,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -861,22 +887,24 @@ class ValuesRouteV2E2ESpec extends E2ESpec { // duplicate value IRI val params = s"""{ - | "@id" : "${AThing.iri}", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "$customValueIri", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : 43 - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin - - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "${AThing.iri}", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "$customValueIri", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : 43 + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + val request = + Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) @@ -892,19 +920,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue, - | "knora-api:valueHasUUID" : "$intValueCustomUUID" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:valueHasUUID" : "$intValueCustomUUID" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -917,8 +945,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) @@ -964,8 +994,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) @@ -978,22 +1010,22 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue, - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$customCreationDate" - | } - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$customCreationDate" + | } + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1006,8 +1038,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1034,24 +1068,24 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "$customValueIri", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue, - | "knora-api:valueHasUUID" : "$customValueUUID", - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$customCreationDate" - | } - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "$customValueIri", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:valueHasUUID" : "$customValueUUID", + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$customCreationDate" + | } + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1064,8 +1098,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1090,17 +1126,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : $intValue, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" - | } - |}""".stripMargin - - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : $intValue, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" + | } + |}""".stripMargin + + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -1115,18 +1153,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue, - | "knora-api:hasPermissions" : "$customPermissions" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:hasPermissions" : "$customPermissions" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1139,8 +1177,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1188,8 +1228,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1223,8 +1265,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { valueAsString = valueAsString ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) } @@ -1239,8 +1283,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { valueAsString = valueAsString ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) } @@ -1268,8 +1314,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1317,8 +1365,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1353,8 +1403,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { valueHasComment = "Adding a comment" ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) } @@ -1372,8 +1424,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { valueHasComment = "Updated comment" ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1406,18 +1460,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@type" : "knora-api:TextValue", - | "knora-api:valueAsString" : "$valueAsString", - | "knora-api:valueHasComment" : "$valueHasComment" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@type" : "knora-api:TextValue", + | "knora-api:valueAsString" : "$valueAsString", + | "knora-api:valueHasComment" : "$valueHasComment" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -1430,8 +1484,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1479,8 +1535,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) val responseStr = responseToString(response) assert(response.status == StatusCodes.OK, responseStr) @@ -1516,430 +1574,430 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val textValueAsXml: String = """ - | - |

This ref is a link to an out of page tag.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

Many lines to force to create a page.

- |

This strong value is linked by an out of page anchor link at the top.

- |
+ | + |

This ref is a link to an out of page tag.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

Many lines to force to create a page.

+ |

This strong value is linked by an out of page anchor link at the top.

+ |
""".stripMargin val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri @@ -1951,8 +2009,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { mappingIri = standardMappingIri ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -1985,9 +2045,9 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val textValueAsXml: String = """ - | - | This text links to a web site. - | + | + | This text links to a web site. + | """.stripMargin val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri @@ -1999,8 +2059,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { mappingIri = standardMappingIri ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2027,8 +2089,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val resourceIri = AThing.iri val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail) val jsonLDEntity = FileUtil.readTextFile(Paths.get("test_data/valuesE2EV2/CreateValueWithEscape.jsonld")) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2050,7 +2114,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val expectedText = """

- | test

""".stripMargin + | test

""".stripMargin assert(savedTextValueAsXml.contains(expectedText)) } @@ -2062,16 +2126,16 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val mappingParams = s"""{ - | "knora-api:mappingHasName": "HTMLMapping", - | "knora-api:attachedToProject": { - | "@id": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}" - | }, - | "rdfs:label": "HTML mapping", - | "@context": { - | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", - | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" - | } - |}""".stripMargin + | "knora-api:mappingHasName": "HTMLMapping", + | "knora-api:attachedToProject": { + | "@id": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}" + | }, + | "rdfs:label": "HTML mapping", + | "@context": { + | "rdfs": "${OntologyConstants.Rdfs.RdfsPrefixExpansion}", + | "knora-api": "${OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion}" + | } + |}""".stripMargin val formDataMapping = Multipart.FormData( Multipart.FormData.BodyPart( @@ -2087,8 +2151,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { // create standoff from XML val mappingRequest = Post(baseApiUrl + "/v2/mapping", formDataMapping) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + BasicHttpCredentials(anythingUserEmail, password) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val mappingResponse: HttpResponse = singleAwaitingRequest(mappingRequest) assert(mappingResponse.status == StatusCodes.OK, mappingResponse.toString) @@ -2100,9 +2164,9 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val textValueAsXml = """ - | - |

This an event.

- |
""".stripMargin + | + |

This an event.

+ |
""".stripMargin val jsonLDEntity = createTextValueWithStandoffRequest( resourceIri = resourceIri, @@ -2110,8 +2174,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { mappingIri = s"${SharedTestDataADM.ANYTHING_PROJECT_IRI}/mappings/HTMLMapping" ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2141,8 +2207,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { valueAsString = valueAsString ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) } @@ -2155,21 +2223,21 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasDecimal" : { - | "@type" : "knora-api:DecimalValue", - | "knora-api:decimalValueAsDecimal" : { - | "@type" : "xsd:decimal", - | "@value" : "$decimalValueAsDecimal" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDecimal" : { + | "@type" : "knora-api:DecimalValue", + | "knora-api:decimalValueAsDecimal" : { + | "@type" : "xsd:decimal", + | "@value" : "$decimalValueAsDecimal" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -2182,8 +2250,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2250,8 +2320,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2272,15 +2344,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "GREGORIAN:2018-10-05 CE:2018-10-06 CE") + "GREGORIAN:2018-10-05 CE:2018-10-06 CE" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(dateValueHasStartDay) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasEndMonth) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(dateValueHasEndDay) @@ -2321,8 +2397,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2343,15 +2421,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "GREGORIAN:2018-10 CE:2018-11 CE") + "GREGORIAN:2018-10 CE:2018-11 CE" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasEndMonth) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -2388,8 +2470,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2410,14 +2494,17 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "GREGORIAN:2018 CE:2019 CE") + "GREGORIAN:2018 CE:2019 CE" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -2447,8 +2534,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndEra = dateValueHasStartEra ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2470,13 +2559,16 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("GREGORIAN:2018-10-05 CE") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(dateValueHasStartDay) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasStartMonth) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(dateValueHasStartDay) @@ -2503,8 +2595,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndEra = dateValueHasStartEra ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2526,13 +2620,16 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("GREGORIAN:2018-10 CE") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasStartMonth) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -2556,8 +2653,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndEra = dateValueHasStartEra ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2579,12 +2678,14 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("GREGORIAN:2018 CE") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -2611,8 +2712,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndDay = dateValueHasStartDay ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2634,10 +2737,12 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("ISLAMIC:1407-01-26") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(dateValueHasStartDay) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasStartMonth) @@ -2667,8 +2772,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndDay = dateValueHasEndDay ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2689,12 +2796,15 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "ISLAMIC:1407-01-15:1407-01-26") + "ISLAMIC:1407-01-15:1407-01-26" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(dateValueHasStartDay) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasEndMonth) @@ -2709,17 +2819,17 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasBoolean" : { - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : $booleanValue - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasBoolean" : { + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : $booleanValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -2732,8 +2842,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2765,17 +2877,17 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasGeometry" : { - | "@type" : "knora-api:GeomValue", - | "knora-api:geometryValueAsGeometry" : ${stringFormatter.toJsonEncodedString(geometryValue1)} - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasGeometry" : { + | "@type" : "knora-api:GeomValue", + | "knora-api:geometryValueAsGeometry" : ${stringFormatter.toJsonEncodedString(geometryValue1)} + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -2788,8 +2900,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2823,25 +2937,25 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInterval" : { - | "@type" : "knora-api:IntervalValue", - | "knora-api:intervalValueHasStart" : { - | "@type" : "xsd:decimal", - | "@value" : "$intervalStart" - | }, - | "knora-api:intervalValueHasEnd" : { - | "@type" : "xsd:decimal", - | "@value" : "$intervalEnd" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInterval" : { + | "@type" : "knora-api:IntervalValue", + | "knora-api:intervalValueHasStart" : { + | "@type" : "xsd:decimal", + | "@value" : "$intervalStart" + | }, + | "knora-api:intervalValueHasEnd" : { + | "@type" : "xsd:decimal", + | "@value" : "$intervalEnd" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -2854,8 +2968,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2900,21 +3016,21 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasTimeStamp" : { - | "@type" : "knora-api:TimeValue", - | "knora-api:timeValueAsTimeStamp" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$timeStamp" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasTimeStamp" : { + | "@type" : "knora-api:TimeValue", + | "knora-api:timeValueAsTimeStamp" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$timeStamp" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -2927,8 +3043,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -2965,20 +3083,20 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasListItem" : { - | "@type" : "knora-api:ListValue", - | "knora-api:listValueAsListNode" : { - | "@id" : "$listNode" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasListItem" : { + | "@type" : "knora-api:ListValue", + | "knora-api:listValueAsListNode" : { + | "@id" : "$listNode" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -2991,8 +3109,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3013,8 +3133,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) val savedListValueHasListNode: IRI = - savedValue.requireIriInObject(OntologyConstants.KnoraApiV2Complex.ListValueAsListNode, - stringFormatter.validateAndEscapeIri) + savedValue.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.ListValueAsListNode, + stringFormatter.validateAndEscapeIri + ) savedListValueHasListNode should ===(listNode) } @@ -3026,18 +3148,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasColor" : { - | "@type" : "knora-api:ColorValue", - | "knora-api:colorValueAsColor" : "$color" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasColor" : { + | "@type" : "knora-api:ColorValue", + | "knora-api:colorValueAsColor" : "$color" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3050,8 +3172,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3083,21 +3207,21 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasUri" : { - | "@type" : "knora-api:UriValue", - | "knora-api:uriValueAsUri" : { - | "@type" : "xsd:anyURI", - | "@value" : "$uri" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasUri" : { + | "@type" : "knora-api:UriValue", + | "knora-api:uriValueAsUri" : { + | "@type" : "xsd:anyURI", + | "@value" : "$uri" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3110,8 +3234,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3148,18 +3274,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasGeoname" : { - | "@type" : "knora-api:GeonameValue", - | "knora-api:geonameValueAsGeonameCode" : "$geonameCode" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasGeoname" : { + | "@type" : "knora-api:GeonameValue", + | "knora-api:geonameValueAsGeonameCode" : "$geonameCode" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3172,8 +3298,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3206,20 +3334,20 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity: String = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasOtherThingValue" : { - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "${TestDing.iri}" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasOtherThingValue" : { + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "${TestDing.iri}" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3232,8 +3360,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3244,8 +3374,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri) - linkValueUUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, - stringFormatter.validateBase64EncodedUuid) + linkValueUUID = responseJsonDoc.body.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasUUID, + stringFormatter.validateBase64EncodedUuid + ) val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, @@ -3270,26 +3402,26 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasOtherThingValue" : { - | "@id" : "$customValueIri", - | "@type" : "knora-api:LinkValue", - | "knora-api:valueHasUUID": "$customValueUUID", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "$targetResourceIri" - | }, - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$customCreationDate" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasOtherThingValue" : { + | "@id" : "$customValueIri", + | "@type" : "knora-api:LinkValue", + | "knora-api:valueHasUUID": "$customValueUUID", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "$targetResourceIri" + | }, + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$customCreationDate" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3302,8 +3434,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3330,18 +3464,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueIri.get}", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueIri.get}", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3354,8 +3488,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseStr: String = responseToString(response) assert(response.status == StatusCodes.OK, responseStr) @@ -3367,8 +3503,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri) val newIntegerValueUUID: UUID = - responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, - stringFormatter.validateBase64EncodedUuid) + responseJsonDoc.body.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasUUID, + stringFormatter.validateBase64EncodedUuid + ) assert(newIntegerValueUUID == integerValueUUID) // The new version should have the same UUID. val savedValue: JsonLDObject = getValue( @@ -3404,23 +3542,23 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueForRsyncIri.get}", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue, - | "knora-api:valueCreationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$valueCreationDate" - | } - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueForRsyncIri.get}", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:valueCreationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$valueCreationDate" + | } + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3433,8 +3571,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3480,8 +3620,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { newValueVersionIri = newValueVersionIri ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) @@ -3516,8 +3658,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { newValueVersionIri = newValueVersionIri ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -3535,8 +3679,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { newValueVersionIri = newValueVersionIri ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -3554,8 +3700,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { newValueVersionIri = newValueVersionIri ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -3573,8 +3721,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { newValueVersionIri = newValueVersionIri ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -3586,21 +3736,23 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueIri.get}", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" - | } - |}""".stripMargin - - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueIri.get}", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" + | } + |}""".stripMargin + + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -3615,19 +3767,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueWithCustomPermissionsIri.get}", - | "@type" : "knora-api:IntValue", - | "knora-api:intValueAsInt" : $intValue, - | "knora-api:hasPermissions" : "$customPermissions" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueWithCustomPermissionsIri.get}", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:hasPermissions" : "$customPermissions" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3640,8 +3792,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3675,18 +3829,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueIri.get}", - | "@type" : "knora-api:IntValue", - | "knora-api:hasPermissions" : "$customPermissions" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueIri.get}", + | "@type" : "knora-api:IntValue", + | "knora-api:hasPermissions" : "$customPermissions" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3699,8 +3853,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3732,22 +3888,22 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasDecimal" : { - | "@id" : "${decimalValueIri.get}", - | "@type" : "knora-api:DecimalValue", - | "knora-api:decimalValueAsDecimal" : { - | "@type" : "xsd:decimal", - | "@value" : "$decimalValue" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasDecimal" : { + | "@id" : "${decimalValueIri.get}", + | "@type" : "knora-api:DecimalValue", + | "knora-api:decimalValueAsDecimal" : { + | "@type" : "xsd:decimal", + | "@value" : "$decimalValue" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3760,8 +3916,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3798,22 +3956,23 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasText" : { - | "@id" : "${textValueWithStandoffIri.get}", - | "@type" : "knora-api:TextValue", - | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString( - textValue2AsXmlWithStandardMapping)}, - | "knora-api:textValueHasMapping" : { - | "@id": "$standardMappingIri" - | } - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasText" : { + | "@id" : "${textValueWithStandoffIri.get}", + | "@type" : "knora-api:TextValue", + | "knora-api:textValueAsXml" : ${stringFormatter.toJsonEncodedString( + textValue2AsXmlWithStandardMapping + )}, + | "knora-api:textValueHasMapping" : { + | "@id": "$standardMappingIri" + | } + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -3826,8 +3985,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3859,8 +4020,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntityWithResourceValueIri = jsonLDEntity.replace("VALUE_IRI", textValueWithEscapeIri.get) val request = Put( baseApiUrl + "/v2/values", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntityWithResourceValueIri)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntityWithResourceValueIri) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3882,7 +4043,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val expectedText = """

- | test update

""".stripMargin + | test update

""".stripMargin assert(savedTextValueAsXml.contains(expectedText)) } @@ -3901,8 +4062,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { valueHasComment = valueHasComment ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3967,8 +4130,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -3989,15 +4154,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "GREGORIAN:2018-10-05 CE:2018-12-06 CE") + "GREGORIAN:2018-10-05 CE:2018-12-06 CE" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(dateValueHasStartDay) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasEndMonth) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(dateValueHasEndDay) @@ -4039,8 +4208,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4061,15 +4232,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "GREGORIAN:2018-09 CE:2018-12 CE") + "GREGORIAN:2018-09 CE:2018-12 CE" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasEndMonth) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -4107,8 +4282,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4129,14 +4306,17 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===( - "GREGORIAN:2018 CE:2020 CE") + "GREGORIAN:2018 CE:2020 CE" + ) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasEndYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -4167,8 +4347,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndEra = dateValueHasStartEra ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4190,13 +4372,16 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("GREGORIAN:2018-10-06 CE") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(dateValueHasStartDay) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasStartMonth) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(dateValueHasStartDay) @@ -4224,8 +4409,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndEra = dateValueHasStartEra ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4247,13 +4434,16 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("GREGORIAN:2018-07 CE") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===( - dateValueHasStartMonth) + dateValueHasStartMonth + ) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(dateValueHasStartMonth) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -4278,8 +4468,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { dateValueHasEndEra = dateValueHasStartEra ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4301,12 +4493,14 @@ class ValuesRouteV2E2ESpec extends E2ESpec { savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString) should ===("GREGORIAN:2019 CE") savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasCalendar) should ===( - dateValueHasCalendar) + dateValueHasCalendar + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartYear) should ===(dateValueHasStartYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasStartDay) should ===(None) savedValue.requireString(OntologyConstants.KnoraApiV2Complex.DateValueHasStartEra) should ===( - dateValueHasStartEra) + dateValueHasStartEra + ) savedValue.requireInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndYear) should ===(dateValueHasStartYear) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndMonth) should ===(None) savedValue.maybeInt(OntologyConstants.KnoraApiV2Complex.DateValueHasEndDay) should ===(None) @@ -4321,18 +4515,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasBoolean" : { - | "@id" : "${booleanValueIri.get}", - | "@type" : "knora-api:BooleanValue", - | "knora-api:booleanValueAsBoolean" : $booleanValue - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasBoolean" : { + | "@id" : "${booleanValueIri.get}", + | "@type" : "knora-api:BooleanValue", + | "knora-api:booleanValueAsBoolean" : $booleanValue + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4345,8 +4539,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4378,18 +4574,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasGeometry" : { - | "@id" : "${geometryValueIri.get}", - | "@type" : "knora-api:GeomValue", - | "knora-api:geometryValueAsGeometry" : ${stringFormatter.toJsonEncodedString(geometryValue2)} - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasGeometry" : { + | "@id" : "${geometryValueIri.get}", + | "@type" : "knora-api:GeomValue", + | "knora-api:geometryValueAsGeometry" : ${stringFormatter.toJsonEncodedString(geometryValue2)} + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4402,8 +4598,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4437,26 +4635,26 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasInterval" : { - | "@id" : "${intervalValueIri.get}", - | "@type" : "knora-api:IntervalValue", - | "knora-api:intervalValueHasStart" : { - | "@type" : "xsd:decimal", - | "@value" : "$intervalStart" - | }, - | "knora-api:intervalValueHasEnd" : { - | "@type" : "xsd:decimal", - | "@value" : "$intervalEnd" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInterval" : { + | "@id" : "${intervalValueIri.get}", + | "@type" : "knora-api:IntervalValue", + | "knora-api:intervalValueHasStart" : { + | "@type" : "xsd:decimal", + | "@value" : "$intervalStart" + | }, + | "knora-api:intervalValueHasEnd" : { + | "@type" : "xsd:decimal", + | "@value" : "$intervalEnd" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4469,8 +4667,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4515,22 +4715,22 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasTimeStamp" : { - | "@id" : "${timeValueIri.get}", - | "@type" : "knora-api:TimeValue", - | "knora-api:timeValueAsTimeStamp" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$timeStamp" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasTimeStamp" : { + | "@id" : "${timeValueIri.get}", + | "@type" : "knora-api:TimeValue", + | "knora-api:timeValueAsTimeStamp" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$timeStamp" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4543,8 +4743,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4581,21 +4783,21 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasListItem" : { - | "@id" : "${listValueIri.get}", - | "@type" : "knora-api:ListValue", - | "knora-api:listValueAsListNode" : { - | "@id" : "$listNode" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasListItem" : { + | "@id" : "${listValueIri.get}", + | "@type" : "knora-api:ListValue", + | "knora-api:listValueAsListNode" : { + | "@id" : "$listNode" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4608,8 +4810,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4630,8 +4834,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) val savedListValueHasListNode: IRI = - savedValue.requireIriInObject(OntologyConstants.KnoraApiV2Complex.ListValueAsListNode, - stringFormatter.validateAndEscapeIri) + savedValue.requireIriInObject( + OntologyConstants.KnoraApiV2Complex.ListValueAsListNode, + stringFormatter.validateAndEscapeIri + ) savedListValueHasListNode should ===(listNode) } @@ -4643,19 +4849,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasColor" : { - | "@id" : "${colorValueIri.get}", - | "@type" : "knora-api:ColorValue", - | "knora-api:colorValueAsColor" : "$color" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasColor" : { + | "@id" : "${colorValueIri.get}", + | "@type" : "knora-api:ColorValue", + | "knora-api:colorValueAsColor" : "$color" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4668,8 +4874,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4701,22 +4909,22 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasUri" : { - | "@id" : "${uriValueIri.get}", - | "@type" : "knora-api:UriValue", - | "knora-api:uriValueAsUri" : { - | "@type" : "xsd:anyURI", - | "@value" : "$uri" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasUri" : { + | "@id" : "${uriValueIri.get}", + | "@type" : "knora-api:UriValue", + | "knora-api:uriValueAsUri" : { + | "@type" : "xsd:anyURI", + | "@value" : "$uri" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4729,8 +4937,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4767,19 +4977,19 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasGeoname" : { - | "@id" : "${geonameValueIri.get}", - | "@type" : "knora-api:GeonameValue", - | "knora-api:geonameValueAsGeonameCode" : "$geonameCode" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasGeoname" : { + | "@id" : "${geonameValueIri.get}", + | "@type" : "knora-api:GeonameValue", + | "knora-api:geonameValueAsGeonameCode" : "$geonameCode" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -4792,8 +5002,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4842,8 +5054,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4857,8 +5071,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { // When you change a link value's target, it gets a new UUID. val newLinkValueUUID: UUID = - responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, - stringFormatter.validateBase64EncodedUuid) + responseJsonDoc.body.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasUUID, + stringFormatter.validateBase64EncodedUuid + ) assert(newLinkValueUUID != linkValueUUID) linkValueUUID = newLinkValueUUID @@ -4886,8 +5102,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { targetResourceIri = linkTargetIri ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) } @@ -4907,8 +5125,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { comment = Some(comment) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4922,8 +5142,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { // Since we only changed metadata, the UUID should be the same. val newLinkValueUUID: UUID = - responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, - stringFormatter.validateBase64EncodedUuid) + responseJsonDoc.body.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasUUID, + stringFormatter.validateBase64EncodedUuid + ) assert(newLinkValueUUID == linkValueUUID) val savedValue: JsonLDObject = getValue( @@ -4954,8 +5176,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { comment = Some(comment) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -4969,8 +5193,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { // Since we only changed metadata, the UUID should be the same. val newLinkValueUUID: UUID = - responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, - stringFormatter.validateBase64EncodedUuid) + responseJsonDoc.body.requireStringWithValidation( + OntologyConstants.KnoraApiV2Complex.ValueHasUUID, + stringFormatter.validateBase64EncodedUuid + ) assert(newLinkValueUUID == linkValueUUID) val savedValue: JsonLDObject = getValue( @@ -4998,8 +5224,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { comment = Some(comment) ) - val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest, response.toString) } @@ -5013,24 +5241,26 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:Thing", - | "anything:hasOtherThingValue" : { - | "@type" : "knora-api:LinkValue", - | "knora-api:linkValueHasTargetIri" : { - | "@id" : "${TestDing.iri}" - | }, - | "knora-api:valueHasComment" : "$comment" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasOtherThingValue" : { + | "@type" : "knora-api:LinkValue", + | "knora-api:linkValueHasTargetIri" : { + | "@id" : "${TestDing.iri}" + | }, + | "knora-api:valueHasComment" : "$comment" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val request = Post( + baseApiUrl + "/v2/values", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) @@ -5077,9 +5307,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) } @@ -5089,22 +5320,22 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val jsonLDEntity = s"""{ - | "@id" : "${AThing.iri}", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueForRsyncIri.get}", - | "@type" : "knora-api:IntValue", - | "knora-api:deleteDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "$deleteDate" - | } - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "${AThing.iri}", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueForRsyncIri.get}", + | "@type" : "knora-api:IntValue", + | "knora-api:deleteDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "$deleteDate" + | } + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -5117,9 +5348,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) } @@ -5127,21 +5359,22 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "not delete an integer value if the simple schema is submitted" in { val jsonLDEntity = s"""{ - | "@id" : "${AThing.iri}", - | "@type" : "anything:Thing", - | "anything:hasInteger" : { - | "@id" : "${intValueIri.get}", - | "@type" : "knora-api:IntValue" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" - | } - |}""".stripMargin - - val request = Post(baseApiUrl + "/v2/values/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "${AThing.iri}", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "${intValueIri.get}", + | "@type" : "knora-api:IntValue" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/simple/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#" + | } + |}""".stripMargin + + val request = Post( + baseApiUrl + "/v2/values/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) val responseAsString = responseToString(response) assert(response.status == StatusCodes.BadRequest, responseAsString) @@ -5157,9 +5390,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { maybeDeleteComment = None ) - val deleteRequest = Post(baseApiUrl + "/v2/values/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(SharedTestDataADM.anythingUser2.email, password)) + val deleteRequest = Post( + baseApiUrl + "/v2/values/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingUser2.email, password)) val deleteResponse: HttpResponse = singleAwaitingRequest(deleteRequest) assert(deleteResponse.status == StatusCodes.OK, deleteResponse.toString) @@ -5175,18 +5409,18 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "delete a link between two resources" in { val jsonLDEntity = s"""{ - | "@id" : "${AThing.iri}", - | "@type" : "anything:Thing", - | "anything:hasOtherThingValue" : { - | "@id": "${linkValueIri.get}", - | "@type" : "knora-api:LinkValue" - | }, - | "@context" : { - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "${AThing.iri}", + | "@type" : "anything:Thing", + | "anything:hasOtherThingValue" : { + | "@id": "${linkValueIri.get}", + | "@type" : "knora-api:LinkValue" + | }, + | "@context" : { + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -5199,9 +5433,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) - val request = Post(baseApiUrl + "/v2/values/delete", - HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + baseApiUrl + "/v2/values/delete", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status == StatusCodes.OK, response.toString) } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala index 8a15af7c9c..c6de9444ef 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala @@ -40,13 +40,13 @@ import org.knora.webapi.util.MutableTestIri import scala.concurrent.ExecutionContextExecutor /** - * Tests creating a still image file value using a mock Sipi. - */ + * Tests creating a still image file value using a mock Sipi. + */ class ValuesV2R2RSpec extends R2RSpec { override def testConfigSource: String = """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" + |# akka.loglevel = "DEBUG" + |# akka.stdout-loglevel = "DEBUG" """.stripMargin private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -61,7 +61,8 @@ class ValuesV2R2RSpec extends R2RSpec { /* we need to run our app with the mocked sipi actor */ override lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with ManagersWithMockedSipi).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + name = APPLICATION_MANAGER_ACTOR_NAME + ) private val aThingPictureIri = "http://rdfh.ch/0001/a-thing-picture" @@ -80,9 +81,11 @@ class ValuesV2R2RSpec extends R2RSpec { RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") ) - private def getResourceWithValues(resourceIri: IRI, - propertyIrisForGravsearch: Seq[SmartIri], - userEmail: String): JsonLDDocument = { + private def getResourceWithValues( + resourceIri: IRI, + propertyIrisForGravsearch: Seq[SmartIri], + userEmail: String + ): JsonLDDocument = { // Make a Gravsearch query from a template. val gravsearchQuery: String = org.knora.webapi.messages.twirl.queries.gravsearch.txt .getResourceWithSpecifiedProperties( @@ -93,48 +96,58 @@ class ValuesV2R2RSpec extends R2RSpec { // Run the query. - Post("/v2/searchextended", HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) ~> searchPath ~> check { + Post( + "/v2/searchextended", + HttpEntity(SparqlQueryConstants.`application/sparql-query`, gravsearchQuery) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) ~> searchPath ~> check { assert(status == StatusCodes.OK, response.toString) responseToJsonLDDocument(response) } } - private def getValuesFromResource(resource: JsonLDDocument, propertyIriInResult: SmartIri): JsonLDArray = { + private def getValuesFromResource(resource: JsonLDDocument, propertyIriInResult: SmartIri): JsonLDArray = resource.requireArray(propertyIriInResult.toString) - } - private def getValueFromResource(resource: JsonLDDocument, - propertyIriInResult: SmartIri, - expectedValueIri: IRI): JsonLDObject = { + private def getValueFromResource( + resource: JsonLDDocument, + propertyIriInResult: SmartIri, + expectedValueIri: IRI + ): JsonLDObject = { val resourceIri: IRI = resource.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) val propertyValues: JsonLDArray = getValuesFromResource(resource = resource, propertyIriInResult = propertyIriInResult) val matchingValues: Seq[JsonLDObject] = propertyValues.value.collect { case jsonLDObject: JsonLDObject - if jsonLDObject.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) == expectedValueIri => + if jsonLDObject.requireStringWithValidation( + JsonLDKeywords.ID, + stringFormatter.validateAndEscapeIri + ) == expectedValueIri => jsonLDObject } if (matchingValues.isEmpty) { throw AssertionException( - s"Property <$propertyIriInResult> of resource <$resourceIri> does not have value <$expectedValueIri>") + s"Property <$propertyIriInResult> of resource <$resourceIri> does not have value <$expectedValueIri>" + ) } if (matchingValues.size > 1) { throw AssertionException( - s"Property <$propertyIriInResult> of resource <$resourceIri> has more than one value with the IRI <$expectedValueIri>") + s"Property <$propertyIriInResult> of resource <$resourceIri> has more than one value with the IRI <$expectedValueIri>" + ) } matchingValues.head } - private def getValue(resourceIri: IRI, - propertyIriForGravsearch: SmartIri, - propertyIriInResult: SmartIri, - expectedValueIri: IRI, - userEmail: String): JsonLDObject = { + private def getValue( + resourceIri: IRI, + propertyIriForGravsearch: SmartIri, + propertyIriInResult: SmartIri, + expectedValueIri: IRI, + userEmail: String + ): JsonLDObject = { val resource: JsonLDDocument = getResourceWithValues( resourceIri = resourceIri, propertyIrisForGravsearch = Seq(propertyIriForGravsearch), @@ -155,18 +168,18 @@ class ValuesV2R2RSpec extends R2RSpec { val jsonLDEntity = s"""{ - | "@id" : "$resourceIri", - | "@type" : "anything:ThingPicture", - | "knora-api:hasStillImageFileValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q", - | "@type" : "knora-api:StillImageFileValue", - | "knora-api:fileValueHasFilename" : "$internalFilename" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "$resourceIri", + | "@type" : "anything:ThingPicture", + | "knora-api:hasStillImageFileValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q", + | "@type" : "knora-api:StillImageFileValue", + | "knora-api:fileValueHasFilename" : "$internalFilename" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin clientTestDataCollector.addFile( TestDataFileContent( @@ -180,7 +193,8 @@ class ValuesV2R2RSpec extends R2RSpec { ) Put("/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) ~> valuesPath ~> check { + BasicHttpCredentials(anythingUserEmail, password) + ) ~> valuesPath ~> check { assert(status == StatusCodes.OK, response.toString) val responseJsonDoc = responseToJsonLDDocument(response) val valueIri: IRI = diff --git a/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala b/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala index baf8c3912d..bdcf194a2c 100644 --- a/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala @@ -24,8 +24,8 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike /** - * This spec is used to test 'ListAdminMessages'. - */ + * This spec is used to test 'ListAdminMessages'. + */ class ServerVersionSpec extends AnyWordSpecLike with Matchers { "The server version header" should { diff --git a/webapi/src/test/scala/org/knora/webapi/http/version/versioninfo/VersionInfoSpec.scala b/webapi/src/test/scala/org/knora/webapi/http/version/versioninfo/VersionInfoSpec.scala index 9bcda9960a..9bdc585887 100644 --- a/webapi/src/test/scala/org/knora/webapi/http/version/versioninfo/VersionInfoSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/http/version/versioninfo/VersionInfoSpec.scala @@ -23,8 +23,8 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike /** - * This spec is used to test 'ListAdminMessages'. - */ + * This spec is used to test 'ListAdminMessages'. + */ class VersionInfoSpec extends AnyWordSpecLike with Matchers { "The version info" should { diff --git a/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala index 6b9a37f732..19a5e383ed 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala @@ -32,14 +32,14 @@ import scala.languageFeature.postfixOps object VersionRouteITSpec { val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" + |akka.loglevel = "DEBUG" + |akka.stdout-loglevel = "DEBUG" """.stripMargin) } /** - * End-to-End (E2E) test specification for testing route rejections. - */ + * End-to-End (E2E) test specification for testing route rejections. + */ class VersionRouteITSpec extends ITKnoraLiveSpec(VersionRouteITSpec.config) { private def getJsonResponse: JsObject = { diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala index fcf077e623..97122f5885 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala @@ -42,22 +42,30 @@ object DrawingsGodsV1ITSpec { } /** - * End-to-End (E2E) test specification for additional testing of permissions. - */ + * End-to-End (E2E) test specification for additional testing of permissions. + */ class DrawingsGodsV1ITSpec extends ITKnoraLiveSpec(DrawingsGodsV1ITSpec.config) with AuthenticationV2JsonProtocol with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_admin-data.ttl", - name = "http://www.knora.org/data/admin"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_permissions-data.ttl", - name = "http://www.knora.org/data/permissions"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_ontology.ttl", - name = "http://www.knora.org/ontology/0105/drawings-gods"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_data.ttl", - name = "http://www.knora.org/data/0105/drawings-gods") + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_admin-data.ttl", + name = "http://www.knora.org/data/admin" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_permissions-data.ttl", + name = "http://www.knora.org/data/permissions" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_ontology.ttl", + name = "http://www.knora.org/ontology/0105/drawings-gods" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_data.ttl", + name = "http://www.knora.org/data/0105/drawings-gods" + ) ) "issue: https://github.com/dhlab-basel/Knora/issues/408" should { @@ -72,10 +80,10 @@ class DrawingsGodsV1ITSpec val params = s""" - |{ - | "email": "$drawingsOfGodsUserEmail", - | "password": "$testPass" - |} + |{ + | "email": "$drawingsOfGodsUserEmail", + | "password": "$testPass" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -99,35 +107,38 @@ class DrawingsGodsV1ITSpec val params = s""" - |{ - | "restype_id":"http://www.knora.org/ontology/0105/drawings-gods#Verso", - | "properties":{ - | "http://www.knora.org/ontology/0105/drawings-gods#hasVersoTranslatorEn":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-TranslatorList-PYB"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasCommentOriginalLanguage":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-LanguageList-Buriat"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasDescriptionOriginalLanguage":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-LanguageList-Buriat"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasDescriptionAuthor":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-DescriptionAuthorList-child"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasInstructionRestitutionOriginalLanguage":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-LanguageList-Buriat"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasVersoTranslatorFr":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-TranslatorList-PYB"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasCommentAuthor":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-CommentAuthorList-child"}], - | "http://www.knora.org/ontology/0105/drawings-gods#hasCodeVerso":[{"richtext_value":{"utf8str":"dayyad"}}] - | }, - | "file": "${uploadedFile.internalFilename}", - | "project_id":"http://rdfh.ch/projects/0105", - | "label":"dayyad" - |} + |{ + | "restype_id":"http://www.knora.org/ontology/0105/drawings-gods#Verso", + | "properties":{ + | "http://www.knora.org/ontology/0105/drawings-gods#hasVersoTranslatorEn":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-TranslatorList-PYB"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasCommentOriginalLanguage":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-LanguageList-Buriat"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasDescriptionOriginalLanguage":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-LanguageList-Buriat"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasDescriptionAuthor":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-DescriptionAuthorList-child"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasInstructionRestitutionOriginalLanguage":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-LanguageList-Buriat"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasVersoTranslatorFr":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-TranslatorList-PYB"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasCommentAuthor":[{"hlist_value":"http://rdfh.ch/lists/0105/drawings-gods-2016-list-CommentAuthorList-child"}], + | "http://www.knora.org/ontology/0105/drawings-gods#hasCodeVerso":[{"richtext_value":{"utf8str":"dayyad"}}] + | }, + | "file": "${uploadedFile.internalFilename}", + | "project_id":"http://rdfh.ch/projects/0105", + | "label":"dayyad" + |} """.stripMargin // Send the JSON in a POST request to the Knora API server. - val knoraPostRequest = Post(baseApiUrl + "/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(drawingsOfGodsUserEmail, testPass)) + val knoraPostRequest = + Post(baseApiUrl + "/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(drawingsOfGodsUserEmail, testPass) + ) val knoraPostResponseJson = getResponseJson(knoraPostRequest) // Get the IRI of the newly created resource. val resourceIri: String = knoraPostResponseJson.fields("res_id").asInstanceOf[JsString].value // Request the resource from the Knora API server. - val knoraRequestNewResource = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(drawingsOfGodsUserEmail, testPass)) + val knoraRequestNewResource = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(drawingsOfGodsUserEmail, testPass)) val knoraNewResourceJson = getResponseJson(knoraRequestNewResource) // Get the URL of the image that was uploaded. diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/ErrorV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/ErrorV1ITSpec.scala index 97e2e3d95e..5f6f606890 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/ErrorV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/ErrorV1ITSpec.scala @@ -26,8 +26,8 @@ import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtoc import scala.concurrent.duration._ /** - * Causes an internal server error to see if logging is working correctly. - */ + * Causes an internal server error to see if logging is working correctly. + */ class ErrorV1ITSpec extends ITKnoraLiveSpec with TriplestoreJsonProtocol { "Make a request that causes an internal server error" in { diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala index 25f8b295d5..2c289fa6d9 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala @@ -43,14 +43,14 @@ import scala.xml.transform.{RewriteRule, RuleTransformer} object KnoraSipiIntegrationV1ITSpec { val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" + |akka.loglevel = "DEBUG" + |akka.stdout-loglevel = "DEBUG" """.stripMargin) } /** - * End-to-End (E2E) test specification for testing Knora-Sipi integration. - */ + * End-to-End (E2E) test specification for testing Knora-Sipi integration. + */ class KnoraSipiIntegrationV1ITSpec extends ITKnoraLiveSpec(KnoraSipiIntegrationV1ITSpec.config) with AuthenticationV2JsonProtocol @@ -112,29 +112,27 @@ class KnoraSipiIntegrationV1ITSpec private val pathToTestVideo2 = s"test_data/test_route/files/$testVideoOriginalFilename" /** - * Adds the IRI of a XSL transformation to the given mapping. - * - * @param mapping the mapping to be updated. - * @param xsltIri the Iri of the XSLT to be added. - * @return the updated mapping. - */ + * Adds the IRI of a XSL transformation to the given mapping. + * + * @param mapping the mapping to be updated. + * @param xsltIri the Iri of the XSLT to be added. + * @return the updated mapping. + */ private def addXSLTIriToMapping(mapping: String, xsltIri: String): String = { val mappingXML: Elem = XML.loadString(mapping) // add the XSL transformation's Iri to the mapping XML (replacing the string 'toBeDefined') val rule: RewriteRule = new RewriteRule() { - override def transform(node: Node): Node = { - + override def transform(node: Node): Node = node match { case e: Elem if e.label == "defaultXSLTransformation" => - e.copy(child = e.child collect { - case Text(_) => Text(xsltIri) + e.copy(child = e.child collect { case Text(_) => + Text(xsltIri) }) case other => other } - } } val transformer = new RuleTransformer(rule) @@ -151,12 +149,12 @@ class KnoraSipiIntegrationV1ITSpec } /** - * Given the id originally provided by the client, gets the generated IRI from a bulk import response. - * - * @param bulkResponse the response from the bulk import route. - * @param clientID the client id to look for. - * @return the Knora IRI of the resource. - */ + * Given the id originally provided by the client, gets the generated IRI from a bulk import response. + * + * @param bulkResponse the response from the bulk import route. + * @param clientID the client id to look for. + * @return the Knora IRI of the resource. + */ private def getResourceIriFromBulkResponse(bulkResponse: JsObject, clientID: String): String = { val resIriOption: Option[JsValue] = bulkResponse.fields.get("createdResources") match { case Some(createdResources: JsArray) => @@ -197,10 +195,10 @@ class KnoraSipiIntegrationV1ITSpec val params = s""" - |{ - | "email": "$userEmail", - | "password": "$password" - |} + |{ + | "email": "$userEmail", + | "password": "$password" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -224,30 +222,31 @@ class KnoraSipiIntegrationV1ITSpec val knoraParams = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0803/incunabula#page", - | "properties": { - | "http://www.knora.org/ontology/0803/incunabula#pagenum": [ - | {"richtext_value": {"utf8str": "test page"}} - | ], - | "http://www.knora.org/ontology/0803/incunabula#origname": [ - | {"richtext_value": {"utf8str": "Chlaus"}} - | ], - | "http://www.knora.org/ontology/0803/incunabula#partOf": [ - | {"link_value": "http://rdfh.ch/0803/5e77e98d2603"} - | ], - | "http://www.knora.org/ontology/0803/incunabula#seqnum": [{"int_value": 99999999}] - | }, - | "file": "${uploadedFile.internalFilename}", - | "label": "test page", - | "project_id": "http://rdfh.ch/projects/0803" - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0803/incunabula#page", + | "properties": { + | "http://www.knora.org/ontology/0803/incunabula#pagenum": [ + | {"richtext_value": {"utf8str": "test page"}} + | ], + | "http://www.knora.org/ontology/0803/incunabula#origname": [ + | {"richtext_value": {"utf8str": "Chlaus"}} + | ], + | "http://www.knora.org/ontology/0803/incunabula#partOf": [ + | {"link_value": "http://rdfh.ch/0803/5e77e98d2603"} + | ], + | "http://www.knora.org/ontology/0803/incunabula#seqnum": [{"int_value": 99999999}] + | }, + | "file": "${uploadedFile.internalFilename}", + | "label": "test page", + | "project_id": "http://rdfh.ch/projects/0803" + |} """.stripMargin // Send the JSON in a POST request to the Knora API server. - val knoraPostRequest = Post(baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, knoraParams)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val knoraPostRequest = + Post(baseApiUrl + "/v1/resources", HttpEntity(ContentTypes.`application/json`, knoraParams)) ~> addCredentials( + BasicHttpCredentials(userEmail, password) + ) val knoraPostResponseJson = getResponseJson(knoraPostRequest) // Get the IRI of the newly created resource. @@ -255,8 +254,9 @@ class KnoraSipiIntegrationV1ITSpec secondPageIri.set(resourceIri) // Request the resource from the Knora API server. - val knoraRequestNewResource = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val knoraRequestNewResource = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraRequestNewResource) } @@ -279,17 +279,17 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a PUT request to the Knora API server. val knoraPutRequest = Put( baseApiUrl + "/v1/filevalue/" + URLEncoder.encode(secondPageIri.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPutRequest) } "create an 'anything:thing'" in { val standoffXml = """ - | - | Wild thing, you make my heart sing - | + | + | Wild thing, you make my heart sing + | """.stripMargin val knoraParams = JsObject( @@ -324,8 +324,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val knoraPostRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPostRequest) } @@ -347,34 +347,34 @@ class KnoraSipiIntegrationV1ITSpec val knoraParams = s""" - | - | - | a book with one page - | the title of a book with one page - | - | - | a page with an image - | - | Chlaus - | 1a - | - | - | - | 1 - | - |""".stripMargin + | + | + | a book with one page + | the title of a book with one page + | + | + | a page with an image + | + | Chlaus + | 1a + | + | + | + | 1 + | + |""".stripMargin val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0803", "UTF-8") // Send the JSON in a POST request to the Knora API server. val knoraPostRequest: HttpRequest = Post( baseApiUrl + s"/v1/resources/xmlimport/$projectIri", - HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), knoraParams)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), knoraParams) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val knoraPostResponseJson: JsObject = getResponseJson(knoraPostRequest) val createdResources = knoraPostResponseJson.fields("createdResources").asInstanceOf[JsArray].elements @@ -384,13 +384,15 @@ class KnoraSipiIntegrationV1ITSpec val pageResourceIri = createdResources(1).asJsObject.fields("resourceIri").asInstanceOf[JsString].value // Request the book resource from the Knora API server. - val knoraRequestNewBookResource = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(bookResourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val knoraRequestNewBookResource = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(bookResourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraRequestNewBookResource) // Request the page resource from the Knora API server. - val knoraRequestNewPageResource = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(pageResourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val knoraRequestNewPageResource = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(pageResourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val pageJson: JsObject = getResponseJson(knoraRequestNewPageResource) val locdata = pageJson.fields("resinfo").asJsObject.fields("locdata").asJsObject val origname = locdata.fields("origname").asInstanceOf[JsString].value @@ -408,8 +410,11 @@ class KnoraSipiIntegrationV1ITSpec val sipiUploadResponse: SipiUploadResponse = uploadToSipi( loginToken = loginToken, filesToUpload = Seq( - FileToUpload(path = pathToXSLTransformation, - mimeType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`))) + FileToUpload( + path = pathToXSLTransformation, + mimeType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`) + ) + ) ) val uploadedFile: SipiUploadResponseEntry = sipiUploadResponse.uploadedFiles.head @@ -428,8 +433,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val knoraPostRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val responseJson: JsObject = getResponseJson(knoraPostRequest) // get the Iri of the XSL transformation @@ -446,11 +451,11 @@ class KnoraSipiIntegrationV1ITSpec val paramsCreateLetterMappingFromXML = s""" - |{ - | "project_id": "http://rdfh.ch/projects/0001", - | "label": "mapping for letters with XSLT", - | "mappingName": "LetterMappingXSLT" - |} + |{ + | "project_id": "http://rdfh.ch/projects/0001", + | "label": "mapping for letters with XSLT", + | "mappingName": "LetterMappingXSLT" + |} """.stripMargin // create a mapping referring to the XSL transformation @@ -467,8 +472,8 @@ class KnoraSipiIntegrationV1ITSpec ) // send mapping xml to route - val knoraPostRequest2 = Post(baseApiUrl + "/v1/mapping", formDataMapping) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val knoraPostRequest2 = + Post(baseApiUrl + "/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPostRequest2) @@ -479,11 +484,11 @@ class KnoraSipiIntegrationV1ITSpec val paramsForMapping = s""" - |{ - | "project_id": "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", - | "label": "mapping for BEOL letter", - | "mappingName": "BEOLMapping" - |} + |{ + | "project_id": "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", + | "label": "mapping for BEOL letter", + | "mappingName": "BEOLMapping" + |} """.stripMargin // create a mapping referring to the XSL transformation @@ -500,8 +505,8 @@ class KnoraSipiIntegrationV1ITSpec ) // send mapping xml to route - val knoraPostRequest = Post(baseApiUrl + "/v1/mapping", formDataMapping) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val knoraPostRequest = + Post(baseApiUrl + "/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(userEmail, password)) getResponseJson(knoraPostRequest) @@ -510,8 +515,8 @@ class KnoraSipiIntegrationV1ITSpec val bulkXML = FileUtil.readTextFile(Paths.get(pathToBEOLBulkXML)) val bulkRequest = Post( - baseApiUrl + "/v1/resources/xmlimport/" + URLEncoder.encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", - "UTF-8"), + baseApiUrl + "/v1/resources/xmlimport/" + URLEncoder + .encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", "UTF-8"), HttpEntity(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), bulkXML) ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) @@ -526,8 +531,11 @@ class KnoraSipiIntegrationV1ITSpec val bodyXsltUploadResponse: SipiUploadResponse = uploadToSipi( loginToken = loginToken, filesToUpload = Seq( - FileToUpload(path = pathToBEOLBodyXSLTransformation, - mimeType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`))) + FileToUpload( + path = pathToBEOLBodyXSLTransformation, + mimeType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`) + ) + ) ) val uploadedBodyXsltFile: SipiUploadResponseEntry = bodyXsltUploadResponse.uploadedFiles.head @@ -546,8 +554,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val bodyXSLTRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, bodyXsltParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, bodyXsltParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val bodyXSLTJson: JsObject = getResponseJson(bodyXSLTRequest) // get the Iri of the body XSL transformation @@ -564,11 +572,11 @@ class KnoraSipiIntegrationV1ITSpec val paramsCreateLetterMappingFromXML = s""" - |{ - | "project_id": "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", - | "label": "mapping for BEOL to TEI", - | "mappingName": "BEOLToTEI" - |} + |{ + | "project_id": "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF", + | "label": "mapping for BEOL to TEI", + | "mappingName": "BEOLToTEI" + |} """.stripMargin // create a mapping referring to the XSL transformation @@ -585,8 +593,8 @@ class KnoraSipiIntegrationV1ITSpec ) // send mapping xml to route - val mappingRequest = Post(baseApiUrl + "/v1/mapping", formDataMapping) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val mappingRequest = + Post(baseApiUrl + "/v1/mapping", formDataMapping) ~> addCredentials(BasicHttpCredentials(userEmail, password)) getResponseJson(mappingRequest) @@ -594,8 +602,11 @@ class KnoraSipiIntegrationV1ITSpec val gravsearchTemplateUploadResponse: SipiUploadResponse = uploadToSipi( loginToken = loginToken, filesToUpload = Seq( - FileToUpload(path = pathToBEOLGravsearchTemplate, - mimeType = MediaTypes.`text/plain`.toContentType(HttpCharsets.`UTF-8`))) + FileToUpload( + path = pathToBEOLGravsearchTemplate, + mimeType = MediaTypes.`text/plain`.toContentType(HttpCharsets.`UTF-8`) + ) + ) ) val uploadedGravsearchTemplateFile: SipiUploadResponseEntry = gravsearchTemplateUploadResponse.uploadedFiles.head @@ -613,8 +624,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val gravsearchTemplateRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, gravsearchTemplateParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, gravsearchTemplateParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val gravsearchTemplateJSON: JsObject = getResponseJson(gravsearchTemplateRequest) gravsearchTemplateIri.set(gravsearchTemplateJSON.fields.get("res_id") match { @@ -626,8 +637,11 @@ class KnoraSipiIntegrationV1ITSpec val headerXsltUploadResponse: SipiUploadResponse = uploadToSipi( loginToken = loginToken, filesToUpload = Seq( - FileToUpload(path = pathToBEOLHeaderXSLTransformation, - mimeType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`))) + FileToUpload( + path = pathToBEOLHeaderXSLTransformation, + mimeType = MediaTypes.`application/xml`.toContentType(HttpCharsets.`UTF-8`) + ) + ) ) val uploadedHeaderXsltFile: SipiUploadResponseEntry = headerXsltUploadResponse.uploadedFiles.head @@ -643,10 +657,10 @@ class KnoraSipiIntegrationV1ITSpec ) // Send the JSON in a POST request to the Knora API server. - val headerXSLTRequest: HttpRequest = Post(baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, - headerXsltParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val headerXSLTRequest: HttpRequest = Post( + baseApiUrl + "/v1/resources", + HttpEntity(ContentTypes.`application/json`, headerXsltParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val headerXSLTJson = getResponseJson(headerXSLTRequest) val headerXSLTIri: IRI = headerXSLTJson.fields.get("res_id") match { @@ -659,10 +673,13 @@ class KnoraSipiIntegrationV1ITSpec val letterTEIRequest: HttpRequest = Get( baseApiUrl + "/v2/tei/" + URLEncoder.encode(letterIri.get, "UTF-8") + "?textProperty=" + URLEncoder.encode("http://0.0.0.0:3333/ontology/0801/beol/v2#hasText", "UTF-8") + - "&mappingIri=" + URLEncoder.encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF/mappings/BEOLToTEI", - "UTF-8") + + "&mappingIri=" + URLEncoder.encode( + "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF/mappings/BEOLToTEI", + "UTF-8" + ) + "&gravsearchTemplateIri=" + URLEncoder.encode(gravsearchTemplateIri.get, "UTF-8") + - "&teiHeaderXSLTIri=" + URLEncoder.encode(headerXSLTIri, "UTF-8")) + "&teiHeaderXSLTIri=" + URLEncoder.encode(headerXSLTIri, "UTF-8") + ) val letterTEIResponse: HttpResponse = singleAwaitingRequest(letterTEIRequest) val letterResponseBodyFuture: Future[String] = @@ -671,47 +688,47 @@ class KnoraSipiIntegrationV1ITSpec val xmlExpected = s""" - | - | - | - | - | Testletter - | - | - |

This is the TEI/XML representation of the resource identified by the Iri - | ${letterIri.get}. - |

- |
- | - |

Representation of the resource's text as TEI/XML

- |
- |
- | - | - | - | Scheuchzer, - | Johann Jacob - | - | - | - | Hermann, - | Jacob - | - | - | - |
- | - | - |

[...] Viro Clarissimo.

- |

Dn. Jacobo Hermanno S. S. M. C.

- |

et Ph. M.

- |

S. P. D.

- |

J. J. Sch.

- |

En quae desideras, vir Erud.e κεχαρισμένω θυμῷ Actorum Lipsiensium fragmentaGemeint sind die im Brief Hermanns von 1703.06.05 erbetenen Exemplare AE Aprilis 1703 und AE Suppl., tom. III, 1702. animi mei erga te prope[n]sissimi tenuia indicia. Dudum est, ex quo Tibi innotescere, et tuam ambire amicitiam decrevi, dudum, ex quo Ingenij Tui acumen suspexi, immo non potui quin admirarer pro eo, quod summam Demonstrationem Tuam de Iride communicare dignatus fueris summas ago grates; quamvis in hoc studij genere, non alias [siquid] μετρικώτατος, propter aliorum negotiorum continuam seriem non altos possim scandere gradus. Perge Vir Clariss. Erudito orbi propalare Ingenij Tui fructum; sed et me amare.

- |

d. [10] Jun. 1703.Der Tag ist im Manuskript unleserlich. Da der Entwurf in Scheuchzers "Copiae epistolarum" zwischen zwei Einträgen vom 10. Juni 1703 steht, ist der Brief wohl auf den gleichen Tag zu datieren. - |

- |
- |
+ | + | + | + | + | Testletter + | + | + |

This is the TEI/XML representation of the resource identified by the Iri + | ${letterIri.get}. + |

+ |
+ | + |

Representation of the resource's text as TEI/XML

+ |
+ |
+ | + | + | + | Scheuchzer, + | Johann Jacob + | + | + | + | Hermann, + | Jacob + | + | + | + |
+ | + | + |

[...] Viro Clarissimo.

+ |

Dn. Jacobo Hermanno S. S. M. C.

+ |

et Ph. M.

+ |

S. P. D.

+ |

J. J. Sch.

+ |

En quae desideras, vir Erud.e κεχαρισμένω θυμῷ Actorum Lipsiensium fragmentaGemeint sind die im Brief Hermanns von 1703.06.05 erbetenen Exemplare AE Aprilis 1703 und AE Suppl., tom. III, 1702. animi mei erga te prope[n]sissimi tenuia indicia. Dudum est, ex quo Tibi innotescere, et tuam ambire amicitiam decrevi, dudum, ex quo Ingenij Tui acumen suspexi, immo non potui quin admirarer pro eo, quod summam Demonstrationem Tuam de Iride communicare dignatus fueris summas ago grates; quamvis in hoc studij genere, non alias [siquid] μετρικώτατος, propter aliorum negotiorum continuam seriem non altos possim scandere gradus. Perge Vir Clariss. Erudito orbi propalare Ingenij Tui fructum; sed et me amare.

+ |

d. [10] Jun. 1703.Der Tag ist im Manuskript unleserlich. Da der Entwurf in Scheuchzers "Copiae epistolarum" zwischen zwei Einträgen vom 10. Juni 1703 steht, ist der Brief wohl auf den gleichen Tag zu datieren. + |

+ |
+ |
""".stripMargin val xmlDiff: Diff = @@ -725,10 +742,13 @@ class KnoraSipiIntegrationV1ITSpec val letterTEIRequest: HttpRequest = Get( baseApiUrl + "/v2/tei/" + URLEncoder.encode(letterIri.get, "UTF-8") + "?textProperty=" + URLEncoder.encode("http://0.0.0.0:3333/ontology/0801/beol/v2#hasText", "UTF-8") + - "&mappingIri=" + URLEncoder.encode("http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF/mappings/BEOLToTEI", - "UTF-8") + + "&mappingIri=" + URLEncoder.encode( + "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF/mappings/BEOLToTEI", + "UTF-8" + ) + "&gravsearchTemplateIri=" + URLEncoder.encode(gravsearchTemplateIri.get, "UTF-8") + - "&teiHeaderXSLTIri=" + URLEncoder.encode(missingHeaderXSLTIri, "UTF-8")) + "&teiHeaderXSLTIri=" + URLEncoder.encode(missingHeaderXSLTIri, "UTF-8") + ) val response: HttpResponse = singleAwaitingRequest(letterTEIRequest) assert(response.status.intValue == 500) @@ -763,8 +783,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val createDocumentResourceRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, createDocumentResourceParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, createDocumentResourceParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val createDocumentResourceResponseJson: JsObject = getResponseJson(createDocumentResourceRequest) @@ -777,8 +797,9 @@ class KnoraSipiIntegrationV1ITSpec pdfResourceIri.set(resourceIri) // Request the document resource from the Knora API server. - val documentResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val documentResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val documentResourceResponse: JsObject = getResponseJson(documentResourceRequest) val locdata = documentResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -814,14 +835,15 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a PUT request to the Knora API server. val knoraPutRequest = Put( baseApiUrl + "/v1/filevalue/" + URLEncoder.encode(pdfResourceIri.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPutRequest) // Request the document resource from the Knora API server. - val documentResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(pdfResourceIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val documentResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(pdfResourceIri.get, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val documentResourceResponse: JsObject = getResponseJson(documentResourceRequest) val locdata = documentResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -855,8 +877,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val createDocumentResourceRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, createDocumentResourceParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, createDocumentResourceParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val createDocumentResourceResponseJson: JsObject = getResponseJson(createDocumentResourceRequest) @@ -869,8 +891,9 @@ class KnoraSipiIntegrationV1ITSpec zipResourceIri.set(resourceIri) // Request the document resource from the Knora API server. - val documentResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val documentResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val documentResourceResponse: JsObject = getResponseJson(documentResourceRequest) val locdata = documentResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -902,14 +925,15 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a PUT request to the Knora API server. val knoraPutRequest = Put( baseApiUrl + "/v1/filevalue/" + URLEncoder.encode(zipResourceIri.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPutRequest) // Request the document resource from the Knora API server. - val documentResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(zipResourceIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val documentResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(zipResourceIri.get, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val documentResourceResponse: JsObject = getResponseJson(documentResourceRequest) val locdata = documentResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -945,8 +969,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val createAudioResourceRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, createAudioResourceParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, createAudioResourceParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val createAudioResourceResponseJson: JsObject = getResponseJson(createAudioResourceRequest) @@ -959,8 +983,9 @@ class KnoraSipiIntegrationV1ITSpec wavResourceIri.set(resourceIri) // Request the audio file resource from the Knora API server. - val audioResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val audioResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val audioResourceResponse: JsObject = getResponseJson(audioResourceRequest) val locdata = audioResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -992,14 +1017,15 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a PUT request to the Knora API server. val knoraPutRequest = Put( baseApiUrl + "/v1/filevalue/" + URLEncoder.encode(wavResourceIri.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPutRequest) // Request the document resource from the Knora API server. - val audioResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(wavResourceIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val audioResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(wavResourceIri.get, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val audioResourceResponse: JsObject = getResponseJson(audioResourceRequest) val locdata = audioResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -1036,8 +1062,8 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a POST request to the Knora API server. val createVideoResourceRequest: HttpRequest = Post( baseApiUrl + "/v1/resources", - HttpEntity(ContentTypes.`application/json`, createVideoResourceParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, createVideoResourceParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val createVideoResourceResponseJson: JsObject = getResponseJson(createVideoResourceRequest) @@ -1050,8 +1076,9 @@ class KnoraSipiIntegrationV1ITSpec videoResourceIri.set(resourceIri) // Request the video file resource from the Knora API server. - val videoResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val videoResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(resourceIri, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val videoResourceResponse: JsObject = getResponseJson(videoResourceRequest) val locdata = videoResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject @@ -1083,14 +1110,15 @@ class KnoraSipiIntegrationV1ITSpec // Send the JSON in a PUT request to the Knora API server. val knoraPutRequest = Put( baseApiUrl + "/v1/filevalue/" + URLEncoder.encode(wavResourceIri.get, "UTF-8"), - HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint)) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + HttpEntity(ContentTypes.`application/json`, knoraParams.compactPrint) + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) checkResponseOK(knoraPutRequest) // Request the document resource from the Knora API server. - val videoResourceRequest = Get(baseApiUrl + "/v1/resources/" + URLEncoder.encode(videoResourceIri.get, "UTF-8")) ~> addCredentials( - BasicHttpCredentials(userEmail, password)) + val videoResourceRequest = Get( + baseApiUrl + "/v1/resources/" + URLEncoder.encode(videoResourceIri.get, "UTF-8") + ) ~> addCredentials(BasicHttpCredentials(userEmail, password)) val videoResourceResponse: JsObject = getResponseJson(videoResourceRequest) val locdata = videoResourceResponse.fields("resinfo").asJsObject.fields("locdata").asJsObject diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala index 021197f8e6..5e25b6004e 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala @@ -23,9 +23,9 @@ import org.knora.webapi._ import org.knora.webapi.messages.store.triplestoremessages.{RdfDataObject, TriplestoreJsonProtocol} /** - * End-to-End (E2E) test specification for testing Knora-Sipi integration. Sipi must be running with the config file - * `sipi.knora-docker-it-config.lua`. - */ + * End-to-End (E2E) test specification for testing Knora-Sipi integration. Sipi must be running with the config file + * `sipi.knora-docker-it-config.lua`. + */ class KnoraSipiPermissionsV1ITSpec extends ITKnoraLiveSpec with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( diff --git a/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala index 8c9e0a36b0..3d0fb1fd50 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala @@ -41,14 +41,14 @@ import scala.concurrent.duration._ object KnoraSipiIntegrationV2ITSpec { val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" + |akka.loglevel = "DEBUG" + |akka.stdout-loglevel = "DEBUG" """.stripMargin) } /** - * Tests interaction between Knora and Sipi using Knora API v2. - */ + * Tests interaction between Knora and Sipi using Knora API v2. + */ class KnoraSipiIntegrationV2ITSpec extends ITKnoraLiveSpec(KnoraSipiIntegrationV2ITSpec.config) with AuthenticationV2JsonProtocol @@ -129,117 +129,127 @@ class KnoraSipiIntegrationV2ITSpec private val pathToTestVideo2 = s"test_data/test_route/files/$testVideo2OriginalFilename" /** - * Represents the information that Knora returns about an image file value that was created. - * - * @param internalFilename the image's internal filename. - * @param iiifUrl the image's IIIF URL. - * @param width the image's width in pixels. - * @param height the image's height in pixels. - */ + * Represents the information that Knora returns about an image file value that was created. + * + * @param internalFilename the image's internal filename. + * @param iiifUrl the image's IIIF URL. + * @param width the image's width in pixels. + * @param height the image's height in pixels. + */ case class SavedImage(internalFilename: String, iiifUrl: String, width: Int, height: Int) /** - * Represents the information that Knora returns about a document file value that was created. - * - * @param internalFilename the files's internal filename. - * @param url the file's URL. - * @param pageCount the document's page count. - * @param width the document's width in pixels. - * @param height the document's height in pixels. - */ - case class SavedDocument(internalFilename: String, - url: String, - pageCount: Option[Int], - width: Option[Int], - height: Option[Int]) + * Represents the information that Knora returns about a document file value that was created. + * + * @param internalFilename the files's internal filename. + * @param url the file's URL. + * @param pageCount the document's page count. + * @param width the document's width in pixels. + * @param height the document's height in pixels. + */ + case class SavedDocument( + internalFilename: String, + url: String, + pageCount: Option[Int], + width: Option[Int], + height: Option[Int] + ) /** - * Represents the information that Knora returns about a text file value that was created. - * - * @param internalFilename the file's internal filename. - * @param url the file's URL. - */ + * Represents the information that Knora returns about a text file value that was created. + * + * @param internalFilename the file's internal filename. + * @param url the file's URL. + */ case class SavedTextFile(internalFilename: String, url: String) /** - * Represents the information that Knora returns about an audio file value that was created. - * - * @param internalFilename the file's internal filename. - * @param url the file's URL. - * @param duration the duration of the audio in seconds. - */ + * Represents the information that Knora returns about an audio file value that was created. + * + * @param internalFilename the file's internal filename. + * @param url the file's URL. + * @param duration the duration of the audio in seconds. + */ case class SavedAudioFile(internalFilename: String, url: String, duration: Option[BigDecimal]) /** - * Represents the information that Knora returns about a video file value that was created. - * - * @param internalFilename the file's internal filename. - * @param url the file's URL. - * @param width the video's width in pixels. - * @param height the video's height in pixels. - * @param duration the duration of the video in seconds. - * @param fps the frame rate of the video in seconds. - */ - case class SavedVideoFile(internalFilename: String, - url: String, - dimX: Int, - dimY: Int, - duration: Option[BigDecimal], - fps: Option[BigDecimal]) + * Represents the information that Knora returns about a video file value that was created. + * + * @param internalFilename the file's internal filename. + * @param url the file's URL. + * @param width the video's width in pixels. + * @param height the video's height in pixels. + * @param duration the duration of the video in seconds. + * @param fps the frame rate of the video in seconds. + */ + case class SavedVideoFile( + internalFilename: String, + url: String, + dimX: Int, + dimY: Int, + duration: Option[BigDecimal], + fps: Option[BigDecimal] + ) /** - * Given a JSON-LD document representing a resource, returns a JSON-LD array containing the values of the specified - * property. - * - * @param resource the JSON-LD document. - * @param propertyIriInResult the property IRI. - * @return a JSON-LD array containing the values of the specified property. - */ - private def getValuesFromResource(resource: JsonLDDocument, propertyIriInResult: SmartIri): JsonLDArray = { + * Given a JSON-LD document representing a resource, returns a JSON-LD array containing the values of the specified + * property. + * + * @param resource the JSON-LD document. + * @param propertyIriInResult the property IRI. + * @return a JSON-LD array containing the values of the specified property. + */ + private def getValuesFromResource(resource: JsonLDDocument, propertyIriInResult: SmartIri): JsonLDArray = resource.requireArray(propertyIriInResult.toString) - } /** - * Given a JSON-LD document representing a resource, returns a JSON-LD object representing the expected single - * value of the specified property. - * - * @param resource the JSON-LD document. - * @param propertyIriInResult the property IRI. - * @param expectedValueIri the IRI of the expected value. - * @return a JSON-LD object representing the expected single value of the specified property. - */ - private def getValueFromResource(resource: JsonLDDocument, - propertyIriInResult: SmartIri, - expectedValueIri: IRI): JsonLDObject = { + * Given a JSON-LD document representing a resource, returns a JSON-LD object representing the expected single + * value of the specified property. + * + * @param resource the JSON-LD document. + * @param propertyIriInResult the property IRI. + * @param expectedValueIri the IRI of the expected value. + * @return a JSON-LD object representing the expected single value of the specified property. + */ + private def getValueFromResource( + resource: JsonLDDocument, + propertyIriInResult: SmartIri, + expectedValueIri: IRI + ): JsonLDObject = { val resourceIri: IRI = resource.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) val propertyValues: JsonLDArray = getValuesFromResource(resource = resource, propertyIriInResult = propertyIriInResult) val matchingValues: Seq[JsonLDObject] = propertyValues.value.collect { case jsonLDObject: JsonLDObject - if jsonLDObject.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) == expectedValueIri => + if jsonLDObject.requireStringWithValidation( + JsonLDKeywords.ID, + stringFormatter.validateAndEscapeIri + ) == expectedValueIri => jsonLDObject } if (matchingValues.isEmpty) { throw AssertionException( - s"Property <$propertyIriInResult> of resource <$resourceIri> does not have value <$expectedValueIri>") + s"Property <$propertyIriInResult> of resource <$resourceIri> does not have value <$expectedValueIri>" + ) } if (matchingValues.size > 1) { throw AssertionException( - s"Property <$propertyIriInResult> of resource <$resourceIri> has more than one value with the IRI <$expectedValueIri>") + s"Property <$propertyIriInResult> of resource <$resourceIri> has more than one value with the IRI <$expectedValueIri>" + ) } matchingValues.head } /** - * Given a JSON-LD object representing a Knora image file value, returns a [[SavedImage]] containing the same information. - * - * @param savedValue a JSON-LD object representing a Knora image file value. - * @return a [[SavedImage]] containing the same information. - */ + * Given a JSON-LD object representing a Knora image file value, returns a [[SavedImage]] containing the same information. + * + * @param savedValue a JSON-LD object representing a Knora image file value. + * @return a [[SavedImage]] containing the same information. + */ private def savedValueToSavedImage(savedValue: JsonLDObject): SavedImage = { val internalFilename = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.FileValueHasFilename) @@ -261,11 +271,11 @@ class KnoraSipiIntegrationV2ITSpec } /** - * Given a JSON-LD object representing a Knora document file value, returns a [[SavedDocument]] containing the same information. - * - * @param savedValue a JSON-LD object representing a Knora document file value. - * @return a [[SavedDocument]] containing the same information. - */ + * Given a JSON-LD object representing a Knora document file value, returns a [[SavedDocument]] containing the same information. + * + * @param savedValue a JSON-LD object representing a Knora document file value. + * @return a [[SavedDocument]] containing the same information. + */ private def savedValueToSavedDocument(savedValue: JsonLDObject): SavedDocument = { val internalFilename = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.FileValueHasFilename) @@ -289,11 +299,11 @@ class KnoraSipiIntegrationV2ITSpec } /** - * Given a JSON-LD object representing a Knora text file value, returns a [[SavedTextFile]] containing the same information. - * - * @param savedValue a JSON-LD object representing a Knora document file value. - * @return a [[SavedTextFile]] containing the same information. - */ + * Given a JSON-LD object representing a Knora text file value, returns a [[SavedTextFile]] containing the same information. + * + * @param savedValue a JSON-LD object representing a Knora document file value. + * @return a [[SavedTextFile]] containing the same information. + */ private def savedValueToSavedTextFile(savedValue: JsonLDObject): SavedTextFile = { val internalFilename = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.FileValueHasFilename) @@ -310,11 +320,11 @@ class KnoraSipiIntegrationV2ITSpec } /** - * Given a JSON-LD object representing a Knora audio file value, returns a [[SavedAudioFile]] containing the same information. - * - * @param savedValue a JSON-LD object representing a Knora audio file value. - * @return a [[SavedAudioFile]] containing the same information. - */ + * Given a JSON-LD object representing a Knora audio file value, returns a [[SavedAudioFile]] containing the same information. + * + * @param savedValue a JSON-LD object representing a Knora audio file value. + * @return a [[SavedAudioFile]] containing the same information. + */ private def savedValueToSavedAudioFile(savedValue: JsonLDObject): SavedAudioFile = { val internalFilename = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.FileValueHasFilename) @@ -338,11 +348,11 @@ class KnoraSipiIntegrationV2ITSpec } /** - * Given a JSON-LD object representing a Knora video file value, returns a [[SavedVideoFile]] containing the same information. - * - * @param savedValue a JSON-LD object representing a Knora video file value. - * @return a [[SavedVideoFile]] containing the same information. - */ + * Given a JSON-LD object representing a Knora video file value, returns a [[SavedVideoFile]] containing the same information. + * + * @param savedValue a JSON-LD object representing a Knora video file value. + * @return a [[SavedVideoFile]] containing the same information. + */ private def savedValueToSavedVideoFile(savedValue: JsonLDObject): SavedVideoFile = { val internalFilename = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.FileValueHasFilename) @@ -408,10 +418,10 @@ class KnoraSipiIntegrationV2ITSpec val params = s""" - |{ - | "email": "$anythingUserEmail", - | "password": "$password" - |} + |{ + | "email": "$anythingUserEmail", + | "password": "$password" + |} """.stripMargin val request = Post(baseApiUrl + s"/v2/authentication", HttpEntity(ContentTypes.`application/json`, params)) @@ -441,26 +451,28 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@type" : "anything:ThingPicture", - | "knora-api:hasStillImageFileValue" : { - | "@type" : "knora-api:StillImageFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type" : "anything:ThingPicture", + | "knora-api:hasStillImageFileValue" : { + | "@type" : "knora-api:StillImageFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) stillImageResourceIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -468,7 +480,8 @@ class KnoraSipiIntegrationV2ITSpec val knoraGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(stillImageResourceIri.get, "UTF-8")}") val resource: JsonLDDocument = getResponseJsonLD(knoraGetRequest) assert( - resource.requireTypeAsKnoraTypeIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#ThingPicture") + resource.requireTypeAsKnoraTypeIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#ThingPicture" + ) // Get the new file value from the resource. @@ -521,23 +534,25 @@ class KnoraSipiIntegrationV2ITSpec // JSON describing the new image to Knora. val jsonLdEntity = s"""{ - | "@id" : "${stillImageResourceIri.get}", - | "@type" : "anything:ThingPicture", - | "knora-api:hasStillImageFileValue" : { - | "@id" : "${stillImageFileValueIri.get}", - | "@type" : "knora-api:StillImageFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}", - | "knora-api:hasPermissions" : "CR knora-admin:Creator|V knora-admin:UnknownUser" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "${stillImageResourceIri.get}", + | "@type" : "anything:ThingPicture", + | "knora-api:hasStillImageFileValue" : { + | "@id" : "${stillImageFileValueIri.get}", + | "@type" : "knora-api:StillImageFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}", + | "knora-api:hasPermissions" : "CR knora-admin:Creator|V knora-admin:UnknownUser" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin // Send the JSON in a PUT request to Knora. - val knoraPostRequest = Put(baseApiUrl + "/v2/values", HttpEntity(ContentTypes.`application/json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val knoraPostRequest = + Put(baseApiUrl + "/v2/values", HttpEntity(ContentTypes.`application/json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc = getResponseJsonLD(knoraPostRequest) stillImageFileValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -577,21 +592,23 @@ class KnoraSipiIntegrationV2ITSpec // JSON describing the new image to Knora. val jsonLdEntity = s"""{ - | "@id" : "${stillImageResourceIri.get}", - | "@type" : "anything:ThingDocument", - | "knora-api:hasStillImageFileValue" : { - | "@type" : "knora-api:StillImageFileValue", - | "knora-api:fileValueHasFilename" : "$internalFilename" - | }, - | "@context" : { - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin + | "@id" : "${stillImageResourceIri.get}", + | "@type" : "anything:ThingDocument", + | "knora-api:hasStillImageFileValue" : { + | "@type" : "knora-api:StillImageFileValue", + | "knora-api:fileValueHasFilename" : "$internalFilename" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin // Send the JSON in a POST request to Knora. - val knoraPostRequest = Post(baseApiUrl + "/v2/values", HttpEntity(ContentTypes.`application/json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(incunabulaUserEmail, password)) + val knoraPostRequest = + Post(baseApiUrl + "/v2/values", HttpEntity(ContentTypes.`application/json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(incunabulaUserEmail, password) + ) val knoraPostResponse = singleAwaitingRequest(knoraPostRequest) assert(knoraPostResponse.status == StatusCodes.Forbidden) @@ -615,26 +632,28 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@type" : "anything:ThingDocument", - | "knora-api:hasDocumentFileValue" : { - | "@type" : "knora-api:DocumentFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "test thing", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type" : "anything:ThingDocument", + | "knora-api:hasDocumentFileValue" : { + | "@type" : "knora-api:DocumentFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "test thing", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) pdfResourceIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -642,7 +661,8 @@ class KnoraSipiIntegrationV2ITSpec val knoraGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(pdfResourceIri.get, "UTF-8")}") val resource: JsonLDDocument = getResponseJsonLD(knoraGetRequest) assert( - resource.requireTypeAsKnoraTypeIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#ThingDocument") + resource.requireTypeAsKnoraTypeIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#ThingDocument" + ) // Get the new file value from the resource. @@ -690,24 +710,26 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@id" : "${pdfResourceIri.get}", - | "@type" : "anything:ThingDocument", - | "knora-api:hasDocumentFileValue" : { - | "@id" : "${pdfValueIri.get}", - | "@type" : "knora-api:DocumentFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - - val request = Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "${pdfResourceIri.get}", + | "@type" : "anything:ThingDocument", + | "knora-api:hasDocumentFileValue" : { + | "@id" : "${pdfValueIri.get}", + | "@type" : "knora-api:DocumentFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + + val request = + Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) pdfValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -748,25 +770,27 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@type" : "knora-api:TextRepresentation", - | "knora-api:hasTextFileValue" : { - | "@type" : "knora-api:TextFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "text file", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type" : "knora-api:TextRepresentation", + | "knora-api:hasTextFileValue" : { + | "@type" : "knora-api:TextFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "text file", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) val resourceIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) @@ -819,23 +843,25 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@id" : "${csvResourceIri.get}", - | "@type" : "knora-api:TextRepresentation", - | "knora-api:hasTextFileValue" : { - | "@id" : "${csvValueIri.get}", - | "@type" : "knora-api:TextFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin - - val request = Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "${csvResourceIri.get}", + | "@type" : "knora-api:TextRepresentation", + | "knora-api:hasTextFileValue" : { + | "@id" : "${csvValueIri.get}", + | "@type" : "knora-api:TextFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + val request = + Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) csvValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -873,25 +899,27 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@type" : "knora-api:StillImageRepresentation", - | "knora-api:hasStillImageValue" : { - | "@type" : "knora-api:StillImageFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "still image file", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type" : "knora-api:StillImageRepresentation", + | "knora-api:hasStillImageValue" : { + | "@type" : "knora-api:StillImageFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "still image file", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val response = singleAwaitingRequest(request) assert(response.status == StatusCodes.BadRequest) } @@ -911,25 +939,27 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@type" : "knora-api:TextRepresentation", - | "knora-api:hasTextFileValue" : { - | "@type" : "knora-api:TextFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/0001" - | }, - | "rdfs:label" : "text file", - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin - - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@type" : "knora-api:TextRepresentation", + | "knora-api:hasTextFileValue" : { + | "@type" : "knora-api:TextFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/0001" + | }, + | "rdfs:label" : "text file", + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) val resourceIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) @@ -982,23 +1012,25 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ - | "@id" : "${xmlResourceIri.get}", - | "@type" : "knora-api:TextRepresentation", - | "knora-api:hasTextFileValue" : { - | "@id" : "${xmlValueIri.get}", - | "@type" : "knora-api:TextFileValue", - | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin - - val request = Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + | "@id" : "${xmlResourceIri.get}", + | "@type" : "knora-api:TextRepresentation", + | "knora-api:hasTextFileValue" : { + | "@id" : "${xmlValueIri.get}", + | "@type" : "knora-api:TextFileValue", + | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + val request = + Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) xmlValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -1053,8 +1085,10 @@ class KnoraSipiIntegrationV2ITSpec | } |}""".stripMargin - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) zipResourceIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -1062,7 +1096,8 @@ class KnoraSipiIntegrationV2ITSpec val knoraGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(zipResourceIri.get, "UTF-8")}") val resource: JsonLDDocument = getResponseJsonLD(knoraGetRequest) assert( - resource.requireTypeAsKnoraTypeIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#ThingDocument") + resource.requireTypeAsKnoraTypeIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#ThingDocument" + ) // Get the new file value from the resource. @@ -1123,8 +1158,10 @@ class KnoraSipiIntegrationV2ITSpec | } |}""".stripMargin - val request = Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) zipValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -1179,8 +1216,10 @@ class KnoraSipiIntegrationV2ITSpec | } |}""".stripMargin - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) wavResourceIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -1188,7 +1227,8 @@ class KnoraSipiIntegrationV2ITSpec val knoraGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(wavResourceIri.get, "UTF-8")}") val resource: JsonLDDocument = getResponseJsonLD(knoraGetRequest) assert( - resource.requireTypeAsKnoraTypeIri.toString == "http://api.knora.org/ontology/knora-api/v2#AudioRepresentation") + resource.requireTypeAsKnoraTypeIri.toString == "http://api.knora.org/ontology/knora-api/v2#AudioRepresentation" + ) // Get the new file value from the resource. @@ -1234,10 +1274,10 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ | "@id" : "${wavResourceIri.get}", - | "@type" : "knora-api:AudioRepresentation", + | "@type" : "knora-api:AudioRepresentation", | "knora-api:hasAudioFileValue" : { | "@type" : "knora-api:AudioFileValue", - | "@id" : "${wavValueIri.get}", + | "@id" : "${wavValueIri.get}", | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" | }, | "@context" : { @@ -1248,8 +1288,10 @@ class KnoraSipiIntegrationV2ITSpec | } |}""".stripMargin - val request = Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) wavValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -1304,8 +1346,10 @@ class KnoraSipiIntegrationV2ITSpec | } |}""".stripMargin - val request = Post(s"$baseApiUrl/v2/resources", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = Post( + s"$baseApiUrl/v2/resources", + HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity) + ) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password)) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) videoResourceIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) @@ -1313,7 +1357,8 @@ class KnoraSipiIntegrationV2ITSpec val knoraGetRequest = Get(s"$baseApiUrl/v2/resources/${URLEncoder.encode(videoResourceIri.get, "UTF-8")}") val resource: JsonLDDocument = getResponseJsonLD(knoraGetRequest) assert( - resource.requireTypeAsKnoraTypeIri.toString == "http://api.knora.org/ontology/knora-api/v2#MovingImageRepresentation") + resource.requireTypeAsKnoraTypeIri.toString == "http://api.knora.org/ontology/knora-api/v2#MovingImageRepresentation" + ) // Get the new file value from the resource. @@ -1358,10 +1403,10 @@ class KnoraSipiIntegrationV2ITSpec val jsonLdEntity = s"""{ | "@id" : "${videoResourceIri.get}", - | "@type" : "knora-api:MovingImageRepresentation", + | "@type" : "knora-api:MovingImageRepresentation", | "knora-api:hasMovingImageFileValue" : { | "@type" : "knora-api:MovingImageFileValue", - | "@id" : "${videoValueIri.get}", + | "@id" : "${videoValueIri.get}", | "knora-api:fileValueHasFilename" : "${uploadedFile.internalFilename}" | }, | "@context" : { @@ -1372,8 +1417,10 @@ class KnoraSipiIntegrationV2ITSpec | } |}""".stripMargin - val request = Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( - BasicHttpCredentials(anythingUserEmail, password)) + val request = + Put(s"$baseApiUrl/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password) + ) val responseJsonDoc: JsonLDDocument = getResponseJsonLD(request) videoValueIri.set(responseJsonDoc.body.requireIDAsKnoraDataIri.toString) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala index cd74c058b6..3395ded380 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala @@ -29,8 +29,8 @@ import org.knora.webapi.messages.StringFormatter.SalsahGuiAttributeDefinition import org.knora.webapi.sharedtestdata.{SharedOntologyTestDataADM, SharedTestDataADM, SharedTestDataV1} /** - * Tests [[StringFormatter]]. - */ + * Tests [[StringFormatter]]. + */ class StringFormatterSpec extends CoreSpec() { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -155,7 +155,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.isEmpty) + internalOntologyIri.getProjectCode.isEmpty + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Simple) externalOntologyIri.toString should ===("http://api.knora.org/ontology/knora-api/simple/v2") @@ -163,7 +164,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Simple) && externalOntologyIri.isKnoraOntologyIri && externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.isEmpty) + externalOntologyIri.getProjectCode.isEmpty + ) } "convert http://www.knora.org/ontology/knora-base#Resource to http://api.knora.org/ontology/knora-api/simple/v2#Resource" in { @@ -172,7 +174,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.isEmpty) + internalEntityIri.getProjectCode.isEmpty + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Simple) externalEntityIri.toString should ===("http://api.knora.org/ontology/knora-api/simple/v2#Resource") @@ -180,7 +183,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Simple) && externalEntityIri.isKnoraApiV2EntityIri && externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.isEmpty) + externalEntityIri.getProjectCode.isEmpty + ) } "convert http://www.knora.org/ontology/knora-base to http://api.knora.org/ontology/knora-api/v2" in { @@ -189,7 +193,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.isEmpty) + internalOntologyIri.getProjectCode.isEmpty + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Complex) externalOntologyIri.toString should ===("http://api.knora.org/ontology/knora-api/v2") @@ -197,7 +202,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Complex) && externalOntologyIri.isKnoraOntologyIri && externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.isEmpty) + externalOntologyIri.getProjectCode.isEmpty + ) } "convert http://www.knora.org/ontology/knora-base#Resource to http://api.knora.org/ontology/knora-api/v2#Resource" in { @@ -206,7 +212,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.isEmpty) + internalEntityIri.getProjectCode.isEmpty + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Complex) externalEntityIri.toString should ===("http://api.knora.org/ontology/knora-api/v2#Resource") @@ -214,7 +221,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Complex) && externalEntityIri.isKnoraApiV2EntityIri && externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.isEmpty) + externalEntityIri.getProjectCode.isEmpty + ) } "convert http://api.knora.org/ontology/knora-api/simple/v2 to http://www.knora.org/ontology/knora-base" in { @@ -223,7 +231,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Simple) && externalOntologyIri.isKnoraOntologyIri && externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.isEmpty) + externalOntologyIri.getProjectCode.isEmpty + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/knora-base") @@ -231,7 +240,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.isEmpty) + internalOntologyIri.getProjectCode.isEmpty + ) } "convert http://api.knora.org/ontology/knora-api/simple/v2#Resource to http://www.knora.org/ontology/knora-base#Resource" in { @@ -240,7 +250,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Simple) && externalEntityIri.isKnoraApiV2EntityIri && externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.isEmpty) + externalEntityIri.getProjectCode.isEmpty + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/knora-base#Resource") @@ -248,7 +259,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.isEmpty) + internalEntityIri.getProjectCode.isEmpty + ) } "convert http://api.knora.org/ontology/knora-api/v2 to http://www.knora.org/ontology/knora-base" in { @@ -257,7 +269,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Complex) && externalOntologyIri.isKnoraOntologyIri && externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.isEmpty) + externalOntologyIri.getProjectCode.isEmpty + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/knora-base") @@ -265,7 +278,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.isEmpty) + internalOntologyIri.getProjectCode.isEmpty + ) } "convert http://api.knora.org/ontology/knora-api/v2#Resource to http://www.knora.org/ontology/knora-base#Resource" in { @@ -274,7 +288,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Complex) && externalEntityIri.isKnoraApiV2EntityIri && externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.isEmpty) + externalEntityIri.getProjectCode.isEmpty + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/knora-base#Resource") @@ -282,7 +297,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.isEmpty) + internalEntityIri.getProjectCode.isEmpty + ) } ////////////////////////////////////////// @@ -294,7 +310,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.contains("00FF")) + internalOntologyIri.getProjectCode.contains("00FF") + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Simple) externalOntologyIri.toString should ===("http://0.0.0.0:3333/ontology/00FF/images/simple/v2") @@ -302,7 +319,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Simple) && externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.contains("00FF")) + externalOntologyIri.getProjectCode.contains("00FF") + ) } "convert http://www.knora.org/ontology/00FF/images#bild to http://0.0.0.0:3333/ontology/00FF/images/simple/v2#bild" in { @@ -311,7 +329,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.contains("00FF")) + internalEntityIri.getProjectCode.contains("00FF") + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Simple) externalEntityIri.toString should ===("http://0.0.0.0:3333/ontology/00FF/images/simple/v2#bild") @@ -319,7 +338,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Simple) && externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.contains("00FF")) + externalEntityIri.getProjectCode.contains("00FF") + ) } "convert http://www.knora.org/ontology/00FF/images to http://0.0.0.0:3333/ontology/00FF/images/v2" in { @@ -328,7 +348,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.contains("00FF")) + internalOntologyIri.getProjectCode.contains("00FF") + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Complex) externalOntologyIri.toString should ===("http://0.0.0.0:3333/ontology/00FF/images/v2") @@ -336,7 +357,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Complex) && externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.contains("00FF")) + externalOntologyIri.getProjectCode.contains("00FF") + ) } "convert http://www.knora.org/ontology/00FF/images#bild to http://0.0.0.0:3333/ontology/00FF/images/v2#bild" in { @@ -345,7 +367,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.contains("00FF")) + internalEntityIri.getProjectCode.contains("00FF") + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Complex) externalEntityIri.toString should ===("http://0.0.0.0:3333/ontology/00FF/images/v2#bild") @@ -353,7 +376,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Complex) && externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.contains("00FF")) + externalEntityIri.getProjectCode.contains("00FF") + ) } "convert http://0.0.0.0:3333/ontology/00FF/images/simple/v2 to http://www.knora.org/ontology/00FF/images" in { @@ -362,7 +386,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Simple) && externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.contains("00FF")) + externalOntologyIri.getProjectCode.contains("00FF") + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/00FF/images") @@ -370,7 +395,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.contains("00FF")) + internalOntologyIri.getProjectCode.contains("00FF") + ) } "convert http://0.0.0.0:3333/ontology/00FF/images/simple/v2#bild to http://www.knora.org/ontology/00FF/images#bild" in { @@ -379,7 +405,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Simple) && externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.contains("00FF")) + externalEntityIri.getProjectCode.contains("00FF") + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/00FF/images#bild") @@ -387,7 +414,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.contains("00FF")) + internalEntityIri.getProjectCode.contains("00FF") + ) } "convert http://0.0.0.0:3333/ontology/00FF/images/v2 to http://www.knora.org/ontology/00FF/images" in { @@ -396,7 +424,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.getOntologySchema.contains(ApiV2Complex) && externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && - externalOntologyIri.getProjectCode.contains("00FF")) + externalOntologyIri.getProjectCode.contains("00FF") + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/00FF/images") @@ -404,7 +433,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.getOntologySchema.contains(InternalSchema) && internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && - internalOntologyIri.getProjectCode.contains("00FF")) + internalOntologyIri.getProjectCode.contains("00FF") + ) } "convert http://0.0.0.0:3333/ontology/00FF/images/v2#bild to http://www.knora.org/ontology/00FF/images#bild" in { @@ -413,7 +443,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Complex) && externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && - externalEntityIri.getProjectCode.contains("00FF")) + externalEntityIri.getProjectCode.contains("00FF") + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/00FF/images#bild") @@ -421,7 +452,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.contains("00FF")) + internalEntityIri.getProjectCode.contains("00FF") + ) } "convert http://www.knora.org/ontology/knora-base#TextValue to http://www.w3.org/2001/XMLSchema#string" in { @@ -430,7 +462,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && internalEntityIri.isKnoraInternalEntityIri && internalEntityIri.isKnoraBuiltInDefinitionIri && - internalEntityIri.getProjectCode.isEmpty) + internalEntityIri.getProjectCode.isEmpty + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Simple) assert(externalEntityIri.toString == "http://www.w3.org/2001/XMLSchema#string" && !externalEntityIri.isKnoraIri) @@ -446,7 +479,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0000")) + internalOntologyIri.getProjectCode.contains("0000") + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Simple) externalOntologyIri.toString should ===("http://api.knora.org/ontology/shared/example/simple/v2") @@ -455,7 +489,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0000")) + externalOntologyIri.getProjectCode.contains("0000") + ) } "convert http://www.knora.org/ontology/shared/example#Person to http://api.knora.org/ontology/shared/example/simple/v2#Person" in { @@ -464,7 +499,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0000")) + internalEntityIri.getProjectCode.contains("0000") + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Simple) externalEntityIri.toString should ===("http://api.knora.org/ontology/shared/example/simple/v2#Person") @@ -473,7 +509,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0000")) + externalEntityIri.getProjectCode.contains("0000") + ) } "convert http://www.knora.org/ontology/shared/example to http://api.knora.org/ontology/shared/example/v2" in { @@ -483,7 +520,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0000")) + internalOntologyIri.getProjectCode.contains("0000") + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Complex) externalOntologyIri.toString should ===("http://api.knora.org/ontology/shared/example/v2") @@ -492,7 +530,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0000")) + externalOntologyIri.getProjectCode.contains("0000") + ) } "convert http://www.knora.org/ontology/shared/example#Person to http://api.knora.org/ontology/shared/example/v2#Person" in { @@ -501,7 +540,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0000")) + internalEntityIri.getProjectCode.contains("0000") + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Complex) externalEntityIri.toString should ===("http://api.knora.org/ontology/shared/example/v2#Person") @@ -510,7 +550,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0000")) + externalEntityIri.getProjectCode.contains("0000") + ) } "convert http://api.knora.org/ontology/shared/example/simple/v2 to http://www.knora.org/ontology/shared/example" in { @@ -520,7 +561,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0000")) + externalOntologyIri.getProjectCode.contains("0000") + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/shared/example") @@ -529,7 +571,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0000")) + internalOntologyIri.getProjectCode.contains("0000") + ) } "convert http://api.knora.org/ontology/shared/example/simple/v2#Person to http://www.knora.org/ontology/shared/example#Person" in { @@ -538,7 +581,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Simple) && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0000")) + externalEntityIri.getProjectCode.contains("0000") + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/shared/example#Person") @@ -546,7 +590,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0000")) + internalEntityIri.getProjectCode.contains("0000") + ) } "convert http://api.knora.org/ontology/shared/example/v2 to http://www.knora.org/ontology/shared/example" in { @@ -556,7 +601,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0000")) + externalOntologyIri.getProjectCode.contains("0000") + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/shared/example") @@ -565,7 +611,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0000")) + internalOntologyIri.getProjectCode.contains("0000") + ) } "convert http://api.knora.org/ontology/shared/example/v2#Person to http://www.knora.org/ontology/shared/example#Person" in { @@ -574,7 +621,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Complex) && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0000")) + externalEntityIri.getProjectCode.contains("0000") + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/shared/example#Person") @@ -582,7 +630,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0000")) + internalEntityIri.getProjectCode.contains("0000") + ) } /////////////////////////////////////////////////////////////// @@ -595,7 +644,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0111")) + internalOntologyIri.getProjectCode.contains("0111") + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Simple) externalOntologyIri.toString should ===("http://api.knora.org/ontology/shared/0111/example/simple/v2") @@ -604,7 +654,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0111")) + externalOntologyIri.getProjectCode.contains("0111") + ) } "convert http://www.knora.org/ontology/shared/0111/example#Person to http://api.knora.org/ontology/shared/0111/example/simple/v2#Person" in { @@ -613,7 +664,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0111")) + internalEntityIri.getProjectCode.contains("0111") + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Simple) externalEntityIri.toString should ===("http://api.knora.org/ontology/shared/0111/example/simple/v2#Person") @@ -622,7 +674,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0111")) + externalEntityIri.getProjectCode.contains("0111") + ) } "convert http://www.knora.org/ontology/shared/0111/example to http://api.knora.org/ontology/shared/0111/example/v2" in { @@ -632,7 +685,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0111")) + internalOntologyIri.getProjectCode.contains("0111") + ) val externalOntologyIri = internalOntologyIri.toOntologySchema(ApiV2Complex) externalOntologyIri.toString should ===("http://api.knora.org/ontology/shared/0111/example/v2") @@ -641,7 +695,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0111")) + externalOntologyIri.getProjectCode.contains("0111") + ) } "convert http://www.knora.org/ontology/shared/0111/example#Person to http://api.knora.org/ontology/shared/0111/example/v2#Person" in { @@ -650,7 +705,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0111")) + internalEntityIri.getProjectCode.contains("0111") + ) val externalEntityIri = internalEntityIri.toOntologySchema(ApiV2Complex) externalEntityIri.toString should ===("http://api.knora.org/ontology/shared/0111/example/v2#Person") @@ -659,7 +715,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.isKnoraApiV2EntityIri && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0111")) + externalEntityIri.getProjectCode.contains("0111") + ) } "convert http://api.knora.org/ontology/shared/0111/example/simple/v2 to http://www.knora.org/ontology/shared/0111/example" in { @@ -669,7 +726,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0111")) + externalOntologyIri.getProjectCode.contains("0111") + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/shared/0111/example") @@ -678,7 +736,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0111")) + internalOntologyIri.getProjectCode.contains("0111") + ) } "convert http://api.knora.org/ontology/shared/0111/example/simple/v2#Person to http://www.knora.org/ontology/shared/0111/example#Person" in { @@ -687,7 +746,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Simple) && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0111")) + externalEntityIri.getProjectCode.contains("0111") + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/shared/0111/example#Person") @@ -695,7 +755,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.getOntologySchema.contains(InternalSchema) && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0111")) + internalEntityIri.getProjectCode.contains("0111") + ) } "convert http://api.knora.org/ontology/shared/0111/example/v2 to http://www.knora.org/ontology/shared/0111/example" in { @@ -705,7 +766,8 @@ class StringFormatterSpec extends CoreSpec() { externalOntologyIri.isKnoraOntologyIri && !externalOntologyIri.isKnoraBuiltInDefinitionIri && externalOntologyIri.isKnoraSharedDefinitionIri && - externalOntologyIri.getProjectCode.contains("0111")) + externalOntologyIri.getProjectCode.contains("0111") + ) val internalOntologyIri = externalOntologyIri.toOntologySchema(InternalSchema) internalOntologyIri.toString should ===("http://www.knora.org/ontology/shared/0111/example") @@ -714,7 +776,8 @@ class StringFormatterSpec extends CoreSpec() { internalOntologyIri.isKnoraOntologyIri && !internalOntologyIri.isKnoraBuiltInDefinitionIri && internalOntologyIri.isKnoraSharedDefinitionIri && - internalOntologyIri.getProjectCode.contains("0111")) + internalOntologyIri.getProjectCode.contains("0111") + ) } "convert http://api.knora.org/ontology/shared/0111/example/v2#Person to http://www.knora.org/ontology/shared/0111/example#Person" in { @@ -723,7 +786,8 @@ class StringFormatterSpec extends CoreSpec() { externalEntityIri.getOntologySchema.contains(ApiV2Complex) && !externalEntityIri.isKnoraBuiltInDefinitionIri && externalEntityIri.isKnoraSharedDefinitionIri && - externalEntityIri.getProjectCode.contains("0111")) + externalEntityIri.getProjectCode.contains("0111") + ) val internalEntityIri = externalEntityIri.toOntologySchema(InternalSchema) internalEntityIri.toString should ===("http://www.knora.org/ontology/shared/0111/example#Person") @@ -731,7 +795,8 @@ class StringFormatterSpec extends CoreSpec() { internalEntityIri.isKnoraInternalEntityIri && !internalEntityIri.isKnoraBuiltInDefinitionIri && internalEntityIri.isKnoraSharedDefinitionIri && - internalEntityIri.getProjectCode.contains("0111")) + internalEntityIri.getProjectCode.contains("0111") + ) } ///////////////////// @@ -770,7 +835,8 @@ class StringFormatterSpec extends CoreSpec() { !xsdIri.isKnoraOntologyIri && !xsdIri.isKnoraDataIri && xsdIri.getOntologySchema.isEmpty && - xsdIri.getProjectCode.isEmpty) + xsdIri.getProjectCode.isEmpty + ) } "validate import namespace with project shortcode" in { @@ -875,7 +941,8 @@ class StringFormatterSpec extends CoreSpec() { "reject http://api.knora.org/ontology/00FF/images/simple/v2 (wrong hostname)" in { assertThrows[AssertionException] { "http://api.knora.org/ontology/00FF/images/simple/v2".toSmartIriWithErr( - throw AssertionException(s"Invalid IRI")) + throw AssertionException(s"Invalid IRI") + ) } } @@ -994,15 +1061,20 @@ class StringFormatterSpec extends CoreSpec() { // check consistency of our test data stringFormatter.projectDataNamedGraphV2(SharedTestDataADM.anythingProject) should be( - SharedOntologyTestDataADM.ANYTHING_DATA_IRI) + SharedOntologyTestDataADM.ANYTHING_DATA_IRI + ) stringFormatter.projectDataNamedGraphV2(SharedTestDataADM.imagesProject) should be( - SharedOntologyTestDataADM.IMAGES_DATA_IRI) + SharedOntologyTestDataADM.IMAGES_DATA_IRI + ) stringFormatter.projectDataNamedGraphV2(SharedTestDataADM.beolProject) should be( - SharedOntologyTestDataADM.BEOL_DATA_IRI) + SharedOntologyTestDataADM.BEOL_DATA_IRI + ) stringFormatter.projectDataNamedGraphV2(SharedTestDataADM.incunabulaProject) should be( - SharedOntologyTestDataADM.INCUNABULA_DATA_IRI) + SharedOntologyTestDataADM.INCUNABULA_DATA_IRI + ) stringFormatter.projectDataNamedGraphV2(SharedTestDataADM.dokubibProject) should be( - SharedOntologyTestDataADM.DOKUBIB_DATA_IRI) + SharedOntologyTestDataADM.DOKUBIB_DATA_IRI + ) } "parse the objects of salsah-gui:guiAttributeDefinition" in { @@ -1162,32 +1234,41 @@ class StringFormatterSpec extends CoreSpec() { "validate project IRI" in { stringFormatter.validateAndEscapeProjectIri( SharedTestDataADM.incunabulaProject.id, - throw AssertionException("not valid")) shouldBe SharedTestDataADM.incunabulaProject.id - stringFormatter.validateAndEscapeOptionalProjectIri(Some(SharedTestDataADM.incunabulaProject.id), - throw AssertionException("not valid")) shouldBe Some( - SharedTestDataADM.incunabulaProject.id) + throw AssertionException("not valid") + ) shouldBe SharedTestDataADM.incunabulaProject.id + stringFormatter.validateAndEscapeOptionalProjectIri( + Some(SharedTestDataADM.incunabulaProject.id), + throw AssertionException("not valid") + ) shouldBe Some(SharedTestDataADM.incunabulaProject.id) stringFormatter.validateAndEscapeProjectIri( SharedTestDataADM.systemProject.id, - throw AssertionException("not valid")) shouldBe SharedTestDataADM.systemProject.id - stringFormatter.validateAndEscapeOptionalProjectIri(Some(SharedTestDataADM.systemProject.id), - throw AssertionException("not valid")) shouldBe Some( - SharedTestDataADM.systemProject.id) + throw AssertionException("not valid") + ) shouldBe SharedTestDataADM.systemProject.id + stringFormatter.validateAndEscapeOptionalProjectIri( + Some(SharedTestDataADM.systemProject.id), + throw AssertionException("not valid") + ) shouldBe Some(SharedTestDataADM.systemProject.id) stringFormatter.validateAndEscapeProjectIri( SharedTestDataADM.defaultSharedOntologiesProject.id, - throw AssertionException("not valid")) shouldBe SharedTestDataADM.defaultSharedOntologiesProject.id - stringFormatter.validateAndEscapeOptionalProjectIri(Some(SharedTestDataADM.defaultSharedOntologiesProject.id), - throw AssertionException("not valid")) shouldBe Some( - SharedTestDataADM.defaultSharedOntologiesProject.id) + throw AssertionException("not valid") + ) shouldBe SharedTestDataADM.defaultSharedOntologiesProject.id + stringFormatter.validateAndEscapeOptionalProjectIri( + Some(SharedTestDataADM.defaultSharedOntologiesProject.id), + throw AssertionException("not valid") + ) shouldBe Some(SharedTestDataADM.defaultSharedOntologiesProject.id) } "validate project shortname" in { // shortname with dash is valid - stringFormatter.validateAndEscapeProjectShortname("valid-shortname", throw AssertionException("not valid")) should be( - "valid-shortname") + stringFormatter.validateAndEscapeProjectShortname( + "valid-shortname", + throw AssertionException("not valid") + ) should be("valid-shortname") // shortname with numbers stringFormatter.validateAndEscapeProjectShortname("valid_1111", throw AssertionException("not valid")) should be( - "valid_1111") + "valid_1111" + ) // has special character colon an[AssertionException] should be thrownBy { stringFormatter.validateAndEscapeProjectShortname("invalid:shortname", throw AssertionException("not valid")) @@ -1235,8 +1316,10 @@ class StringFormatterSpec extends CoreSpec() { stringFormatter.validateAndEscapeUsername("abc", throw AssertionException("not valid")) } an[AssertionException] should be thrownBy { - stringFormatter.validateAndEscapeUsername("123456789012345678901234567890123456789012345678901", - throw AssertionException("not valid")) + stringFormatter.validateAndEscapeUsername( + "123456789012345678901234567890123456789012345678901", + throw AssertionException("not valid") + ) } // only contain alphanumeric, underscore, and dot @@ -1278,7 +1361,8 @@ class StringFormatterSpec extends CoreSpec() { "validate email" in { stringFormatter.validateEmailAndThrow("donald.duck@example.com", throw AssertionException("not valid")) should be( - "donald.duck@example.com") + "donald.duck@example.com" + ) an[AssertionException] should be thrownBy { stringFormatter.validateEmailAndThrow("donald.duck", throw AssertionException("not valid")) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADMSpec.scala index 776ae2f718..d0fc3547e2 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADMSpec.scala @@ -32,8 +32,8 @@ object GroupsMessagesADMSpec { } /** - * This spec is used to test 'GroupAdminMessages'. - */ + * This spec is used to test 'GroupAdminMessages'. + */ class GroupsMessagesADMSpec extends CoreSpec(GroupsMessagesADMSpec.config) { "The CreateGroupsApiRequestADM case class" should { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala index 788acced4f..66646b77f9 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala @@ -38,8 +38,8 @@ object ListsMessagesADMSpec { } /** - * This spec is used to test 'ListAdminMessages'. - */ + * This spec is used to test 'ListAdminMessages'. + */ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with ListADMJsonProtocol { val exampleListIri = "http://rdfh.ch/lists/00FF/abcd" @@ -52,11 +52,15 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li id = "http://rdfh.ch/lists/73d0ec0302", projectIri = "http://rdfh.ch/projects/00FF", labels = StringLiteralSequenceV2( - Vector(StringLiteralV2("Title", Some("en")), - StringLiteralV2("Titel", Some("de")), - StringLiteralV2("Titre", Some("fr")))), + Vector( + StringLiteralV2("Title", Some("en")), + StringLiteralV2("Titel", Some("de")), + StringLiteralV2("Titre", Some("fr")) + ) + ), comments = StringLiteralSequenceV2( - Vector(StringLiteralV2("Hierarchisches Stichwortverzeichnis / Signatur der Bilder", Some("de")))) + Vector(StringLiteralV2("Hierarchisches Stichwortverzeichnis / Signatur der Bilder", Some("de"))) + ) ) val json = listInfo.toJson.compactPrint @@ -97,7 +101,7 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li comments = StringLiteralSequenceV2(Vector.empty[StringLiteralV2]), children = Seq.empty[ListChildNodeADM], position = 0, - hasRootNode = "http://rdfh.ch/lists/00FF/d19af9ab", + hasRootNode = "http://rdfh.ch/lists/00FF/d19af9ab" ) val json = listNode.toJson.compactPrint @@ -159,11 +163,11 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "projectIri": "", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateListApiRequestADM] @@ -176,11 +180,11 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "projectIri": "not an IRI", - | "labels": [{ "value": "Neue Liste", "language": "de"}], - | "comments": [] - |} + |{ + | "projectIri": "not an IRI", + | "labels": [{ "value": "Neue Liste", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateListApiRequestADM] @@ -192,11 +196,11 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateListApiRequestADM] @@ -209,12 +213,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li // invalid list IRI val payload = s""" - |{ - | "id": "invalid-list-IRI", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "New List", "language": "en"}], - | "comments": [] - |} + |{ + | "id": "invalid-list-IRI", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "New List", "language": "en"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateListApiRequestADM] @@ -226,11 +230,11 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neuer List Node", "language": "de"}, { "value": "", "language": "en"}], - | "comments": [] - |} + |{ + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neuer List Node", "language": "de"}, { "value": "", "language": "en"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateListApiRequestADM] @@ -242,12 +246,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "$exampleListIri", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neuer List Node", "language": "de"}], - | "comments": [{ "value": "", "language": "de"}] - |} + |{ + | "parentNodeIri": "$exampleListIri", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neuer List Node", "language": "de"}], + | "comments": [{ "value": "", "language": "de"}] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateListApiRequestADM] @@ -259,12 +263,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "listIri": "", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodeInfoApiRequestADM] @@ -276,12 +280,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val invalidIri = "notvalidIRI" val payload = s""" - |{ - | "listIri": "$invalidIri", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], - | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] - |} + |{ + | "listIri": "$invalidIri", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}], + | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodeInfoApiRequestADM] @@ -293,11 +297,11 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "listIri": "$exampleListIri", - | "projectIri": "", - | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}] - |} + |{ + | "listIri": "$exampleListIri", + | "projectIri": "", + | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodeInfoApiRequestADM] @@ -309,11 +313,11 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "listIri": "$exampleListIri", - | "projectIri": "notvalidIRI", - | "name": "a new name" - |} + |{ + | "listIri": "$exampleListIri", + | "projectIri": "notvalidIRI", + | "name": "a new name" + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodeInfoApiRequestADM] @@ -325,12 +329,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "listIri": "$exampleListIri", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "listIri": "$exampleListIri", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodeInfoApiRequestADM] @@ -399,12 +403,14 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li Seq( StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")), StringLiteralV2(value = "Changed list", language = Some("en")) - )), + ) + ), comments = Some( Seq( StringLiteralV2(value = "Neuer Kommentar", language = Some("de")), StringLiteralV2(value = "New comment", language = Some("en")) - )) + ) + ) ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser02, @@ -418,12 +424,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neuer List Node", "language": "de"}], - | "comments": [] - |} + |{ + | "parentNodeIri": "", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neuer List Node", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateNodeApiRequestADM] @@ -436,12 +442,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "notvalidIRI", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neuer List Node", "language": "de"}], - | "comments": [] - |} + |{ + | "parentNodeIri": "notvalidIRI", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neuer List Node", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateNodeApiRequestADM] @@ -454,12 +460,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "$exampleListIri", - | "projectIri": "", - | "labels": [{ "value": "Neuer List Node", "language": "de"}], - | "comments": [] - |} + |{ + | "parentNodeIri": "$exampleListIri", + | "projectIri": "", + | "labels": [{ "value": "Neuer List Node", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateNodeApiRequestADM] @@ -472,12 +478,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "$exampleListIri", - | "projectIri": "notvalidIRI", - | "labels": [{ "value": "Neuer List Node", "language": "de"}], - | "comments": [] - |} + |{ + | "parentNodeIri": "$exampleListIri", + | "projectIri": "notvalidIRI", + | "labels": [{ "value": "Neuer List Node", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateNodeApiRequestADM] @@ -490,12 +496,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "$exampleListIri", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [], - | "comments": [] - |} + |{ + | "parentNodeIri": "$exampleListIri", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateNodeApiRequestADM] @@ -508,12 +514,12 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ "id": "invalid-list-node-IRI", - | "parentNodeIri": "$exampleListIri", - | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", - | "labels": [{ "value": "Neuer List Node", "language": "de"}], - | "comments": [] - |} + |{ "id": "invalid-list-node-IRI", + | "parentNodeIri": "$exampleListIri", + | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", + | "labels": [{ "value": "Neuer List Node", "language": "de"}], + | "comments": [] + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[CreateNodeApiRequestADM] @@ -526,10 +532,10 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val payload = s""" - |{ - | "parentNodeIri": "", - | "position": 1 - |} + |{ + | "parentNodeIri": "", + | "position": 1 + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodePositionApiRequestADM] @@ -542,10 +548,10 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val invalid_parentIri = "invalid-iri" val payload = s""" - |{ - | "parentNodeIri": "$invalid_parentIri", - | "position": 1 - |} + |{ + | "parentNodeIri": "$invalid_parentIri", + | "position": 1 + |} """.stripMargin val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodePositionApiRequestADM] diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala index 0e5b14dce4..f5a3ddbadd 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala @@ -31,8 +31,8 @@ import org.knora.webapi.sharedtestdata.SharedTestDataV1._ import org.knora.webapi.sharedtestdata._ /** - * This spec is used to test subclasses of the [[PermissionsResponderRequestADM]] class. - */ + * This spec is used to test subclasses of the [[PermissionsResponderRequestADM]] class. + */ class PermissionsMessagesADMSpec extends CoreSpec() { "Administrative Permission Get Requests" should { @@ -166,7 +166,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { name = invalidName, additionalInformation = None, permissionCode = None - )) + ) + ) val caught = intercept[BadRequestException]( AdministrativePermissionCreateRequestADM( createRequest = CreateAdministrativePermissionAPIRequestADM( @@ -181,7 +182,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { ) assert( caught.getMessage === s"Invalid value for name parameter of hasPermissions: $invalidName, it should be one of " + - s"${AdministrativePermissionAbbreviations.toString}") + s"${AdministrativePermissionAbbreviations.toString}" + ) } "return 'BadRequest' if the a permissions supplied for AdministrativePermissionCreateRequestADM had invalid name" in { @@ -312,7 +314,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { ) ) assert( - caught.getMessage === s"Either a group, a resource class, a property, or a combination of resource class and property must be given.") + caught.getMessage === s"Either a group, a resource class, a property, or a combination of resource class and property must be given." + ) } "return 'ForbiddenException' if requesting user of DefaultObjectAccessPermissionGetRequestADM is not system or project admin" in { @@ -324,7 +327,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { ) ) assert( - caught.getMessage === s"Default object access permissions can only be queried by system and project admin.") + caught.getMessage === s"Default object access permissions can only be queried by system and project admin." + ) } "return 'BadRequest' if the supplied project IRI for DefaultObjectAccessPermissionsForProjectGetRequestADM is not valid" in { @@ -557,7 +561,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { name = invalidName, additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), permissionCode = Some(8) - )) + ) + ) val caught = intercept[BadRequestException]( DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( @@ -568,11 +573,13 @@ class PermissionsMessagesADMSpec extends CoreSpec() { featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.anythingAdminUser, apiRequestID = UUID.randomUUID() - )) + ) + ) assert( caught.getMessage === s"Invalid value for name parameter of hasPermissions: $invalidName, it should be one of " + - s"${EntityPermissionAbbreviations.toString}") + s"${EntityPermissionAbbreviations.toString}" + ) } "not create a DefaultObjectAccessPermission for project and property if hasPermissions set contained permission with invalid code" in { @@ -582,7 +589,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { name = OntologyConstants.KnoraBase.ChangeRightsPermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), permissionCode = Some(invalidCode) - )) + ) + ) val caught = intercept[BadRequestException]( DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( @@ -593,11 +601,13 @@ class PermissionsMessagesADMSpec extends CoreSpec() { featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.anythingAdminUser, apiRequestID = UUID.randomUUID() - )) + ) + ) assert( caught.getMessage === s"Invalid value for permissionCode parameter of hasPermissions: $invalidCode, it should be one of " + - s"${PermissionTypeAndCodes.values.toString}") + s"${PermissionTypeAndCodes.values.toString}" + ) } "not create a DefaultObjectAccessPermission for project and property if hasPermissions set contained permission with inconsistent code and name" in { @@ -608,7 +618,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { name = name, additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), permissionCode = Some(code) - )) + ) + ) val caught = intercept[BadRequestException]( DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( @@ -619,7 +630,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.anythingAdminUser, apiRequestID = UUID.randomUUID() - )) + ) + ) assert(caught.getMessage === s"Given permission code $code and permission name $name are not consistent.") } @@ -630,7 +642,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { name = "", additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), permissionCode = None - )) + ) + ) val caught = intercept[BadRequestException]( DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( @@ -641,10 +654,12 @@ class PermissionsMessagesADMSpec extends CoreSpec() { featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.anythingAdminUser, apiRequestID = UUID.randomUUID() - )) + ) + ) assert( caught.getMessage === - s"One of permission code or permission name must be provided for a default object access permission.") + s"One of permission code or permission name must be provided for a default object access permission." + ) } "not create a DefaultObjectAccessPermission for project and property if hasPermissions set contained permission without additionalInformation parameter" in { @@ -654,7 +669,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { name = OntologyConstants.KnoraBase.ChangeRightsPermission, additionalInformation = None, permissionCode = Some(8) - )) + ) + ) val caught = intercept[BadRequestException]( DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( @@ -665,10 +681,12 @@ class PermissionsMessagesADMSpec extends CoreSpec() { featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.anythingAdminUser, apiRequestID = UUID.randomUUID() - )) + ) + ) assert( caught.getMessage === - s"additionalInformation of a default object access permission type cannot be empty.") + s"additionalInformation of a default object access permission type cannot be empty." + ) } "return 'ForbiddenException' if the user requesting DefaultObjectAccessPermissionCreateRequestADM is not system or project Admin" in { @@ -766,7 +784,8 @@ class PermissionsMessagesADMSpec extends CoreSpec() { ) ) assert( - caught.getMessage === "Either a group, a resource class, a property, or a combination of resource class and property must be given.") + caught.getMessage === "Either a group, a resource class, a property, or a combination of resource class and property must be given." + ) } } @@ -803,9 +822,11 @@ class PermissionsMessagesADMSpec extends CoreSpec() { val projectIri = INCUNABULA_PROJECT_IRI val resourceClassIri = s"$INCUNABULA_ONTOLOGY_IRI#book" - val result = SharedTestDataADM.rootUser.permissions.hasPermissionFor(ResourceCreateOperation(resourceClassIri), - projectIri, - None) + val result = SharedTestDataADM.rootUser.permissions.hasPermissionFor( + ResourceCreateOperation(resourceClassIri), + projectIri, + None + ) result should be(true) } @@ -836,9 +857,11 @@ class PermissionsMessagesADMSpec extends CoreSpec() { val projectIri = INCUNABULA_PROJECT_IRI val resourceClassIri = s"$INCUNABULA_ONTOLOGY_IRI#book" - val result = SharedTestDataADM.normalUser.permissions.hasPermissionFor(ResourceCreateOperation(resourceClassIri), - projectIri, - None) + val result = SharedTestDataADM.normalUser.permissions.hasPermissionFor( + ResourceCreateOperation(resourceClassIri), + projectIri, + None + ) result should be(false) } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala index 0395efea60..8ab95b9825 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala @@ -34,8 +34,8 @@ object ProjectsMessagesADMSpec { } /** - * This spec is used to test subclasses of the [[ProjectsResponderRequestADM]] trait. - */ + * This spec is used to test subclasses of the [[ProjectsResponderRequestADM]] trait. + */ class ProjectsMessagesADMSpec extends CoreSpec(ProjectsMessagesADMSpec.config) { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -183,9 +183,15 @@ class ProjectsMessagesADMSpec extends CoreSpec(ProjectsMessagesADMSpec.config) { "The ProjectIdentifierADM class" should { "return without throwing when the project IRI is valid" in { - ProjectIdentifierADM(maybeIri = Some(SharedTestDataADM.incunabulaProject.id)).value shouldBe SharedTestDataADM.incunabulaProject.id - ProjectIdentifierADM(maybeIri = Some(SharedTestDataADM.defaultSharedOntologiesProject.id)).value shouldBe SharedTestDataADM.defaultSharedOntologiesProject.id - ProjectIdentifierADM(maybeIri = Some(SharedTestDataADM.systemProject.id)).value shouldBe SharedTestDataADM.systemProject.id + ProjectIdentifierADM(maybeIri = + Some(SharedTestDataADM.incunabulaProject.id) + ).value shouldBe SharedTestDataADM.incunabulaProject.id + ProjectIdentifierADM(maybeIri = + Some(SharedTestDataADM.defaultSharedOntologiesProject.id) + ).value shouldBe SharedTestDataADM.defaultSharedOntologiesProject.id + ProjectIdentifierADM(maybeIri = + Some(SharedTestDataADM.systemProject.id) + ).value shouldBe SharedTestDataADM.systemProject.id } "return a 'BadRequestException' when the project IRI is invalid" in { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala index a71d484e44..8a408ab032 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala @@ -36,8 +36,8 @@ object UsersMessagesADMSpec { } /** - * This spec is used to test the [[UserADM]] and [[UserIdentifierADM]] classes. - */ + * This spec is used to test the [[UserADM]] and [[UserIdentifierADM]] classes. + */ class UsersMessagesADMSpec extends CoreSpec(UsersMessagesADMSpec.config) { private val id = SharedTestDataADM.rootUser.id @@ -93,13 +93,17 @@ class UsersMessagesADMSpec extends CoreSpec(UsersMessagesADMSpec.config) { } "return true if user is ProjectAdmin in any project " in { - assert(SharedTestDataADM.anythingAdminUser.permissions.isProjectAdminInAnyProject() === true, - "user is not ProjectAdmin in any of his projects") + assert( + SharedTestDataADM.anythingAdminUser.permissions.isProjectAdminInAnyProject() === true, + "user is not ProjectAdmin in any of his projects" + ) } "return false if user is not ProjectAdmin in any project " in { - assert(SharedTestDataADM.anythingUser1.permissions.isProjectAdminInAnyProject() === false, - "user is ProjectAdmin in one of his projects") + assert( + SharedTestDataADM.anythingUser1.permissions.isProjectAdminInAnyProject() === false, + "user is ProjectAdmin in one of his projects" + ) } "allow checking the SCrypt passwords" in { @@ -250,10 +254,8 @@ class UsersMessagesADMSpec extends CoreSpec(UsersMessagesADMSpec.config) { ChangeUserApiRequestADM(status = Some(true), systemAdmin = Some(true)) ) - val errorTooManyParametersStatusUpdate = the[BadRequestException] thrownBy ChangeUserApiRequestADM(status = - Some(true), - systemAdmin = - Some(true)) + val errorTooManyParametersStatusUpdate = + the[BadRequestException] thrownBy ChangeUserApiRequestADM(status = Some(true), systemAdmin = Some(true)) errorTooManyParametersStatusUpdate.getMessage should equal("Too many parameters sent for change request.") // more than one parameter for systemAdmin update @@ -261,9 +263,8 @@ class UsersMessagesADMSpec extends CoreSpec(UsersMessagesADMSpec.config) { ChangeUserApiRequestADM(systemAdmin = Some(true), status = Some(true)) ) - val errorTooManyParametersSystemAdminUpdate = the[BadRequestException] thrownBy ChangeUserApiRequestADM( - systemAdmin = Some(true), - status = Some(true)) + val errorTooManyParametersSystemAdminUpdate = + the[BadRequestException] thrownBy ChangeUserApiRequestADM(systemAdmin = Some(true), status = Some(true)) errorTooManyParametersSystemAdminUpdate.getMessage should equal("Too many parameters sent for change request.") // more than 5 parameters for basic user information update @@ -289,7 +290,8 @@ class UsersMessagesADMSpec extends CoreSpec(UsersMessagesADMSpec.config) { systemAdmin = Some(false) ) errorTooManyParametersBasicInformationUpdate.getMessage should equal( - "Too many parameters sent for change request.") + "Too many parameters sent for change request." + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala index 9edb723765..9de1d3f745 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala @@ -194,16 +194,16 @@ class ValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { "throw 'BadRequestException' if 'username' is invalid" in { Set( - createUserApiRequestADM(username = "don"), // too short + createUserApiRequestADM(username = "don"), // too short createUserApiRequestADM(username = "asdfoiasdfasdnlasdkjflasdjfaskdjflaskdjfaddssdskdfjs"), // too long - createUserApiRequestADM(username = "_donald"), // starts with _ - createUserApiRequestADM(username = ".donald"), // starts with . - createUserApiRequestADM(username = "donald_"), // ends with _ - createUserApiRequestADM(username = "donald."), // ends with . - createUserApiRequestADM(username = "donald__duck"), // contains multiple _ - createUserApiRequestADM(username = "donald..duck"), // contains multiple . - createUserApiRequestADM(username = "donald#duck"), // contains not only alphanumeric characters - createUserApiRequestADM(username = "dönälddück") // contains umlaut characters + createUserApiRequestADM(username = "_donald"), // starts with _ + createUserApiRequestADM(username = ".donald"), // starts with . + createUserApiRequestADM(username = "donald_"), // ends with _ + createUserApiRequestADM(username = "donald."), // ends with . + createUserApiRequestADM(username = "donald__duck"), // contains multiple _ + createUserApiRequestADM(username = "donald..duck"), // contains multiple . + createUserApiRequestADM(username = "donald#duck"), // contains not only alphanumeric characters + createUserApiRequestADM(username = "dönälddück") // contains umlaut characters ).map(request => the[BadRequestException] thrownBy createUserCreatePayloadADM(request) should have message "Invalid username" ) @@ -211,9 +211,9 @@ class ValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { "throw 'BadRequestException' if 'email' is invalid" in { Set( - createUserApiRequestADM(email = "don"), // does not contain @ + createUserApiRequestADM(email = "don"), // does not contain @ createUserApiRequestADM(email = "don@"), // ends with @ - createUserApiRequestADM(email = "@don") // starts with @ + createUserApiRequestADM(email = "@don") // starts with @ ).map(request => the[BadRequestException] thrownBy createUserCreatePayloadADM(request) should have message "Invalid email" ) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessagesSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessagesSpec.scala index 3cb1beaa4a..1d5c4c372d 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessagesSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessagesSpec.scala @@ -26,8 +26,8 @@ import org.scalatest.wordspec.AnyWordSpecLike import spray.json._ /** - * This spec is used to test 'ListAdminMessages'. - */ + * This spec is used to test 'ListAdminMessages'. + */ class TriplestoreMessagesSpec extends AnyWordSpecLike with Matchers with ListADMJsonProtocol { "Conversion from case class to JSON and back" should { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/CalendarDateUtilV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/CalendarDateUtilV2Spec.scala index fc214b1661..5aacbb2704 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/CalendarDateUtilV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/CalendarDateUtilV2Spec.scala @@ -24,13 +24,15 @@ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.messages.util._ /** - * Tests [[CalendarDateUtilV2]]. - */ + * Tests [[CalendarDateUtilV2]]. + */ class CalendarDateUtilV2Spec extends CoreSpec() { - private def checkSingleDate(calendarDate: CalendarDateV2, - expectedStartJDN: Int, - expectedEndJDN: Int, - dateStr: String): Unit = { + private def checkSingleDate( + calendarDate: CalendarDateV2, + expectedStartJDN: Int, + expectedEndJDN: Int, + dateStr: String + ): Unit = { val calendarDateRange = CalendarDateRangeV2( startCalendarDate = calendarDate, endCalendarDate = calendarDate @@ -44,10 +46,12 @@ class CalendarDateUtilV2Spec extends CoreSpec() { ) } - private def checkDateRange(calendarDateRange: CalendarDateRangeV2, - expectedStartJDN: Int, - expectedEndJDN: Int, - dateStr: String): Unit = { + private def checkDateRange( + calendarDateRange: CalendarDateRangeV2, + expectedStartJDN: Int, + expectedEndJDN: Int, + dateStr: String + ): Unit = { // Convert the date range to Julian Day Numbers and check that they're correct. val (startJDN: Int, endJDN: Int) = calendarDateRange.toJulianDayRange assert(startJDN == expectedStartJDN) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2Spec.scala index 6cc918ca9b..b5c35cb4d6 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2Spec.scala @@ -36,8 +36,8 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} /** - * Tests [[ConstructResponseUtilV2]]. - */ + * Tests [[ConstructResponseUtilV2]]. + */ class ConstructResponseUtilV2Spec extends CoreSpec() with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance private implicit val timeout: Timeout = 10.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2SpecFullData.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2SpecFullData.scala index fe7ea23457..1b770b29ad 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2SpecFullData.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2SpecFullData.scala @@ -28,42 +28,45 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, creationDate = Instant.parse("2019-11-29T10:00:00.673298Z"), userPermission = ChangeRightsPermission, - values = Map("http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( - ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 543212345, - comment = Some("second hidden int value in visible resource") - ), - valueIri = "http://rdfh.ch/0001/F8L7zPp7TI-4MGJQlCO4Zg/values/F2xCr0S2QfWRQxJDWY9L0g", - permissions = "M knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2019-11-29T10:00:00.673298Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("F2xCr0S2QfWRQxJDWY9L0g"), - userPermission = ChangeRightsPermission, - deletionInfo = None - ), - ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 123454321, - comment = Some("first hidden int value in visible resource") + values = Map( + "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 543212345, + comment = Some("second hidden int value in visible resource") + ), + valueIri = "http://rdfh.ch/0001/F8L7zPp7TI-4MGJQlCO4Zg/values/F2xCr0S2QfWRQxJDWY9L0g", + permissions = "M knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2019-11-29T10:00:00.673298Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("F2xCr0S2QfWRQxJDWY9L0g"), + userPermission = ChangeRightsPermission, + deletionInfo = None ), - valueIri = "http://rdfh.ch/0001/F8L7zPp7TI-4MGJQlCO4Zg/values/yVTqO37cRkCSvXbFc3vTyw", - permissions = "M knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2019-11-29T10:00:00.673298Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("yVTqO37cRkCSvXbFc3vTyw"), - userPermission = ChangeRightsPermission, - deletionInfo = None + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 123454321, + comment = Some("first hidden int value in visible resource") + ), + valueIri = "http://rdfh.ch/0001/F8L7zPp7TI-4MGJQlCO4Zg/values/yVTqO37cRkCSvXbFc3vTyw", + permissions = "M knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2019-11-29T10:00:00.673298Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("yVTqO37cRkCSvXbFc3vTyw"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) ) - )), + ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = None, deletionInfo = None - )), + ) + ), hiddenResourceIris = Set(), mayHaveMoreResults = false ) @@ -83,7 +86,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat projectADM = SharedTestDataADM.anythingProject, lastModificationDate = None, deletionInfo = None - )), + ) + ), hiddenResourceIris = Set(), mayHaveMoreResults = false ) @@ -100,58 +104,65 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat creationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), userPermission = ChangeRightsPermission, values = Map( - "http://www.knora.org/ontology/0001/anything#hasOtherThingValue".toSmartIri -> Vector(ReadLinkValueV2( - valueContent = LinkValueContentV2( - isIncomingLink = false, - referredResourceIri = "http://rdfh.ch/0001/XTxSMt0ySraVmwXD-bD2wQ", - ontologySchema = InternalSchema, - comment = Some("link value pointing to hidden resource"), - referredResourceExists = true, - nestedResource = Some(ReadResourceV2( - versionDate = None, - label = "hidden thing", - resourceIri = "http://rdfh.ch/0001/XTxSMt0ySraVmwXD-bD2wQ", - permissions = "V knora-admin:Creator", - attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", - resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, - creationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), - userPermission = ChangeRightsPermission, - values = Map(), - projectADM = SharedTestDataADM.anythingProject, - lastModificationDate = None, - deletionInfo = None - )) - ), - valueHasRefCount = 1, - valueIri = "http://rdfh.ch/0001/0JhgKcqoRIeRRG6ownArSw/values/UgSp5mXTTSKdI02ZU1KIAA", - permissions = "V knora-admin:UnknownUser|M knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("UgSp5mXTTSKdI02ZU1KIAA"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )), - "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector(ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 123454321, - comment = Some("visible int value in main resource") - ), - valueIri = "http://rdfh.ch/0001/0JhgKcqoRIeRRG6ownArSw/values/U1PwfNaVRQebbOSFWNdMqQ", - permissions = "V knora-admin:UnknownUser|M knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("U1PwfNaVRQebbOSFWNdMqQ"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )) + "http://www.knora.org/ontology/0001/anything#hasOtherThingValue".toSmartIri -> Vector( + ReadLinkValueV2( + valueContent = LinkValueContentV2( + isIncomingLink = false, + referredResourceIri = "http://rdfh.ch/0001/XTxSMt0ySraVmwXD-bD2wQ", + ontologySchema = InternalSchema, + comment = Some("link value pointing to hidden resource"), + referredResourceExists = true, + nestedResource = Some( + ReadResourceV2( + versionDate = None, + label = "hidden thing", + resourceIri = "http://rdfh.ch/0001/XTxSMt0ySraVmwXD-bD2wQ", + permissions = "V knora-admin:Creator", + attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", + resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, + creationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), + userPermission = ChangeRightsPermission, + values = Map(), + projectADM = SharedTestDataADM.anythingProject, + lastModificationDate = None, + deletionInfo = None + ) + ) + ), + valueHasRefCount = 1, + valueIri = "http://rdfh.ch/0001/0JhgKcqoRIeRRG6ownArSw/values/UgSp5mXTTSKdI02ZU1KIAA", + permissions = "V knora-admin:UnknownUser|M knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("UgSp5mXTTSKdI02ZU1KIAA"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ), + "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 123454321, + comment = Some("visible int value in main resource") + ), + valueIri = "http://rdfh.ch/0001/0JhgKcqoRIeRRG6ownArSw/values/U1PwfNaVRQebbOSFWNdMqQ", + permissions = "V knora-admin:UnknownUser|M knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("U1PwfNaVRQebbOSFWNdMqQ"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ) ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = None, deletionInfo = None - )), + ) + ), hiddenResourceIris = Set(), mayHaveMoreResults = false ) @@ -167,25 +178,30 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, creationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), userPermission = ViewPermission, - values = Map("http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector(ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 123454321, - comment = Some("visible int value in main resource") - ), - valueIri = "http://rdfh.ch/0001/0JhgKcqoRIeRRG6ownArSw/values/U1PwfNaVRQebbOSFWNdMqQ", - permissions = "V knora-admin:UnknownUser|M knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("U1PwfNaVRQebbOSFWNdMqQ"), - userPermission = ViewPermission, - deletionInfo = None - ))), + values = Map( + "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 123454321, + comment = Some("visible int value in main resource") + ), + valueIri = "http://rdfh.ch/0001/0JhgKcqoRIeRRG6ownArSw/values/U1PwfNaVRQebbOSFWNdMqQ", + permissions = "V knora-admin:UnknownUser|M knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2020-04-07T09:12:56.710717Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("U1PwfNaVRQebbOSFWNdMqQ"), + userPermission = ViewPermission, + deletionInfo = None + ) + ) + ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = None, deletionInfo = None - )), + ) + ), hiddenResourceIris = Set("http://rdfh.ch/0001/XTxSMt0ySraVmwXD-bD2wQ"), mayHaveMoreResults = false ) @@ -230,7 +246,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasLink".toSmartIri, value = "http://rdfh.ch/0001/a-thing", targetExists = true - )), + ) + ), startIndex = 1, endIndex = None, dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), @@ -283,7 +300,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasLink".toSmartIri, value = "http://rdfh.ch/0001/a-thing", targetExists = true - )), + ) + ), startIndex = 1, endIndex = None, dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), @@ -297,7 +315,7 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat maybeValueHasString = Some("Na ja, die Dinge sind OK."), comment = None, xslt = None, - mappingIri = Some("http://rdfh.ch/standoff/mappings/StandardMapping"), + mappingIri = Some("http://rdfh.ch/standoff/mappings/StandardMapping") ), valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/2", permissions = "CR knora-admin:Creator", @@ -310,43 +328,48 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat deletionInfo = None ) ), - "http://www.knora.org/ontology/knora-base#hasStandoffLinkToValue".toSmartIri -> Vector(ReadLinkValueV2( - valueContent = LinkValueContentV2( - isIncomingLink = false, - referredResourceIri = "http://rdfh.ch/0001/a-thing", - ontologySchema = InternalSchema, - comment = None, - referredResourceExists = true, - nestedResource = Some(ReadResourceV2( - versionDate = None, - label = "A thing", - resourceIri = "http://rdfh.ch/0001/a-thing", - permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser", - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, - creationDate = Instant.parse("2016-03-02T15:05:10Z"), - userPermission = ChangeRightsPermission, - values = Map(), - projectADM = SharedTestDataADM.anythingProject, - lastModificationDate = None, - deletionInfo = None - )) - ), - valueHasRefCount = 2, - valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/0", - permissions = "CR knora-admin:Creator|V knora-admin:UnknownUser", - valueCreationDate = Instant.parse("2016-03-02T15:05:54Z"), - attachedToUser = "http://www.knora.org/ontology/knora-admin#SystemUser", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("0"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )) + "http://www.knora.org/ontology/knora-base#hasStandoffLinkToValue".toSmartIri -> Vector( + ReadLinkValueV2( + valueContent = LinkValueContentV2( + isIncomingLink = false, + referredResourceIri = "http://rdfh.ch/0001/a-thing", + ontologySchema = InternalSchema, + comment = None, + referredResourceExists = true, + nestedResource = Some( + ReadResourceV2( + versionDate = None, + label = "A thing", + resourceIri = "http://rdfh.ch/0001/a-thing", + permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser", + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, + creationDate = Instant.parse("2016-03-02T15:05:10Z"), + userPermission = ChangeRightsPermission, + values = Map(), + projectADM = SharedTestDataADM.anythingProject, + lastModificationDate = None, + deletionInfo = None + ) + ) + ), + valueHasRefCount = 2, + valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/0", + permissions = "CR knora-admin:Creator|V knora-admin:UnknownUser", + valueCreationDate = Instant.parse("2016-03-02T15:05:54Z"), + attachedToUser = "http://www.knora.org/ontology/knora-admin#SystemUser", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("0"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ) ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = None, deletionInfo = None - )), + ) + ), hiddenResourceIris = Set(), mayHaveMoreResults = false ) @@ -372,38 +395,44 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat ontologySchema = InternalSchema, comment = None, referredResourceExists = true, - nestedResource = Some(ReadResourceV2( - versionDate = None, - label = "Zeitgl\u00F6cklein des Lebens und Leidens Christi", - resourceIri = "http://rdfh.ch/0803/ff17e5ef9601", - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#book".toSmartIri, - creationDate = Instant.parse("2016-03-02T15:05:23Z"), - userPermission = ChangeRightsPermission, - values = - Map("http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - valueHasLanguage = None, - ontologySchema = InternalSchema, - maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), - comment = None - ), - valueIri = "http://rdfh.ch/0803/ff17e5ef9601/values/d9a522845006", - permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", - valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), - valueHasMaxStandoffStartIndex = None, - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("d9a522845006"), - userPermission = ChangeRightsPermission, - deletionInfo = None - ))), - projectADM = SharedTestDataADM.incunabulaProject, - lastModificationDate = None, - deletionInfo = None - )) + nestedResource = Some( + ReadResourceV2( + versionDate = None, + label = "Zeitgl\u00F6cklein des Lebens und Leidens Christi", + resourceIri = "http://rdfh.ch/0803/ff17e5ef9601", + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#book".toSmartIri, + creationDate = Instant.parse("2016-03-02T15:05:23Z"), + userPermission = ChangeRightsPermission, + values = Map( + "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + valueHasLanguage = None, + ontologySchema = InternalSchema, + maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), + comment = None + ), + valueIri = "http://rdfh.ch/0803/ff17e5ef9601/values/d9a522845006", + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", + valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), + valueHasMaxStandoffStartIndex = None, + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("d9a522845006"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ) + ), + projectADM = SharedTestDataADM.incunabulaProject, + lastModificationDate = None, + deletionInfo = None + ) + ) ), valueHasRefCount = 1, valueIri = "http://rdfh.ch/0803/76570a749901/values/bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d", @@ -415,7 +444,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d"), userPermission = ChangeRightsPermission, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#seqnum".toSmartIri -> Vector( ReadOtherValueV2( valueContent = IntegerValueContentV2( @@ -432,7 +462,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("fae17f4f6106"), userPermission = ChangeRightsPermission, deletionInfo = None - )) + ) + ) ), projectADM = SharedTestDataADM.incunabulaProject, lastModificationDate = None, @@ -457,38 +488,44 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat ontologySchema = InternalSchema, comment = None, referredResourceExists = true, - nestedResource = Some(ReadResourceV2( - versionDate = None, - label = "Zeitgl\u00F6cklein des Lebens und Leidens Christi", - resourceIri = "http://rdfh.ch/0803/c5058f3a", - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#book".toSmartIri, - creationDate = Instant.parse("2016-03-02T15:05:10Z"), - userPermission = ChangeRightsPermission, - values = - Map("http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - valueHasLanguage = None, - ontologySchema = InternalSchema, - maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), - comment = None - ), - valueIri = "http://rdfh.ch/0803/c5058f3a/values/c3295339", - permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", - valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), - valueHasMaxStandoffStartIndex = None, - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("c3295339"), - userPermission = ChangeRightsPermission, - deletionInfo = None - ))), - projectADM = SharedTestDataADM.incunabulaProject, - lastModificationDate = None, - deletionInfo = None - )) + nestedResource = Some( + ReadResourceV2( + versionDate = None, + label = "Zeitgl\u00F6cklein des Lebens und Leidens Christi", + resourceIri = "http://rdfh.ch/0803/c5058f3a", + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#book".toSmartIri, + creationDate = Instant.parse("2016-03-02T15:05:10Z"), + userPermission = ChangeRightsPermission, + values = Map( + "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + valueHasLanguage = None, + ontologySchema = InternalSchema, + maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), + comment = None + ), + valueIri = "http://rdfh.ch/0803/c5058f3a/values/c3295339", + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", + valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), + valueHasMaxStandoffStartIndex = None, + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("c3295339"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ) + ), + projectADM = SharedTestDataADM.incunabulaProject, + lastModificationDate = None, + deletionInfo = None + ) + ) ), valueHasRefCount = 1, valueIri = "http://rdfh.ch/0803/773f258402/values/25c5e9fd-2cb2-4350-88bb-882be3373745", @@ -500,7 +537,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("25c5e9fd-2cb2-4350-88bb-882be3373745"), userPermission = ChangeRightsPermission, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#seqnum".toSmartIri -> Vector( ReadOtherValueV2( valueContent = IntegerValueContentV2( @@ -517,7 +555,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("53feeaf80a"), userPermission = ChangeRightsPermission, deletionInfo = None - )) + ) + ) ), projectADM = SharedTestDataADM.incunabulaProject, lastModificationDate = None, @@ -559,7 +598,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("c3295339"), userPermission = ChangeRightsPermission, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/knora-base#hasIncomingLinkValue".toSmartIri -> Vector( ReadLinkValueV2( valueContent = LinkValueContentV2( @@ -568,56 +608,64 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat ontologySchema = InternalSchema, comment = None, referredResourceExists = true, - nestedResource = Some(ReadResourceV2( - versionDate = None, - label = "a5v", - resourceIri = "http://rdfh.ch/0803/773f258402", - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#page".toSmartIri, - creationDate = Instant.parse("2016-03-02T15:05:10Z"), - userPermission = ChangeRightsPermission, - values = Map( - "http://www.knora.org/ontology/0803/incunabula#partOfValue".toSmartIri -> Vector(ReadLinkValueV2( - valueContent = LinkValueContentV2( - isIncomingLink = false, - referredResourceIri = "http://rdfh.ch/0803/c5058f3a", - ontologySchema = InternalSchema, - comment = None, - referredResourceExists = true, - nestedResource = None - ), - valueHasRefCount = 1, - valueIri = "http://rdfh.ch/0803/773f258402/values/25c5e9fd-2cb2-4350-88bb-882be3373745", - permissions = "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("25c5e9fd-2cb2-4350-88bb-882be3373745"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )), - "http://www.knora.org/ontology/0803/incunabula#seqnum".toSmartIri -> Vector(ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 10, - comment = None + nestedResource = Some( + ReadResourceV2( + versionDate = None, + label = "a5v", + resourceIri = "http://rdfh.ch/0803/773f258402", + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#page".toSmartIri, + creationDate = Instant.parse("2016-03-02T15:05:10Z"), + userPermission = ChangeRightsPermission, + values = Map( + "http://www.knora.org/ontology/0803/incunabula#partOfValue".toSmartIri -> Vector( + ReadLinkValueV2( + valueContent = LinkValueContentV2( + isIncomingLink = false, + referredResourceIri = "http://rdfh.ch/0803/c5058f3a", + ontologySchema = InternalSchema, + comment = None, + referredResourceExists = true, + nestedResource = None + ), + valueHasRefCount = 1, + valueIri = "http://rdfh.ch/0803/773f258402/values/25c5e9fd-2cb2-4350-88bb-882be3373745", + permissions = + "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("25c5e9fd-2cb2-4350-88bb-882be3373745"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) ), - valueIri = "http://rdfh.ch/0803/773f258402/values/53feeaf80a", - permissions = "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("53feeaf80a"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )) - ), - projectADM = SharedTestDataADM.incunabulaProject, - lastModificationDate = None, - deletionInfo = None - )) + "http://www.knora.org/ontology/0803/incunabula#seqnum".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 10, + comment = None + ), + valueIri = "http://rdfh.ch/0803/773f258402/values/53feeaf80a", + permissions = + "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("53feeaf80a"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ) + ), + projectADM = SharedTestDataADM.incunabulaProject, + lastModificationDate = None, + deletionInfo = None + ) + ) ), valueHasRefCount = 1, valueIri = "http://rdfh.ch/0803/773f258402/values/25c5e9fd-2cb2-4350-88bb-882be3373745", @@ -629,7 +677,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("25c5e9fd-2cb2-4350-88bb-882be3373745"), userPermission = ChangeRightsPermission, deletionInfo = None - )) + ) + ) ), projectADM = SharedTestDataADM.incunabulaProject, lastModificationDate = None, @@ -664,7 +713,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("d9a522845006"), userPermission = ChangeRightsPermission, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/knora-base#hasIncomingLinkValue".toSmartIri -> Vector( ReadLinkValueV2( valueContent = LinkValueContentV2( @@ -673,56 +723,64 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat ontologySchema = InternalSchema, comment = None, referredResourceExists = true, - nestedResource = Some(ReadResourceV2( - versionDate = None, - label = "a5v", - resourceIri = "http://rdfh.ch/0803/76570a749901", - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#page".toSmartIri, - creationDate = Instant.parse("2016-03-02T15:05:23Z"), - userPermission = ChangeRightsPermission, - values = Map( - "http://www.knora.org/ontology/0803/incunabula#partOfValue".toSmartIri -> Vector(ReadLinkValueV2( - valueContent = LinkValueContentV2( - isIncomingLink = false, - referredResourceIri = "http://rdfh.ch/0803/ff17e5ef9601", - ontologySchema = InternalSchema, - comment = None, - referredResourceExists = true, - nestedResource = None - ), - valueHasRefCount = 1, - valueIri = "http://rdfh.ch/0803/76570a749901/values/bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d", - permissions = "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )), - "http://www.knora.org/ontology/0803/incunabula#seqnum".toSmartIri -> Vector(ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 10, - comment = None + nestedResource = Some( + ReadResourceV2( + versionDate = None, + label = "a5v", + resourceIri = "http://rdfh.ch/0803/76570a749901", + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser", + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + resourceClassIri = "http://www.knora.org/ontology/0803/incunabula#page".toSmartIri, + creationDate = Instant.parse("2016-03-02T15:05:23Z"), + userPermission = ChangeRightsPermission, + values = Map( + "http://www.knora.org/ontology/0803/incunabula#partOfValue".toSmartIri -> Vector( + ReadLinkValueV2( + valueContent = LinkValueContentV2( + isIncomingLink = false, + referredResourceIri = "http://rdfh.ch/0803/ff17e5ef9601", + ontologySchema = InternalSchema, + comment = None, + referredResourceExists = true, + nestedResource = None + ), + valueHasRefCount = 1, + valueIri = "http://rdfh.ch/0803/76570a749901/values/bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d", + permissions = + "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) ), - valueIri = "http://rdfh.ch/0803/76570a749901/values/fae17f4f6106", - permissions = "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", - valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - previousValueIri = None, - valueHasUUID = stringFormatter.decodeUuid("fae17f4f6106"), - userPermission = ChangeRightsPermission, - deletionInfo = None - )) - ), - projectADM = SharedTestDataADM.incunabulaProject, - lastModificationDate = None, - deletionInfo = None - )) + "http://www.knora.org/ontology/0803/incunabula#seqnum".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 10, + comment = None + ), + valueIri = "http://rdfh.ch/0803/76570a749901/values/fae17f4f6106", + permissions = + "CR knora-admin:Creator|V knora-admin:UnknownUser,knora-admin:KnownUser,knora-admin:ProjectMember", + valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + previousValueIri = None, + valueHasUUID = stringFormatter.decodeUuid("fae17f4f6106"), + userPermission = ChangeRightsPermission, + deletionInfo = None + ) + ) + ), + projectADM = SharedTestDataADM.incunabulaProject, + lastModificationDate = None, + deletionInfo = None + ) + ) ), valueHasRefCount = 1, valueIri = "http://rdfh.ch/0803/76570a749901/values/bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d", @@ -734,7 +792,8 @@ class ConstructResponseUtilV2SpecFullData(implicit stringFormatter: StringFormat valueHasUUID = stringFormatter.decodeUuid("bbd4d6a9-8b73-4670-b0cd-e851cd0a7c5d"), userPermission = ChangeRightsPermission, deletionInfo = None - )) + ) + ) ), projectADM = SharedTestDataADM.incunabulaProject, lastModificationDate = None, diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/DateUtilV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/DateUtilV1Spec.scala index 1ef5a9bd7b..3b8824e70b 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/DateUtilV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/DateUtilV1Spec.scala @@ -34,8 +34,8 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike /** - * Tests [[DateUtilV1]]. - */ + * Tests [[DateUtilV1]]. + */ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { "The DateUtilV1 class" should { "convert a date in YYYY-MM-DD format, in the Julian calendar, into a Julian day count, and back again" in { @@ -56,7 +56,8 @@ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { calendar = KnoraCalendarV1.JULIAN, dateprecision1 = KnoraPrecisionV1.DAY, dateprecision2 = KnoraPrecisionV1.DAY - )) + ) + ) val reverseConvertedBundesbriefDateValueV1 = DateUtilV1.julianDayNumberValueV1ToDateValueV1(bundesbriefJulianDayCountValueV1) @@ -110,9 +111,11 @@ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { val theJulianDayCountValueV1 = DateUtilV1.dateValueV1ToJulianDayNumberValueV1(someDateValueV1) - val date_string = DateUtilV1.julianDayNumber2DateString(theJulianDayCountValueV1.dateval1, - theJulianDayCountValueV1.calendar, - theJulianDayCountValueV1.dateprecision1) + val date_string = DateUtilV1.julianDayNumber2DateString( + theJulianDayCountValueV1.dateval1, + theJulianDayCountValueV1.calendar, + theJulianDayCountValueV1.dateprecision1 + ) } @@ -134,7 +137,8 @@ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { calendar = KnoraCalendarV1.JULIAN, dateprecision1 = KnoraPrecisionV1.DAY, dateprecision2 = KnoraPrecisionV1.DAY - )) + ) + ) } @@ -156,7 +160,8 @@ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { calendar = KnoraCalendarV1.GREGORIAN, dateprecision1 = KnoraPrecisionV1.DAY, dateprecision2 = KnoraPrecisionV1.DAY - )) + ) + ) val reverseConvertedBenBirthdayDateValueV1 = DateUtilV1.julianDayNumberValueV1ToDateValueV1(benBirthdayJulianDayCountValueV1) @@ -182,7 +187,8 @@ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { calendar = KnoraCalendarV1.GREGORIAN, dateprecision1 = KnoraPrecisionV1.MONTH, dateprecision2 = KnoraPrecisionV1.DAY - )) + ) + ) val reverseConvertedDateValueV1 = DateUtilV1.julianDayNumberValueV1ToDateValueV1(julianDayCountValueV1) reverseConvertedDateValueV1 should be(dateValueV1) @@ -206,7 +212,8 @@ class DateUtilV1Spec extends AnyWordSpecLike with Matchers { calendar = KnoraCalendarV1.GREGORIAN, dateprecision1 = KnoraPrecisionV1.MONTH, dateprecision2 = KnoraPrecisionV1.MONTH - )) + ) + ) val reverseConvertedDateValueV1 = DateUtilV1.julianDayNumberValueV1ToDateValueV1(julianDayCountValueV1) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala index a40bd059f9..e38614b539 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala @@ -194,7 +194,10 @@ class PermissionUtilADMSpec extends CoreSpec("PermissionUtilSpec") with Implicit PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser) ) - PermissionUtilADM.parsePermissionsWithType(Some(hasPermissionsString), PermissionType.OAP) should contain allElementsOf permissionsSet + PermissionUtilADM.parsePermissionsWithType( + Some(hasPermissionsString), + PermissionType.OAP + ) should contain allElementsOf permissionsSet } "return parsed permissions string as 'Set[PermissionV1]' (administrative permissions)" in { @@ -208,7 +211,10 @@ class PermissionUtilADMSpec extends CoreSpec("PermissionUtilSpec") with Implicit PermissionADM.projectResourceCreateRestrictedPermission("http://www.knora.org/ontology/00FF/images#bildformat") ) - PermissionUtilADM.parsePermissionsWithType(Some(hasPermissionsString), PermissionType.AP) should contain allElementsOf permissionsSet + PermissionUtilADM.parsePermissionsWithType( + Some(hasPermissionsString), + PermissionType.AP + ) should contain allElementsOf permissionsSet } "build a 'PermissionV1' object" in { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/JsonLDUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/JsonLDUtilSpec.scala index afbbe55f6c..ac26e8134b 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/JsonLDUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/JsonLDUtilSpec.scala @@ -28,8 +28,8 @@ import org.knora.webapi.util.FileUtil import spray.json.{JsValue, JsonParser} /** - * Tests [[JsonLDUtil]]. - */ + * Tests [[JsonLDUtil]]. + */ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { private val featureFactoryConfig: FeatureFactoryConfig = new TestFeatureFactoryConfig( testToggles = Set(featureToggle), @@ -43,78 +43,78 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { "parse JSON-LD text, compact it with an empty context, convert the result to a JsonLDDocument, and convert that back to text" in { val ontologyJsonLDInputStr = """ - |{ - | "knora-api:hasOntologies" : { - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "owl:Ontology", - | "knora-api:hasProperties" : { - | "anything:hasName" : { - | "@id" : "anything:hasName", - | "@type" : "owl:ObjectProperty", - | "knora-api:objectType" : "http://api.knora.org/ontology/knora-api/v2#TextValue", - | "knora-api:subjectType" : "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", - | "rdfs:comment" : [ { - | "@language" : "en", - | "@value" : "The name of a Thing" - | }, { - | "@language" : "de", - | "@value" : "Der Name eines Dinges" - | } ], - | "rdfs:label" : [ { - | "@language" : "en", - | "@value" : "has name" - | }, { - | "@language" : "de", - | "@value" : "hat Namen" - | } ], - | "rdfs:subPropertyOf" : [ "http://api.knora.org/ontology/knora-api/v2#hasValue", "http://schema.org/name" ] - | } - | }, - | "knora-api:lastModificationDate" : "2017-12-19T15:23:42.166Z" - | }, - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "owl" : "http://www.w3.org/2002/07/owl#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + |{ + | "knora-api:hasOntologies" : { + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "owl:Ontology", + | "knora-api:hasProperties" : { + | "anything:hasName" : { + | "@id" : "anything:hasName", + | "@type" : "owl:ObjectProperty", + | "knora-api:objectType" : "http://api.knora.org/ontology/knora-api/v2#TextValue", + | "knora-api:subjectType" : "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", + | "rdfs:comment" : [ { + | "@language" : "en", + | "@value" : "The name of a Thing" + | }, { + | "@language" : "de", + | "@value" : "Der Name eines Dinges" + | } ], + | "rdfs:label" : [ { + | "@language" : "en", + | "@value" : "has name" + | }, { + | "@language" : "de", + | "@value" : "hat Namen" + | } ], + | "rdfs:subPropertyOf" : [ "http://api.knora.org/ontology/knora-api/v2#hasValue", "http://schema.org/name" ] + | } + | }, + | "knora-api:lastModificationDate" : "2017-12-19T15:23:42.166Z" + | }, + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "owl" : "http://www.w3.org/2002/07/owl#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val ontologyCompactedJsonLDOutputStr = """ - |{ - | "http://api.knora.org/ontology/knora-api/v2#hasOntologies" : { - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "http://www.w3.org/2002/07/owl#Ontology", - | "http://api.knora.org/ontology/knora-api/v2#hasProperties" : { - | "http://0.0.0.0:3333/ontology/0001/anything/v2#hasName" : { - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2#hasName", - | "@type" : "http://www.w3.org/2002/07/owl#ObjectProperty", - | "http://api.knora.org/ontology/knora-api/v2#objectType" : "http://api.knora.org/ontology/knora-api/v2#TextValue", - | "http://api.knora.org/ontology/knora-api/v2#subjectType" : "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", - | "http://www.w3.org/2000/01/rdf-schema#comment" : [ { - | "@language" : "en", - | "@value" : "The name of a Thing" - | }, { - | "@language" : "de", - | "@value" : "Der Name eines Dinges" - | } ], - | "http://www.w3.org/2000/01/rdf-schema#label" : [ { - | "@language" : "en", - | "@value" : "has name" - | }, { - | "@language" : "de", - | "@value" : "hat Namen" - | } ], - | "http://www.w3.org/2000/01/rdf-schema#subPropertyOf" : [ "http://api.knora.org/ontology/knora-api/v2#hasValue", "http://schema.org/name" ] - | } - | }, - | "http://api.knora.org/ontology/knora-api/v2#lastModificationDate" : "2017-12-19T15:23:42.166Z" - | } - |} + |{ + | "http://api.knora.org/ontology/knora-api/v2#hasOntologies" : { + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "http://www.w3.org/2002/07/owl#Ontology", + | "http://api.knora.org/ontology/knora-api/v2#hasProperties" : { + | "http://0.0.0.0:3333/ontology/0001/anything/v2#hasName" : { + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2#hasName", + | "@type" : "http://www.w3.org/2002/07/owl#ObjectProperty", + | "http://api.knora.org/ontology/knora-api/v2#objectType" : "http://api.knora.org/ontology/knora-api/v2#TextValue", + | "http://api.knora.org/ontology/knora-api/v2#subjectType" : "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", + | "http://www.w3.org/2000/01/rdf-schema#comment" : [ { + | "@language" : "en", + | "@value" : "The name of a Thing" + | }, { + | "@language" : "de", + | "@value" : "Der Name eines Dinges" + | } ], + | "http://www.w3.org/2000/01/rdf-schema#label" : [ { + | "@language" : "en", + | "@value" : "has name" + | }, { + | "@language" : "de", + | "@value" : "hat Namen" + | } ], + | "http://www.w3.org/2000/01/rdf-schema#subPropertyOf" : [ "http://api.knora.org/ontology/knora-api/v2#hasValue", "http://schema.org/name" ] + | } + | }, + | "http://api.knora.org/ontology/knora-api/v2#lastModificationDate" : "2017-12-19T15:23:42.166Z" + | } + |} """.stripMargin val compactedJsonLDDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(ontologyJsonLDInputStr) @@ -234,16 +234,16 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { // A Turtle document with a circular reference. val turtle = """@prefix foo: . - |@prefix rdfs: . - | - | a foo:Foo; - | rdfs:label "foo 1"; - | foo:hasOtherFoo . - | - | a foo:Foo; - | rdfs:label "foo 2"; - | foo:hasOtherFoo . - |""".stripMargin + |@prefix rdfs: . + | + | a foo:Foo; + | rdfs:label "foo 1"; + | foo:hasOtherFoo . + | + | a foo:Foo; + | rdfs:label "foo 2"; + | foo:hasOtherFoo . + |""".stripMargin // Parse it to an RDF4J Model. val inputModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtle, rdfFormat = Turtle) @@ -258,28 +258,36 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject( - value = Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo1"))), - )), - )) + "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), + "http://example.org/foo#hasOtherFoo" -> JsonLDObject( + value = Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo1")) + ) + ) + ) + ) + ) val expectedWithFoo2AtTopLevel = JsonLDObject( value = Map( "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject( - value = Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo2"))) - )) - )) + "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), + "http://example.org/foo#hasOtherFoo" -> JsonLDObject( + value = Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo2")) + ) + ) + ) + ) + ) assert( outputJsonLD.body == expectedWithFoo1AtTopLevel || @@ -291,25 +299,25 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { // A simple Turtle document. val turtle = """@prefix foo: . - |@prefix rdfs: . - |@prefix xsd: . - | - | a foo:Foo; - | rdfs:label "foo 1"; - | foo:hasBar [ - | a foo:Bar; - | rdfs:label "bar 1" - | ]; - | foo:hasOtherFoo . - | - | a foo:Foo; - | rdfs:label "foo 2"; - | foo:hasIndex "3"^^xsd:integer; - | foo:hasBar [ - | a foo:Bar; - | rdfs:label "bar 2" - | ]. - |""".stripMargin + |@prefix rdfs: . + |@prefix xsd: . + | + | a foo:Foo; + | rdfs:label "foo 1"; + | foo:hasBar [ + | a foo:Bar; + | rdfs:label "bar 1" + | ]; + | foo:hasOtherFoo . + | + | a foo:Foo; + | rdfs:label "foo 2"; + | foo:hasIndex "3"^^xsd:integer; + | foo:hasBar [ + | a foo:Bar; + | rdfs:label "bar 2" + | ]. + |""".stripMargin // Parse it to an RDF4J Model. val inputModel: RdfModel = rdfFormatUtil.parseToRdfModel(rdfStr = turtle, rdfFormat = Turtle) @@ -325,19 +333,25 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { value = Map( "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 1") - )), + ) + ), "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), - "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), - "http://example.org/foo#hasBar" -> JsonLDObject(value = Map( - "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2") - )) - )) - )) + "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), + "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), + "http://example.org/foo#hasBar" -> JsonLDObject(value = + Map( + "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2") + ) + ) + ) + ) + ) + ) assert(hierarchicalJsonLD.body == expectedHierarchicalJsonLD) @@ -345,29 +359,43 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { val flatJsonLD: JsonLDDocument = JsonLDUtil.fromRdfModel(model = inputModel, flatJsonLD = true) val expectedFlatJsonLD = JsonLDObject( - value = Map("@graph" -> JsonLDArray(value = Vector( - JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), - "http://example.org/foo#hasBar" -> JsonLDObject(value = Map( - "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 1") - )), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = - Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo2"))) - )), - JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), - "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), - "http://example.org/foo#hasBar" -> JsonLDObject(value = Map( - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Bar") - )) - )) - )))) + value = Map( + "@graph" -> JsonLDArray(value = + Vector( + JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), + "http://example.org/foo#hasBar" -> JsonLDObject(value = + Map( + "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 1") + ) + ), + "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = + Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo2")) + ) + ) + ), + JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), + "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), + "http://example.org/foo#hasBar" -> JsonLDObject(value = + Map( + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Bar") + ) + ) + ) + ) + ) + ) + ) + ) assert(flatJsonLD.body == expectedFlatJsonLD) } @@ -378,11 +406,11 @@ abstract class JsonLDUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { val jsonLDWithInvalidProperties = """{ - | "http://ns.dasch.swiss/repository#hasLicense":{ - | "type": "https://schema.org/URL", - | "value": "https://creativecommons.org/licenses/by/3.0" - | } - |}""".stripMargin + | "http://ns.dasch.swiss/repository#hasLicense":{ + | "type": "https://schema.org/URL", + | "value": "https://creativecommons.org/licenses/by/3.0" + | } + |}""".stripMargin // Parse the JSON-LD and check the parsed data structure. diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/KnoraResponseV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/KnoraResponseV2Spec.scala index 99a449a995..88997ae4ad 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/KnoraResponseV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/KnoraResponseV2Spec.scala @@ -29,8 +29,8 @@ import org.knora.webapi.settings.KnoraSettingsImpl import org.knora.webapi.util.FileUtil /** - * Tests the formatting of Knora API v2 responses. - */ + * Tests the formatting of Knora API v2 responses. + */ abstract class KnoraResponseV2Spec(featureToggle: FeatureToggle) extends CoreSpec { private val featureFactoryConfig: FeatureFactoryConfig = new TestFeatureFactoryConfig( testToggles = Set(featureToggle), @@ -41,18 +41,18 @@ abstract class KnoraResponseV2Spec(featureToggle: FeatureToggle) extends CoreSpe private val turtle = """ a ; - | [ a ; - | "bar 1" - | ]; - | ; - | "foo 1" . - | - | a ; - | [ a ; - | "bar 2" - | ]; - | 3; - | "foo 2" .""".stripMargin + | [ a ; + | "bar 1" + | ]; + | ; + | "foo 1" . + | + | a ; + | [ a ; + | "bar 2" + | ]; + | 3; + | "foo 2" .""".stripMargin private val hierarchicalJsonLD = JsonLDDocument( JsonLDObject( @@ -63,61 +63,83 @@ abstract class KnoraResponseV2Spec(featureToggle: FeatureToggle) extends CoreSpe value = Map( "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 1") - )), + ) + ), "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), - "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), - "http://example.org/foo#hasBar" -> JsonLDObject(value = Map( - "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2") - )) - )) - )) + "http://example.org/foo#hasOtherFoo" -> JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), + "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), + "http://example.org/foo#hasBar" -> JsonLDObject(value = + Map( + "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2") + ) + ) + ) + ) + ) + ) ) private val flatJsonLD = JsonLDDocument( JsonLDObject( - value = Map("@graph" -> JsonLDArray(value = Vector( - JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), - "http://example.org/foo#hasBar" -> JsonLDObject(value = Map( - "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 1") - )), - "http://example.org/foo#hasOtherFoo" -> JsonLDObject( - value = Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo2"))) - )), - JsonLDObject(value = Map( - "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), - "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), - "http://example.org/foo#hasBar" -> JsonLDObject(value = Map( - "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2"), - "@type" -> JsonLDString(value = "http://example.org/foo#Bar") - )) - )) - )))), + value = Map( + "@graph" -> JsonLDArray(value = + Vector( + JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo1"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 1"), + "http://example.org/foo#hasBar" -> JsonLDObject(value = + Map( + "@type" -> JsonLDString(value = "http://example.org/foo#Bar"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 1") + ) + ), + "http://example.org/foo#hasOtherFoo" -> JsonLDObject( + value = Map("@id" -> JsonLDString(value = "http://rdfh.ch/foo2")) + ) + ) + ), + JsonLDObject(value = + Map( + "@id" -> JsonLDString(value = "http://rdfh.ch/foo2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Foo"), + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "foo 2"), + "http://example.org/foo#hasIndex" -> JsonLDInt(value = 3), + "http://example.org/foo#hasBar" -> JsonLDObject(value = + Map( + "http://www.w3.org/2000/01/rdf-schema#label" -> JsonLDString(value = "bar 2"), + "@type" -> JsonLDString(value = "http://example.org/foo#Bar") + ) + ) + ) + ) + ) + ) + ) + ), isFlat = true ) /** - * A test implementation of [[KnoraTurtleResponseV2]]. - */ + * A test implementation of [[KnoraTurtleResponseV2]]. + */ case class TurtleTestResponse(turtle: String) extends KnoraTurtleResponseV2 /** - * A test implementation of [[KnoraJsonLDResponseV2]]. - */ + * A test implementation of [[KnoraJsonLDResponseV2]]. + */ case class JsonLDTestResponse(jsonLDDocument: JsonLDDocument) extends KnoraJsonLDResponseV2 { - override protected def toJsonLDDocument(targetSchema: ApiV2Schema, - settings: KnoraSettingsImpl, - schemaOptions: Set[SchemaOption]): JsonLDDocument = jsonLDDocument + override protected def toJsonLDDocument( + targetSchema: ApiV2Schema, + settings: KnoraSettingsImpl, + schemaOptions: Set[SchemaOption] + ): JsonLDDocument = jsonLDDocument } "KnoraResponseV2" should { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtilSpec.scala index 187bb4857f..41105920f8 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfFormatUtilSpec.scala @@ -29,8 +29,8 @@ import org.knora.webapi.util.FileUtil import org.knora.webapi.{CoreSpec, IRI} /** - * Tests implementations of [[RdfFormatUtil]]. - */ + * Tests implementations of [[RdfFormatUtil]]. + */ abstract class RdfFormatUtilSpec(featureToggle: FeatureToggle) extends CoreSpec { private val featureFactoryConfig: FeatureFactoryConfig = new TestFeatureFactoryConfig( testToggles = Set(featureToggle), @@ -48,33 +48,29 @@ abstract class RdfFormatUtilSpec(featureToggle: FeatureToggle) extends CoreSpec ) /** - * Processes `anything-onto.ttl` and checks whether the expected content is received. - */ + * Processes `anything-onto.ttl` and checks whether the expected content is received. + */ class TestStreamProcessor extends RdfStreamProcessor { var startCalled: Boolean = false var finishCalled: Boolean = false var gotKnoraBaseNamespace = false var gotThingLabelStatement: Boolean = false - override def start(): Unit = { + override def start(): Unit = startCalled = true - } - override def processNamespace(prefix: String, namespace: IRI): Unit = { + override def processNamespace(prefix: String, namespace: IRI): Unit = if (prefix == "knora-base" && namespace == "http://www.knora.org/ontology/knora-base#") { gotKnoraBaseNamespace = true } - } - override def processStatement(statement: Statement): Unit = { + override def processStatement(statement: Statement): Unit = if (statement == expectedThingLabelStatement) { gotThingLabelStatement = true } - } - override def finish(): Unit = { + override def finish(): Unit = finishCalled = true - } def check(): Unit = { assert(startCalled) @@ -98,9 +94,8 @@ abstract class RdfFormatUtilSpec(featureToggle: FeatureToggle) extends CoreSpec assert(statements.head.obj == rdfNodeFactory.makeIriNode("http://0.0.0.0:3333/ontology/0803/incunabula/v2#book")) } - private def checkJsonLDDocumentForRdfTypeBook(jsonLDDocument: JsonLDDocument): Unit = { + private def checkJsonLDDocumentForRdfTypeBook(jsonLDDocument: JsonLDDocument): Unit = assert(jsonLDDocument.requireString(JsonLDKeywords.TYPE) == "http://0.0.0.0:3333/ontology/0803/incunabula/v2#book") - } "RdfFormatUtil" should { "parse RDF in Turtle format, producing an RdfModel, then format it as Turtle again" in { @@ -177,8 +172,8 @@ abstract class RdfFormatUtilSpec(featureToggle: FeatureToggle) extends CoreSpec "read Turtle, add a graph IRI to it, write it to a TriG file, and read back the TriG file" in { val graphIri = "http://example.org/data#" val rdfSource = RdfInputStreamSource( - new BufferedInputStream( - Files.newInputStream(Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.ttl")))) + new BufferedInputStream(Files.newInputStream(Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.ttl"))) + ) val outputFile: Path = Files.createTempFile("test", ".trig") rdfFormatUtil.turtleToQuadsFile( @@ -195,8 +190,8 @@ abstract class RdfFormatUtilSpec(featureToggle: FeatureToggle) extends CoreSpec "read Turtle, add a graph IRI to it, write it to an N-Quads file, and read back the N-Quads file" in { val graphIri = "http://example.org/data#" val rdfSource = RdfInputStreamSource( - new BufferedInputStream( - Files.newInputStream(Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.ttl")))) + new BufferedInputStream(Files.newInputStream(Paths.get("test_data/resourcesR2RV2/BookReiseInsHeiligeLand.ttl"))) + ) val outputFile: Path = Files.createTempFile("test", ".trig") rdfFormatUtil.turtleToQuadsFile( @@ -305,9 +300,11 @@ abstract class RdfFormatUtilSpec(featureToggle: FeatureToggle) extends CoreSpec assert(rdfModel.contains(expectedThingLabelStatement)) val byteArrayOutputStream = new ByteArrayOutputStream() - rdfFormatUtil.rdfModelToOutputStream(rdfModel = rdfModel, - outputStream = byteArrayOutputStream, - rdfFormat = Turtle) + rdfFormatUtil.rdfModelToOutputStream( + rdfModel = rdfModel, + outputStream = byteArrayOutputStream, + rdfFormat = Turtle + ) byteArrayOutputStream.close() val byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfModelSpec.scala index 4ee7db1236..469d0722b4 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfModelSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/RdfModelSpec.scala @@ -28,11 +28,11 @@ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.{CoreSpec, IRI} /** - * Tests implementations of [[RdfModel]]. - * - * @param featureToggle a feature toggle specifying which implementation of [[RdfModel]] should - * be used for the test. - */ + * Tests implementations of [[RdfModel]]. + * + * @param featureToggle a feature toggle specifying which implementation of [[RdfModel]] should + * be used for the test. + */ abstract class RdfModelSpec(featureToggle: FeatureToggle) extends CoreSpec { private val featureFactoryConfig: FeatureFactoryConfig = new TestFeatureFactoryConfig( testToggles = Set(featureToggle), @@ -44,17 +44,19 @@ abstract class RdfModelSpec(featureToggle: FeatureToggle) extends CoreSpec { private val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(featureFactoryConfig) /** - * Adds a statement, then searches for it by subject and predicate. - * - * @param subj the subject. - * @param pred the predicate. - * @param obj the object. - * @param context the context. - */ - private def addAndFindBySubjAndPred(subj: RdfResource, - pred: IriNode, - obj: RdfNode, - context: Option[IRI] = None): Unit = { + * Adds a statement, then searches for it by subject and predicate. + * + * @param subj the subject. + * @param pred the predicate. + * @param obj the object. + * @param context the context. + */ + private def addAndFindBySubjAndPred( + subj: RdfResource, + pred: IriNode, + obj: RdfNode, + context: Option[IRI] = None + ): Unit = { val statement: Statement = nodeFactory.makeStatement(subj = subj, pred = pred, obj = obj) model.addStatement(statement) assert(model.find(subj = Some(subj), pred = Some(pred), obj = None).toSet == Set(statement)) @@ -176,16 +178,16 @@ abstract class RdfModelSpec(featureToggle: FeatureToggle) extends CoreSpec { val graph1LabelStatement = nodeFactory.makeStatement( subj = nodeFactory.makeIriNode("http://example.org/6"), pred = labelPred, - obj = nodeFactory.makeDatatypeLiteral(value = "Lucky's Discount X-Wing Repair", - datatype = OntologyConstants.Xsd.String), + obj = nodeFactory + .makeDatatypeLiteral(value = "Lucky's Discount X-Wing Repair", datatype = OntologyConstants.Xsd.String), context = Some(context1) ) val graph1CommentStatement = nodeFactory.makeStatement( subj = nodeFactory.makeIriNode("http://example.org/6"), pred = commentPred, - obj = nodeFactory.makeDatatypeLiteral(value = "A safe flight or your money back", - datatype = OntologyConstants.Xsd.String), + obj = nodeFactory + .makeDatatypeLiteral(value = "A safe flight or your money back", datatype = OntologyConstants.Xsd.String), context = Some(context1) ) @@ -199,15 +201,16 @@ abstract class RdfModelSpec(featureToggle: FeatureToggle) extends CoreSpec { val graph2LabelStatement = nodeFactory.makeStatement( subj = nodeFactory.makeIriNode("http://example.org/7"), pred = labelPred, - obj = nodeFactory.makeDatatypeLiteral(value = "Mos Eisley Used Droids", datatype = OntologyConstants.Xsd.String), + obj = + nodeFactory.makeDatatypeLiteral(value = "Mos Eisley Used Droids", datatype = OntologyConstants.Xsd.String), context = Some(context2) ) val graph2CommentStatement = nodeFactory.makeStatement( subj = nodeFactory.makeIriNode("http://example.org/7"), pred = commentPred, - obj = nodeFactory.makeDatatypeLiteral(value = "All droids guaranteed for 10 seconds", - datatype = OntologyConstants.Xsd.String), + obj = nodeFactory + .makeDatatypeLiteral(value = "All droids guaranteed for 10 seconds", datatype = OntologyConstants.Xsd.String), context = Some(context2) ) @@ -222,11 +225,17 @@ abstract class RdfModelSpec(featureToggle: FeatureToggle) extends CoreSpec { assert(model.find(subj = None, pred = None, obj = None, context = Some(context1)).toSet == graph1) assert(model.find(subj = None, pred = None, obj = None, context = Some(context2)).toSet == graph2) assert( - model.find(subj = None, pred = Some(labelPred), obj = None).toSet == Set(graph1LabelStatement, - graph2LabelStatement)) + model.find(subj = None, pred = Some(labelPred), obj = None).toSet == Set( + graph1LabelStatement, + graph2LabelStatement + ) + ) assert( - model.find(subj = None, pred = Some(commentPred), obj = None).toSet == Set(graph1CommentStatement, - graph2CommentStatement)) + model.find(subj = None, pred = Some(commentPred), obj = None).toSet == Set( + graph1CommentStatement, + graph2CommentStatement + ) + ) model.removeStatement(graph1CommentStatement) assert(!model.contains(graph1CommentStatement)) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala index 26d3ab2553..7627d94777 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala @@ -31,20 +31,20 @@ import org.knora.webapi.messages.util.rdf._ object ShaclValidatorSpec { val config: String = """ - |app { - | shacl { - | shapes-dir = "test_data/shacl" - | } - |} - |""".stripMargin + |app { + | shacl { + | shapes-dir = "test_data/shacl" + | } + |} + |""".stripMargin } /** - * Tests implementations of [[ShaclValidator]]. - * - * @param featureToggle a feature toggle specifying which implementation of [[ShaclValidator]] should - * be used for the test. - */ + * Tests implementations of [[ShaclValidator]]. + * + * @param featureToggle a feature toggle specifying which implementation of [[ShaclValidator]] should + * be used for the test. + */ abstract class ShaclValidatorSpec(featureToggle: FeatureToggle) extends CoreSpec(ConfigFactory.parseString(ShaclValidatorSpec.config)) { private val featureFactoryConfig: FeatureFactoryConfig = new TestFeatureFactoryConfig( @@ -69,13 +69,13 @@ abstract class ShaclValidatorSpec(featureToggle: FeatureToggle) "accept valid RDF" in { val validRdfStr = """ - |@prefix ex: . - |@prefix foaf: . - |@prefix xsd: . - | - |ex:sally a foaf:Person ; - | foaf:age "30"^^xsd:int . - |""".stripMargin + |@prefix ex: . + |@prefix foaf: . + |@prefix xsd: . + | + |ex:sally a foaf:Person ; + | foaf:age "30"^^xsd:int . + |""".stripMargin val validRdfModel: RdfModel = rdfFormatUtil.parseToRdfModel( rdfStr = validRdfStr, @@ -96,13 +96,13 @@ abstract class ShaclValidatorSpec(featureToggle: FeatureToggle) "reject invalid RDF" in { val invalidRdfStr = """ - |@prefix ex: . - |@prefix foaf: . - |@prefix xsd: . - | - |ex:sally a foaf:Person ; - | foaf:age 20, "30"^^xsd:int . - |""".stripMargin + |@prefix ex: . + |@prefix foaf: . + |@prefix xsd: . + | + |ex:sally a foaf:Person ; + | foaf:age 20, "30"^^xsd:int . + |""".stripMargin val invalidRdfModel: RdfModel = rdfFormatUtil.parseToRdfModel( rdfStr = invalidRdfStr, diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtilSpec.scala index d38cb0f984..eec75f9786 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaFormatUtilSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOn} import org.knora.webapi.util.rdf.RdfFormatUtilSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.jenaimpl.JenaFormatUtil]]. - */ + * Tests [[org.knora.webapi.messages.util.rdf.jenaimpl.JenaFormatUtil]]. + */ class JenaFormatUtilSpec extends RdfFormatUtilSpec(FeatureToggle("jena-rdf-library", ToggleStateOn(1))) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaJsonLDUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaJsonLDUtilSpec.scala index 0be7c74bc2..70e8ffe2ef 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaJsonLDUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaJsonLDUtilSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOn} import org.knora.webapi.util.rdf.JsonLDUtilSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.JsonLDUtil]] using the Jena RDF API. - */ + * Tests [[org.knora.webapi.messages.util.rdf.JsonLDUtil]] using the Jena RDF API. + */ class JenaJsonLDUtilSpec extends JsonLDUtilSpec(FeatureToggle("jena-rdf-library", ToggleStateOn(1))) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaKnoraResponseV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaKnoraResponseV2Spec.scala index 735ed7e1c9..4623cc303d 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaKnoraResponseV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaKnoraResponseV2Spec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOn} import org.knora.webapi.util.rdf.KnoraResponseV2Spec /** - * Tests [[org.knora.webapi.messages.v2.responder.KnoraResponseV2]] with the Jena API. - */ + * Tests [[org.knora.webapi.messages.v2.responder.KnoraResponseV2]] with the Jena API. + */ class JenaKnoraResponseV2Spec extends KnoraResponseV2Spec(FeatureToggle("jena-rdf-library", ToggleStateOn(1))) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModelSpec.scala index 0338c9fad2..070531c8d2 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModelSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaModelSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature._ import org.knora.webapi.util.rdf.RdfModelSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.jenaimpl.JenaModel]]. - */ + * Tests [[org.knora.webapi.messages.util.rdf.jenaimpl.JenaModel]]. + */ class JenaModelSpec extends RdfModelSpec(FeatureToggle("jena-rdf-library", ToggleStateOn(1))) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidatorSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidatorSpec.scala index 42a44a71e2..e27a2c5599 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidatorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/jenaimpl/JenaShaclValidatorSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOn} import org.knora.webapi.util.rdf.ShaclValidatorSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.ShaclValidator]] using the Jena API. - */ + * Tests [[org.knora.webapi.messages.util.rdf.ShaclValidator]] using the Jena API. + */ class JenaShaclValidatorSpec extends ShaclValidatorSpec(FeatureToggle("jena-rdf-library", ToggleStateOn(1))) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtilSpec.scala index 74c1d8cb5d..b007a93706 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JFormatUtilSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOff} import org.knora.webapi.util.rdf.RdfFormatUtilSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.rdf4jimpl.RDF4JFormatUtil]]. - */ + * Tests [[org.knora.webapi.messages.util.rdf.rdf4jimpl.RDF4JFormatUtil]]. + */ class RDF4JFormatUtilSpec extends RdfFormatUtilSpec(FeatureToggle("jena-rdf-library", ToggleStateOff)) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JJsonLDUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JJsonLDUtilSpec.scala index 251ada999c..9e4ab2076b 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JJsonLDUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JJsonLDUtilSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOff} import org.knora.webapi.util.rdf.JsonLDUtilSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.JsonLDUtil]] using the RDF4J API. - */ + * Tests [[org.knora.webapi.messages.util.rdf.JsonLDUtil]] using the RDF4J API. + */ class RDF4JJsonLDUtilSpec extends JsonLDUtilSpec(FeatureToggle("jena-rdf-library", ToggleStateOff)) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JKnoraResponseV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JKnoraResponseV2Spec.scala index e8c0d89c3d..88e9b406ac 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JKnoraResponseV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JKnoraResponseV2Spec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOff} import org.knora.webapi.util.rdf.KnoraResponseV2Spec /** - * Tests [[org.knora.webapi.messages.v2.responder.KnoraResponseV2]] with the RDF4J API. - */ + * Tests [[org.knora.webapi.messages.v2.responder.KnoraResponseV2]] with the RDF4J API. + */ class RDF4JKnoraResponseV2Spec extends KnoraResponseV2Spec(FeatureToggle("jena-rdf-library", ToggleStateOff)) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModelSpec.scala index 6259bd7db7..d7889eff68 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModelSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JModelSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature._ import org.knora.webapi.util.rdf.RdfModelSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.rdf4jimpl.RDF4JModel]]. - */ + * Tests [[org.knora.webapi.messages.util.rdf.rdf4jimpl.RDF4JModel]]. + */ class RDF4JModelSpec extends RdfModelSpec(FeatureToggle("jena-rdf-library", ToggleStateOff)) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidatorSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidatorSpec.scala index feadb386d1..2248fe05c5 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidatorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/rdf4jimpl/RDF4JShaclValidatorSpec.scala @@ -23,6 +23,6 @@ import org.knora.webapi.feature.{FeatureToggle, ToggleStateOff} import org.knora.webapi.util.rdf.ShaclValidatorSpec /** - * Tests [[org.knora.webapi.messages.util.rdf.ShaclValidator]] using the RDF4J API. - */ + * Tests [[org.knora.webapi.messages.util.rdf.ShaclValidator]] using the RDF4J API. + */ class RDF4JShaclValidatorSpec extends ShaclValidatorSpec(FeatureToggle("jena-rdf-library", ToggleStateOff)) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/search/SparqlTransformerSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/search/SparqlTransformerSpec.scala index 23b87f6c6f..3c5158ffab 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/search/SparqlTransformerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/search/SparqlTransformerSpec.scala @@ -26,8 +26,8 @@ import org.knora.webapi.messages.{OntologyConstants, StringFormatter} import org.knora.webapi.util.ApacheLuceneSupport.LuceneQueryString /** - * Tests [[SparqlTransformer]]. - */ + * Tests [[SparqlTransformer]]. + */ class SparqlTransformerSpec extends CoreSpec() { protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -53,7 +53,8 @@ class SparqlTransformerSpec extends CoreSpec() { "create a syntactically valid base name from a given ontology IRI" in { val baseName = SparqlTransformer.escapeEntityForVariable( - IriRef("http://www.knora.org/ontology/0803/incunabula#book".toSmartIri)) + IriRef("http://www.knora.org/ontology/0803/incunabula#book".toSmartIri) + ) baseName should ===("httpwwwknoraorgontology0803incunabulabook") @@ -127,9 +128,11 @@ class SparqlTransformerSpec extends CoreSpec() { obj = IriRef("http://www.knora.org/ontology/0001/anything#Thing".toSmartIri) ) val hasValueStatement = - StatementPattern.makeExplicit(subj = QueryVariable("foo"), - pred = IriRef("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri), - obj = QueryVariable("text")) + StatementPattern.makeExplicit( + subj = QueryVariable("foo"), + pred = IriRef("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri), + obj = QueryVariable("text") + ) val bindPattern = BindPattern(variable = QueryVariable("foo"), expression = IriRef("http://rdfh.ch/0001/a-thing".toSmartIri)) @@ -152,13 +155,17 @@ class SparqlTransformerSpec extends CoreSpec() { "move a Lucene query pattern to the beginning of a block" in { val hasValueStatement = - StatementPattern.makeExplicit(subj = QueryVariable("foo"), - pred = IriRef("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri), - obj = QueryVariable("text")) + StatementPattern.makeExplicit( + subj = QueryVariable("foo"), + pred = IriRef("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri), + obj = QueryVariable("text") + ) val valueHasStringStatement = - StatementPattern.makeExplicit(subj = QueryVariable("text"), - pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), - QueryVariable("text__valueHasString")) + StatementPattern.makeExplicit( + subj = QueryVariable("text"), + pred = IriRef(OntologyConstants.KnoraBase.ValueHasString.toSmartIri), + QueryVariable("text__valueHasString") + ) val luceneQueryPattern = LuceneQueryPattern( subj = QueryVariable("text"), @@ -190,9 +197,10 @@ class SparqlTransformerSpec extends CoreSpec() { pred = IriRef(OntologyConstants.Rdf.Type.toSmartIri), obj = IriRef("http://www.knora.org/ontology/0001/anything#Thing".toSmartIri) ) - val expandedStatements = SparqlTransformer.transformStatementInWhereForNoInference(statementPattern = - typeStatement, - simulateInference = true) + val expandedStatements = SparqlTransformer.transformStatementInWhereForNoInference( + statementPattern = typeStatement, + simulateInference = true + ) val expectedStatements: Seq[StatementPattern] = Seq( StatementPattern( @@ -223,12 +231,15 @@ class SparqlTransformerSpec extends CoreSpec() { "expand a statement with a property IRI to simulate RDFS inference" in { val hasValueStatement = - StatementPattern.makeInferred(subj = QueryVariable("foo"), - pred = IriRef("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri), - obj = QueryVariable("text")) - val expandedStatements = SparqlTransformer.transformStatementInWhereForNoInference(statementPattern = - hasValueStatement, - simulateInference = true) + StatementPattern.makeInferred( + subj = QueryVariable("foo"), + pred = IriRef("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri), + obj = QueryVariable("text") + ) + val expandedStatements = SparqlTransformer.transformStatementInWhereForNoInference( + statementPattern = hasValueStatement, + simulateInference = true + ) val expectedStatements: Seq[StatementPattern] = Seq( StatementPattern( diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParserSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParserSpec.scala index 2cc6c9dc04..df6c0ba225 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParserSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/GravsearchParserSpec.scala @@ -27,347 +27,347 @@ import org.knora.webapi.messages.util.search.gravsearch.GravsearchParser import org.knora.webapi.{ApiV2Complex, ApiV2Simple, CoreSpec} /** - * Tests [[GravsearchParser]]. - */ + * Tests [[GravsearchParser]]. + */ class GravsearchParserSpec extends CoreSpec() { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance val Query: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:publisher ?bookPublisher . - | ?book incunabula:publoc ?bookPubLoc . - | ?page incunabula:isPartOf ?book . - |} WHERE { - | ?book a incunabula:book . - | - | OPTIONAL { - | ?book incunabula:publisher ?bookPublisher . - | ?book incunabula:publoc ?bookPubLoc . - | } - | - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - | ?page a incunabula:page . - | ?page incunabula:isPartOf ?book . - | - | { - | ?page incunabula:pagenum "a7r"^^xsd:string . - | ?page incunabula:seqnum "14"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a8r"^^xsd:string . - | ?page incunabula:seqnum "16"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a9r"^^xsd:string . - | ?page incunabula:seqnum ?seqnum. - | FILTER(?seqnum > "17"^^xsd:integer) - | } - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:publisher ?bookPublisher . + | ?book incunabula:publoc ?bookPubLoc . + | ?page incunabula:isPartOf ?book . + |} WHERE { + | ?book a incunabula:book . + | + | OPTIONAL { + | ?book incunabula:publisher ?bookPublisher . + | ?book incunabula:publoc ?bookPubLoc . + | } + | + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + | ?page a incunabula:page . + | ?page incunabula:isPartOf ?book . + | + | { + | ?page incunabula:pagenum "a7r"^^xsd:string . + | ?page incunabula:seqnum "14"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a8r"^^xsd:string . + | ?page incunabula:seqnum "16"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a9r"^^xsd:string . + | ?page incunabula:seqnum ?seqnum. + | FILTER(?seqnum > "17"^^xsd:integer) + | } + |} """.stripMargin // val ParsedQuery = ??? val QueryWithBind: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | BIND( AS ?thing) - | ?thing a knora-api:Resource . - | ?thing a anything:Thing . - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | BIND( AS ?thing) + | ?thing a knora-api:Resource . + | ?thing a anything:Thing . + |} """.stripMargin val QueryWithFilterNotExists: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing a anything:Thing . - | - | FILTER NOT EXISTS { - | ?thing anything:hasOtherThing ?aThing . - | } - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing a anything:Thing . + | + | FILTER NOT EXISTS { + | ?thing anything:hasOtherThing ?aThing . + | } + |} """.stripMargin val QueryWithMinus: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing a anything:Thing . - | - | MINUS { - | ?thing anything:hasOtherThing ?aThing . - | } - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing a anything:Thing . + | + | MINUS { + | ?thing anything:hasOtherThing ?aThing . + | } + |} """.stripMargin val QueryWithOffset: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true. - |} WHERE { - | ?thing a anything:Thing . - |} OFFSET 10 + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true. + |} WHERE { + | ?thing a anything:Thing . + |} OFFSET 10 """.stripMargin val QueryStrWithNestedOptional: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?book incunabula:title ?bookTitle . - | ?page incunabula:isPartOf ?book . - |} WHERE { - | ?book a incunabula:book . - | - | OPTIONAL { - | ?book incunabula:publisher ?publisher . - | - | FILTER(?publisher = "Lienhart Ysenhut"^^xsd:string) - | - | OPTIONAL { - | ?book incunabula:title ?bookTitle . - | } - | } - | - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?book incunabula:title ?bookTitle . + | ?page incunabula:isPartOf ?book . + |} WHERE { + | ?book a incunabula:book . + | + | OPTIONAL { + | ?book incunabula:publisher ?publisher . + | + | FILTER(?publisher = "Lienhart Ysenhut"^^xsd:string) + | + | OPTIONAL { + | ?book incunabula:title ?bookTitle . + | } + | } + | + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + |} """.stripMargin val QueryWithWrongFilter: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - |} WHERE { - | ?book a incunabula:book . - | ?book incunabula:pubdate ?pubdate . - | FILTER(BOUND(?pubdate)) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + |} WHERE { + | ?book a incunabula:book . + | ?book incunabula:pubdate ?pubdate . + | FILTER(BOUND(?pubdate)) + |} """.stripMargin val QueryWithInternalEntityIri: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - |} WHERE { - | ?book a incunabula:book . - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + |} WHERE { + | ?book a incunabula:book . + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + |} """.stripMargin val SparqlSelect: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |SELECT ?subject ?object - |WHERE { - | ?subject a incunabula:book . - | ?book incunabula:pubdate ?object . - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |SELECT ?subject ?object + |WHERE { + | ?subject a incunabula:book . + | ?book incunabula:pubdate ?object . + |} """.stripMargin val SparqlDescribe: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |DESCRIBE ?book - |WHERE { - | ?book a incunabula:book . - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |DESCRIBE ?book + |WHERE { + | ?book a incunabula:book . + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + |} """.stripMargin val SparqlInsert: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |INSERT DATA { - | a incunabula:book . - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |INSERT DATA { + | a incunabula:book . + |} """.stripMargin val SparqlDelete: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |DELETE { - | a incunabula:book . - |} WHERE { - | ?book a incunabula:book . - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |DELETE { + | a incunabula:book . + |} WHERE { + | ?book a incunabula:book . + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + |} """.stripMargin val QueryWithLeftNestedUnion: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?page incunabula:isPartOf ?book . - |} WHERE { - | ?book a incunabula:book . - | ?book incunabula:publisher "Lienhart Ysenhut"^^xsd:string . - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - | ?page a incunabula:page . - | ?page incunabula:isPartOf ?book . - | - | { - | ?page incunabula:pagenum "a6r"^^xsd:string . - | ?page incunabula:seqnum "12"^^xsd:integer. - | - | { - | ?page incunabula:pagenum "a7r"^^xsd:string . - | ?page incunabula:seqnum "14"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a8r"^^xsd:string . - | ?page incunabula:seqnum "16"^^xsd:integer. - | } - | } UNION { - | ?page incunabula:pagenum "a9r"^^xsd:string . - | ?page incunabula:seqnum "18"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a10r"^^xsd:string . - | ?page incunabula:seqnum "20"^^xsd:integer. - | } - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?page incunabula:isPartOf ?book . + |} WHERE { + | ?book a incunabula:book . + | ?book incunabula:publisher "Lienhart Ysenhut"^^xsd:string . + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + | ?page a incunabula:page . + | ?page incunabula:isPartOf ?book . + | + | { + | ?page incunabula:pagenum "a6r"^^xsd:string . + | ?page incunabula:seqnum "12"^^xsd:integer. + | + | { + | ?page incunabula:pagenum "a7r"^^xsd:string . + | ?page incunabula:seqnum "14"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a8r"^^xsd:string . + | ?page incunabula:seqnum "16"^^xsd:integer. + | } + | } UNION { + | ?page incunabula:pagenum "a9r"^^xsd:string . + | ?page incunabula:seqnum "18"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a10r"^^xsd:string . + | ?page incunabula:seqnum "20"^^xsd:integer. + | } + |} """.stripMargin val QueryStrWithRightNestedUnion: String = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-api: - |PREFIX incunabula: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | ?page a ?pageType . - | ?page incunabula:isPartOf ?book . - |} WHERE { - | ?book a incunabula:book . - | ?book incunabula:publisher "Lienhart Ysenhut"^^xsd:string . - | ?book incunabula:pubdate ?pubdate . - | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) - | ?page a incunabula:page . - | ?page incunabula:isPartOf ?book . - | - | { - | ?page incunabula:pagenum "a7r"^^xsd:string . - | ?page incunabula:seqnum "14"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a8r"^^xsd:string . - | ?page incunabula:seqnum "16"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a9r"^^xsd:string . - | ?page incunabula:seqnum "18"^^xsd:integer. - | - | { - | ?page incunabula:pagenum "a10r"^^xsd:string . - | ?page incunabula:seqnum "20"^^xsd:integer. - | } UNION { - | ?page incunabula:pagenum "a11r"^^xsd:string . - | ?page incunabula:seqnum "22"^^xsd:integer. - | } - | } - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-api: + |PREFIX incunabula: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | ?page a ?pageType . + | ?page incunabula:isPartOf ?book . + |} WHERE { + | ?book a incunabula:book . + | ?book incunabula:publisher "Lienhart Ysenhut"^^xsd:string . + | ?book incunabula:pubdate ?pubdate . + | FILTER(?pubdate < "GREGORIAN:1500"^^xsd:string) + | ?page a incunabula:page . + | ?page incunabula:isPartOf ?book . + | + | { + | ?page incunabula:pagenum "a7r"^^xsd:string . + | ?page incunabula:seqnum "14"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a8r"^^xsd:string . + | ?page incunabula:seqnum "16"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a9r"^^xsd:string . + | ?page incunabula:seqnum "18"^^xsd:integer. + | + | { + | ?page incunabula:pagenum "a10r"^^xsd:string . + | ?page incunabula:seqnum "20"^^xsd:integer. + | } UNION { + | ?page incunabula:pagenum "a11r"^^xsd:string . + | ?page incunabula:seqnum "22"^^xsd:integer. + | } + | } + |} """.stripMargin val QueryStrWithUnionInOptional: String = """ - |PREFIX knora-api: - |PREFIX test: - |CONSTRUCT { - | ?Project knora-api:isMainResource true . - | ?isInProject test:isInProject ?Project . - |} WHERE { - | ?Project a knora-api:Resource . - | ?Project a test:Project . - | - | OPTIONAL { - | ?isInProject test:isInProject ?Project . - | test:isInProject knora-api:objectType knora-api:Resource . - | ?isInProject a knora-api:Resource . - | { ?isInProject a test:BibliographicNotice . } UNION { ?isInProject a test:Person . } - | } - |} + |PREFIX knora-api: + |PREFIX test: + |CONSTRUCT { + | ?Project knora-api:isMainResource true . + | ?isInProject test:isInProject ?Project . + |} WHERE { + | ?Project a knora-api:Resource . + | ?Project a test:Project . + | + | OPTIONAL { + | ?isInProject test:isInProject ?Project . + | test:isInProject knora-api:objectType knora-api:Resource . + | ?isInProject a knora-api:Resource . + | { ?isInProject a test:BibliographicNotice . } UNION { ?isInProject a test:Person . } + | } + |} """.stripMargin val ParsedQueryWithUnionInOptional: ConstructQuery = ConstructQuery( @@ -418,19 +418,26 @@ class GravsearchParserSpec extends CoreSpec() { pred = IriRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri), obj = IriRef("http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri) ), - UnionPattern(blocks = Vector( - Vector(StatementPattern( - subj = QueryVariable(variableName = "isInProject"), - pred = IriRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri), - obj = IriRef("http://0.0.0.0:3333/ontology/0666/test/simple/v2#BibliographicNotice".toSmartIri) - )), - Vector(StatementPattern( - subj = QueryVariable(variableName = "isInProject"), - pred = IriRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri), - obj = IriRef("http://0.0.0.0:3333/ontology/0666/test/simple/v2#Person".toSmartIri) - )) - )) - )) + UnionPattern(blocks = + Vector( + Vector( + StatementPattern( + subj = QueryVariable(variableName = "isInProject"), + pred = IriRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri), + obj = IriRef("http://0.0.0.0:3333/ontology/0666/test/simple/v2#BibliographicNotice".toSmartIri) + ) + ), + Vector( + StatementPattern( + subj = QueryVariable(variableName = "isInProject"), + pred = IriRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri), + obj = IriRef("http://0.0.0.0:3333/ontology/0666/test/simple/v2#Person".toSmartIri) + ) + ) + ) + ) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "Project"), @@ -452,135 +459,135 @@ class GravsearchParserSpec extends CoreSpec() { val QueryForAThingRelatingToAnotherThing: String = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing knora-api:hasLinkTo . - |} WHERE { - | ?thing a anything:Thing . - | - | ?thing ?linkingProp . - | FILTER(?linkingProp = anything:isPartOfOtherThing || ?linkingProp = anything:hasOtherThing) - | - |} + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing knora-api:hasLinkTo . + |} WHERE { + | ?thing a anything:Thing . + | + | ?thing ?linkingProp . + | FILTER(?linkingProp = anything:isPartOfOtherThing || ?linkingProp = anything:hasOtherThing) + | + |} """.stripMargin val queryWithFilterContainingRegex: String = """ - | PREFIX knora-api: - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes ?propVal0 . - | - | } WHERE { - | - | ?mainRes a knora-api:Resource . - | - | ?mainRes a . - | - | - | ?mainRes ?propVal0 . - | knora-api:objectType . - | ?propVal0 a . - | - | FILTER regex(?propVal0, "Zeit", "i") - | - | } + | PREFIX knora-api: + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes ?propVal0 . + | + | } WHERE { + | + | ?mainRes a knora-api:Resource . + | + | ?mainRes a . + | + | + | ?mainRes ?propVal0 . + | knora-api:objectType . + | ?propVal0 a . + | + | FILTER regex(?propVal0, "Zeit", "i") + | + | } """.stripMargin val QueryWithMatchFunction: String = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | FILTER(knora-api:match(?text, "foo")) - |} + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | FILTER(knora-api:match(?text, "foo")) + |} """.stripMargin val QueryWithMatchTextFunction: String = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | FILTER(knora-api:matchText(?text, "foo")) - |} + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | FILTER(knora-api:matchText(?text, "foo")) + |} """.stripMargin val QueryWithFilterContainingLang: String = """ - |PREFIX knora-api: - |PREFIX anything: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing a anything:Thing . - | ?thing anything:hasText ?text . - | FILTER(lang(?text) = "en") - |} + |PREFIX knora-api: + |PREFIX anything: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing a anything:Thing . + | ?thing anything:hasText ?text . + | FILTER(lang(?text) = "en") + |} """.stripMargin // val ParsedQueryWithLangFunction = ??? val QueryWithFilterInOptional: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - | CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 beol:hasFamilyName ?name . - | - | } WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | ?letter beol:creationDate ?date . - | - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | ?letter ?linkingProp1 ?person1 . - | - | ?person1 a knora-api:Resource . - | - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | OPTIONAL { - | ?person1 beol:hasFamilyName ?name . - | - | beol:hasFamilyName knora-api:objectType xsd:string . - | ?name a xsd:string . - | - | FILTER(?name = "Meier") - | } - | - | } ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + | CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 beol:hasFamilyName ?name . + | + | } WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | ?letter beol:creationDate ?date . + | + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | ?letter ?linkingProp1 ?person1 . + | + | ?person1 a knora-api:Resource . + | + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | OPTIONAL { + | ?person1 beol:hasFamilyName ?name . + | + | beol:hasFamilyName knora-api:objectType xsd:string . + | ?name a xsd:string . + | + | FILTER(?name = "Meier") + | } + | + | } ORDER BY ?date """.stripMargin private val ParsedQueryWithFilterInOptional: ConstructQuery = ConstructQuery( @@ -631,7 +638,8 @@ class GravsearchParserSpec extends CoreSpec() { OrderCriterion( queryVariable = QueryVariable(variableName = "date"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -792,15 +800,18 @@ class GravsearchParserSpec extends CoreSpec() { ), namedGraph = None ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "name"), - operator = CompareExpressionOperator.EQUALS, - rightArg = XsdLiteral( - value = "Meier", - datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "name"), + operator = CompareExpressionOperator.EQUALS, + rightArg = XsdLiteral( + value = "Meier", + datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri + ) ) - )) - )), + ) + ) + ), FilterPattern( expression = OrExpression( leftArg = CompareExpression( @@ -819,7 +830,8 @@ class GravsearchParserSpec extends CoreSpec() { propertyPathOperator = None ) ) - )) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "name"), @@ -958,7 +970,8 @@ class GravsearchParserSpec extends CoreSpec() { obj = QueryVariable(variableName = "bookPubLoc"), namedGraph = None ) - )), + ) + ), StatementPattern( subj = QueryVariable(variableName = "book"), pred = IriRef( @@ -1065,16 +1078,19 @@ class GravsearchParserSpec extends CoreSpec() { obj = QueryVariable(variableName = "seqnum"), namedGraph = None ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "seqnum"), - operator = CompareExpressionOperator.GREATER_THAN, - rightArg = XsdLiteral( - value = "17", - datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "seqnum"), + operator = CompareExpressionOperator.GREATER_THAN, + rightArg = XsdLiteral( + value = "17", + datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + ) ) - )) + ) ) - )), + ) + ), FilterPattern( expression = CompareExpression( leftArg = QueryVariable(variableName = "pubdate"), @@ -1083,7 +1099,8 @@ class GravsearchParserSpec extends CoreSpec() { value = "GREGORIAN:1500", datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri ) - )) + ) + ) ), positiveEntities = Set( IriRef( @@ -1151,7 +1168,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1292,7 +1310,8 @@ class GravsearchParserSpec extends CoreSpec() { propertyPathOperator = None ) ) - )) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "thing"), @@ -1344,7 +1363,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1365,15 +1385,18 @@ class GravsearchParserSpec extends CoreSpec() { namedGraph = None ), FilterNotExistsPattern( - patterns = Vector(StatementPattern( - subj = QueryVariable(variableName = "thing"), - pred = IriRef( - iri = "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#hasOtherThing".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "aThing"), - namedGraph = None - ))) + patterns = Vector( + StatementPattern( + subj = QueryVariable(variableName = "thing"), + pred = IriRef( + iri = "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#hasOtherThing".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "aThing"), + namedGraph = None + ) + ) + ) ), positiveEntities = Set( IriRef( @@ -1408,7 +1431,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1429,15 +1453,18 @@ class GravsearchParserSpec extends CoreSpec() { namedGraph = None ), MinusPattern( - patterns = Vector(StatementPattern( - subj = QueryVariable(variableName = "thing"), - pred = IriRef( - iri = "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#hasOtherThing".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "aThing"), - namedGraph = None - ))) + patterns = Vector( + StatementPattern( + subj = QueryVariable(variableName = "thing"), + pred = IriRef( + iri = "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#hasOtherThing".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "aThing"), + namedGraph = None + ) + ) + ) ), positiveEntities = Set( IriRef( @@ -1472,7 +1499,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1491,7 +1519,8 @@ class GravsearchParserSpec extends CoreSpec() { propertyPathOperator = None ), namedGraph = None - )), + ) + ), positiveEntities = Set( IriRef( iri = "http://0.0.0.0:3333/ontology/0001/anything/simple/v2#Thing".toSmartIri, @@ -1608,7 +1637,8 @@ class GravsearchParserSpec extends CoreSpec() { textExpr = QueryVariable(variableName = "propVal0"), pattern = "Zeit", modifier = Some("i") - )) + ) + ) ), positiveEntities = Set( IriRef( @@ -1660,7 +1690,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1702,7 +1733,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri ) ) - )) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "thing"), @@ -1742,7 +1774,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1784,7 +1817,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri ) ) - )) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "thing"), @@ -1824,7 +1858,8 @@ class GravsearchParserSpec extends CoreSpec() { datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri ), namedGraph = None - )), + ) + ), querySchema = Some(ApiV2Simple) ), querySchema = Some(ApiV2Simple), @@ -1861,7 +1896,8 @@ class GravsearchParserSpec extends CoreSpec() { value = "en", datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri ) - )) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "thing"), @@ -1951,24 +1987,31 @@ class GravsearchParserSpec extends CoreSpec() { obj = QueryVariable(variableName = "publisher"), namedGraph = None ), - OptionalPattern(patterns = Vector(StatementPattern( - subj = QueryVariable(variableName = "book"), - pred = IriRef( - iri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "bookTitle"), - namedGraph = None - ))), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "publisher"), - operator = CompareExpressionOperator.EQUALS, - rightArg = XsdLiteral( - value = "Lienhart Ysenhut", - datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri + OptionalPattern(patterns = + Vector( + StatementPattern( + subj = QueryVariable(variableName = "book"), + pred = IriRef( + iri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "bookTitle"), + namedGraph = None + ) ) - )) - )), + ), + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "publisher"), + operator = CompareExpressionOperator.EQUALS, + rightArg = XsdLiteral( + value = "Lienhart Ysenhut", + datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri + ) + ) + ) + ) + ), StatementPattern( subj = QueryVariable(variableName = "book"), pred = IriRef( @@ -1986,7 +2029,8 @@ class GravsearchParserSpec extends CoreSpec() { value = "GREGORIAN:1500", datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri ) - )) + ) + ) ), positiveEntities = Set( IriRef( @@ -2029,21 +2073,21 @@ class GravsearchParserSpec extends CoreSpec() { val QueryWithIriArgInFunction: String = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX beol: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasText ?text . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffLinkTag . - | ?standoffLinkTag a knora-api:StandoffLinkTag . - | - | FILTER knora-api:standoffLink(?letter, ?standoffLinkTag, ) - |} + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX beol: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasText ?text . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffLinkTag . + | ?standoffLinkTag a knora-api:StandoffLinkTag . + | + | FILTER knora-api:standoffLink(?letter, ?standoffLinkTag, ) + |} """.stripMargin val ParsedQueryWithIriArgInFunction: ConstructQuery = ConstructQuery( @@ -2134,7 +2178,8 @@ class GravsearchParserSpec extends CoreSpec() { propertyPathOperator = None ) ) - )) + ) + ) ), positiveEntities = Set( QueryVariable(variableName = "standoffLinkTag"), diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec.scala index 9efe027484..4a0d3d89f2 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec.scala @@ -26,9 +26,11 @@ private object CountQueryHandler { val anythingUser: UserADM = SharedTestDataADM.anythingAdminUser - def transformQuery(query: String, - responderData: ResponderData, - featureFactoryConfig: FeatureFactoryConfig): SelectQuery = { + def transformQuery( + query: String, + responderData: ResponderData, + featureFactoryConfig: FeatureFactoryConfig + ): SelectQuery = { val constructQuery = GravsearchParser.parseQuery(query) @@ -76,27 +78,27 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor val inputQueryWithDecimalOptionalSortCriterionAndFilter: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | - | FILTER(?decimal > "2"^^xsd:decimal) - | } - |} ORDER BY ASC(?decimal) + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | + | FILTER(?decimal > "2"^^xsd:decimal) + | } + |} ORDER BY ASC(?decimal) """.stripMargin val transformedQueryWithDecimalOptionalSortCriterionAndFilter: SelectQuery = @@ -106,7 +108,8 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor inputVariable = QueryVariable(variableName = "thing"), distinct = true, outputVariableName = "count" - )), + ) + ), offset = 0, groupBy = Nil, orderBy = Nil, @@ -126,7 +129,8 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -165,7 +169,8 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "decimal"), @@ -178,17 +183,21 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "decimal__valueHasDecimal"), - operator = CompareExpressionOperator.GREATER_THAN, - rightArg = XsdLiteral( - value = "2", - datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "decimal__valueHasDecimal"), + operator = CompareExpressionOperator.GREATER_THAN, + rightArg = XsdLiteral( + value = "2", + datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + ) ) - )) - )) + ) + ) + ) ).toSeq, positiveEntities = Set(), querySchema = None @@ -199,26 +208,26 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor val inputQueryWithDecimalOptionalSortCriterionAndFilterComplex: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | - | ?decimal knora-api:decimalValueAsDecimal ?decimalVal . - | - | FILTER(?decimalVal > "2"^^xsd:decimal) - | } - |} ORDER BY ASC(?decimal) + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | + | ?decimal knora-api:decimalValueAsDecimal ?decimalVal . + | + | FILTER(?decimalVal > "2"^^xsd:decimal) + | } + |} ORDER BY ASC(?decimal) """.stripMargin val transformedQueryWithDecimalOptionalSortCriterionAndFilterComplex: SelectQuery = @@ -229,7 +238,8 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor outputVariableName = "count", distinct = true, inputVariable = QueryVariable(variableName = "thing") - )), + ) + ), offset = 0, groupBy = Nil, orderBy = Nil, @@ -249,7 +259,8 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -297,17 +308,21 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "decimalVal"), - operator = CompareExpressionOperator.GREATER_THAN, - rightArg = XsdLiteral( - value = "2", - datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "decimalVal"), + operator = CompareExpressionOperator.GREATER_THAN, + rightArg = XsdLiteral( + value = "2", + datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + ) ) - )) - )) + ) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -321,9 +336,11 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor "transform an input query with a decimal as an optional sort criterion and a filter" in { val transformedQuery = - CountQueryHandler.transformQuery(inputQueryWithDecimalOptionalSortCriterionAndFilter, - responderData, - defaultFeatureFactoryConfig) + CountQueryHandler.transformQuery( + inputQueryWithDecimalOptionalSortCriterionAndFilter, + responderData, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDecimalOptionalSortCriterionAndFilter) @@ -332,9 +349,11 @@ class NonTriplestoreSpecificGravsearchToCountPrequeryTransformerSpec extends Cor "transform an input query with a decimal as an optional sort criterion and a filter (submitted in complex schema)" in { val transformedQuery = - CountQueryHandler.transformQuery(inputQueryWithDecimalOptionalSortCriterionAndFilterComplex, - responderData, - defaultFeatureFactoryConfig) + CountQueryHandler.transformQuery( + inputQueryWithDecimalOptionalSortCriterionAndFilterComplex, + responderData, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDecimalOptionalSortCriterionAndFilterComplex) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec.scala index 883fd58048..5499d51b70 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec.scala @@ -28,10 +28,12 @@ private object QueryHandler { val anythingUser: UserADM = SharedTestDataADM.anythingAdminUser - def transformQuery(query: String, - responderData: ResponderData, - settings: KnoraSettingsImpl, - featureFactoryConfig: FeatureFactoryConfig): SelectQuery = { + def transformQuery( + query: String, + responderData: ResponderData, + settings: KnoraSettingsImpl, + featureFactoryConfig: FeatureFactoryConfig + ): SelectQuery = { val constructQuery = GravsearchParser.parseQuery(query) @@ -77,42 +79,42 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val inputQueryWithDateNonOptionalSortCriterion: String = """ - |PREFIX knora-api: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | ?thing onto:hasDate ?date . - | onto:hasDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | ?thing onto:hasDate ?date . + | onto:hasDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + |} + |ORDER BY DESC(?date) """.stripMargin val inputQueryWithDateNonOptionalSortCriterionComplex: String = """ - |PREFIX knora-api: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | ?thing onto:hasDate ?date . - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | ?thing onto:hasDate ?date . + | + |} + |ORDER BY DESC(?date) """.stripMargin val transformedQueryWithDateNonOptionalSortCriterion: SelectQuery = @@ -156,7 +158,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -181,7 +184,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "date"), @@ -194,7 +198,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ) ).toSeq, positiveEntities = Set(), @@ -206,47 +211,47 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val inputQueryWithDateNonOptionalSortCriterionAndFilter: String = """ - |PREFIX knora-api: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | ?thing onto:hasDate ?date . - | onto:hasDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | FILTER(?date > "GREGORIAN:2012-01-01"^^knora-api:Date) - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | ?thing onto:hasDate ?date . + | onto:hasDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | FILTER(?date > "GREGORIAN:2012-01-01"^^knora-api:Date) + | + |} + |ORDER BY DESC(?date) """.stripMargin val inputQueryWithDateNonOptionalSortCriterionAndFilterComplex: String = """ - |PREFIX knora-api: - |PREFIX knora-api-simple: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | ?thing onto:hasDate ?date . - | - | FILTER(knora-api:toSimpleDate(?date) > "GREGORIAN:2012-01-01"^^knora-api-simple:Date) - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX knora-api-simple: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | ?thing onto:hasDate ?date . + | + | FILTER(knora-api:toSimpleDate(?date) > "GREGORIAN:2012-01-01"^^knora-api-simple:Date) + | + |} + |ORDER BY DESC(?date) """.stripMargin val transformedQueryWithDateNonOptionalSortCriterionAndFilter: SelectQuery = @@ -290,7 +295,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -315,7 +321,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "date"), @@ -328,7 +335,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), FilterPattern( expression = CompareExpression( @@ -338,7 +346,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec value = "2455928", datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri ) - )) + ) + ) ).toSeq, positiveEntities = Set(), querySchema = None @@ -349,50 +358,50 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val inputQueryWithDateOptionalSortCriterion: String = """ - |PREFIX knora-api: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | OPTIONAL { - | - | ?thing onto:hasDate ?date . - | onto:hasDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | } - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | OPTIONAL { + | + | ?thing onto:hasDate ?date . + | onto:hasDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | } + | + |} + |ORDER BY DESC(?date) """.stripMargin val inputQueryWithDateOptionalSortCriterionComplex: String = """ - |PREFIX knora-api: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | OPTIONAL { - | - | ?thing onto:hasDate ?date . - | - | } - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | OPTIONAL { + | + | ?thing onto:hasDate ?date . + | + | } + | + |} + |ORDER BY DESC(?date) """.stripMargin val transformedQueryWithDateOptionalSortCriterion: SelectQuery = @@ -436,7 +445,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -475,7 +485,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "date"), @@ -488,9 +499,11 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ) - )) + ) + ) ).toSeq, positiveEntities = Set(), querySchema = None @@ -501,53 +514,53 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val inputQueryWithDateOptionalSortCriterionAndFilter: String = """ - |PREFIX knora-api: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | OPTIONAL { - | - | ?thing onto:hasDate ?date . - | onto:hasDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | - | FILTER(?date > "GREGORIAN:2012-01-01"^^knora-api:Date) - | } - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | OPTIONAL { + | + | ?thing onto:hasDate ?date . + | onto:hasDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | + | FILTER(?date > "GREGORIAN:2012-01-01"^^knora-api:Date) + | } + | + |} + |ORDER BY DESC(?date) """.stripMargin val inputQueryWithDateOptionalSortCriterionAndFilterComplex: String = """ - |PREFIX knora-api: - |PREFIX knora-api-simple: - |PREFIX onto: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | ?thing onto:hasDate ?date . - |} WHERE { - | - | ?thing a knora-api:Resource . - | ?thing a onto:Thing . - | - | OPTIONAL { - | - | ?thing onto:hasDate ?date . - | - | FILTER(knora-api:toSimpleDate(?date) > "GREGORIAN:2012-01-01"^^knora-api-simple:Date) - | } - | - |} - |ORDER BY DESC(?date) + |PREFIX knora-api: + |PREFIX knora-api-simple: + |PREFIX onto: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | ?thing onto:hasDate ?date . + |} WHERE { + | + | ?thing a knora-api:Resource . + | ?thing a onto:Thing . + | + | OPTIONAL { + | + | ?thing onto:hasDate ?date . + | + | FILTER(knora-api:toSimpleDate(?date) > "GREGORIAN:2012-01-01"^^knora-api-simple:Date) + | } + | + |} + |ORDER BY DESC(?date) """.stripMargin val transformedQueryWithDateOptionalSortCriterionAndFilter: SelectQuery = @@ -557,7 +570,7 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec GroupConcat( inputVariable = QueryVariable(variableName = "date"), separator = StringFormatter.INFORMATION_SEPARATOR_ONE, - outputVariableName = "date__Concat", + outputVariableName = "date__Concat" ) ), offset = 0, @@ -591,7 +604,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -630,7 +644,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "date"), @@ -643,17 +658,21 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "date__valueHasStartJDN"), - operator = CompareExpressionOperator.GREATER_THAN, - rightArg = XsdLiteral( - value = "2455928", - datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "date__valueHasStartJDN"), + operator = CompareExpressionOperator.GREATER_THAN, + rightArg = XsdLiteral( + value = "2455928", + datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + ) ) - )) - )) + ) + ) + ) ).toSeq, positiveEntities = Set(), querySchema = None @@ -664,45 +683,45 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val inputQueryWithDecimalOptionalSortCriterion: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | } - |} ORDER BY ASC(?decimal) + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | } + |} ORDER BY ASC(?decimal) """.stripMargin val inputQueryWithDecimalOptionalSortCriterionComplex: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | } - |} ORDER BY ASC(?decimal) + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | } + |} ORDER BY ASC(?decimal) """.stripMargin val transformedQueryWithDecimalOptionalSortCriterion: SelectQuery = @@ -712,7 +731,7 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec GroupConcat( inputVariable = QueryVariable(variableName = "decimal"), separator = StringFormatter.INFORMATION_SEPARATOR_ONE, - outputVariableName = "decimal__Concat", + outputVariableName = "decimal__Concat" ) ), offset = 0, @@ -746,7 +765,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -785,7 +805,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "decimal"), @@ -798,9 +819,11 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ) - )) + ) + ) ).toSeq, positiveEntities = Set(), querySchema = None @@ -811,51 +834,51 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val inputQueryWithDecimalOptionalSortCriterionAndFilter: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | anything:hasDecimal knora-api:objectType xsd:decimal . - | - | ?decimal a xsd:decimal . - | - | FILTER(?decimal > "2"^^xsd:decimal) - | } - |} ORDER BY ASC(?decimal) + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | anything:hasDecimal knora-api:objectType xsd:decimal . + | + | ?decimal a xsd:decimal . + | + | FILTER(?decimal > "2"^^xsd:decimal) + | } + |} ORDER BY ASC(?decimal) """.stripMargin val inputQueryWithDecimalOptionalSortCriterionAndFilterComplex: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - | - | ?thing anything:hasDecimal ?decimal . - |} WHERE { - | - | ?thing a anything:Thing . - | ?thing a knora-api:Resource . - | - | OPTIONAL { - | ?thing anything:hasDecimal ?decimal . - | - | ?decimal knora-api:decimalValueAsDecimal ?decimalVal . - | - | FILTER(?decimalVal > "2"^^xsd:decimal) - | } - |} ORDER BY ASC(?decimal) + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + | + | ?thing anything:hasDecimal ?decimal . + |} WHERE { + | + | ?thing a anything:Thing . + | ?thing a knora-api:Resource . + | + | OPTIONAL { + | ?thing anything:hasDecimal ?decimal . + | + | ?decimal knora-api:decimalValueAsDecimal ?decimalVal . + | + | FILTER(?decimalVal > "2"^^xsd:decimal) + | } + |} ORDER BY ASC(?decimal) """.stripMargin val transformedQueryWithDecimalOptionalSortCriterionAndFilter: SelectQuery = @@ -899,7 +922,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -938,7 +962,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "decimal"), @@ -951,17 +976,21 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "decimal__valueHasDecimal"), - operator = CompareExpressionOperator.GREATER_THAN, - rightArg = XsdLiteral( - value = "2", - datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "decimal__valueHasDecimal"), + operator = CompareExpressionOperator.GREATER_THAN, + rightArg = XsdLiteral( + value = "2", + datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + ) ) - )) - )) + ) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -1012,7 +1041,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -1060,7 +1090,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "decimal"), @@ -1073,17 +1104,21 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "decimalVal"), - operator = CompareExpressionOperator.GREATER_THAN, - rightArg = XsdLiteral( - value = "2", - datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "decimalVal"), + operator = CompareExpressionOperator.GREATER_THAN, + rightArg = XsdLiteral( + value = "2", + datatype = "http://www.w3.org/2001/XMLSchema#decimal".toSmartIri + ) ) - )) - )) + ) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -1094,30 +1129,30 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val InputQueryWithRdfsLabelAndLiteralInSimpleSchema: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . + |} """.stripMargin val InputQueryWithRdfsLabelAndLiteralInComplexSchema: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . + |} """.stripMargin val TransformedQueryWithRdfsLabelAndLiteral: SelectQuery = SelectQuery( @@ -1129,7 +1164,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "book"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -1146,7 +1182,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "book"), @@ -1182,61 +1219,61 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val InputQueryWithRdfsLabelAndVariableInSimpleSchema: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?label . - | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?label . + | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") + |} """.stripMargin val InputQueryWithRdfsLabelAndVariableInComplexSchema: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?label . - | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?label . + | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") + |} """.stripMargin val InputQueryWithRdfsLabelAndRegexInSimpleSchema: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?bookLabel . - | FILTER regex(?bookLabel, "Zeit", "i") - |}""".stripMargin + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?bookLabel . + | FILTER regex(?bookLabel, "Zeit", "i") + |}""".stripMargin val InputQueryWithRdfsLabelAndRegexInComplexSchema: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?bookLabel . - | FILTER regex(?bookLabel, "Zeit", "i") - |}""".stripMargin + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?bookLabel . + | FILTER regex(?bookLabel, "Zeit", "i") + |}""".stripMargin val TransformedQueryWithRdfsLabelAndVariable: SelectQuery = SelectQuery( fromClause = None, @@ -1247,7 +1284,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "book"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -1264,7 +1302,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "book"), @@ -1295,7 +1334,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec value = "Zeitgl\u00F6cklein des Lebens und Leidens Christi", datatype = "http://www.w3.org/2001/XMLSchema#string".toSmartIri ) - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -1313,7 +1353,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "book"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -1330,7 +1371,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "book"), @@ -1358,7 +1400,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec textExpr = QueryVariable(variableName = "bookLabel"), pattern = "Zeit", modifier = Some("i") - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -1369,23 +1412,23 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec val queryWithOptional: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?document knora-api:isMainResource true . - |} WHERE { - | ?document rdf:type beol:writtenSource . - | - | OPTIONAL { - | ?document beol:hasRecipient ?recipient . - | - | ?recipient beol:hasFamilyName ?familyName . - | - | FILTER knora-api:matchText(?familyName, "Bernoulli") - |} - |} + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?document knora-api:isMainResource true . + |} WHERE { + | ?document rdf:type beol:writtenSource . + | + | OPTIONAL { + | ?document beol:hasRecipient ?recipient . + | + | ?recipient beol:hasFamilyName ?familyName . + | + | FILTER knora-api:matchText(?familyName, "Bernoulli") + |} + |} """.stripMargin val TransformedQueryWithOptional: SelectQuery = SelectQuery( @@ -1397,7 +1440,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "document"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -1414,7 +1458,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), // This statement must not be removed by AbstractPrequeryGenerator.removeEntitiesInferredFromProperty // because the property from which its type can be inferred is in an optional. Without this statement, @@ -1447,7 +1492,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "recipient"), @@ -1472,7 +1518,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "document"), @@ -1490,12 +1537,14 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec propertyPathOperator = None ), obj = QueryVariable( - variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue"), + variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue" + ), namedGraph = None ), StatementPattern( subj = QueryVariable( - variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue"), + variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue" + ), pred = IriRef( iri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri, propertyPathOperator = None @@ -1508,11 +1557,13 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable( - variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue"), + variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue" + ), pred = IriRef( iri = "http://www.knora.org/ontology/knora-base#isDeleted".toSmartIri, propertyPathOperator = None @@ -1525,11 +1576,13 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable( - variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue"), + variableName = "document__httpwwwknoraorgontology0801beolhasRecipient__recipient__LinkValue" + ), pred = IriRef( iri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#object".toSmartIri, propertyPathOperator = None @@ -1539,26 +1592,32 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), LuceneQueryPattern( subj = QueryVariable(variableName = "familyName"), obj = QueryVariable(variableName = "familyName__valueHasString"), queryString = LuceneQueryString(queryString = "Bernoulli"), - literalStatement = Some(StatementPattern( - subj = QueryVariable(variableName = "familyName"), - pred = IriRef( - iri = "http://www.knora.org/ontology/knora-base#valueHasString".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "familyName__valueHasString"), - namedGraph = Some(IriRef( - iri = "http://www.knora.org/explicit".toSmartIri, - propertyPathOperator = None - )) - )) + literalStatement = Some( + StatementPattern( + subj = QueryVariable(variableName = "familyName"), + pred = IriRef( + iri = "http://www.knora.org/ontology/knora-base#valueHasString".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "familyName__valueHasString"), + namedGraph = Some( + IriRef( + iri = "http://www.knora.org/explicit".toSmartIri, + propertyPathOperator = None + ) + ) + ) + ) ) - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -1630,7 +1689,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -1655,7 +1715,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "text"), @@ -1668,7 +1729,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), UnionPattern( blocks = Vector( @@ -1696,7 +1758,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "int"), @@ -1709,16 +1772,19 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), - FilterPattern(expression = CompareExpression( - leftArg = QueryVariable(variableName = "int__valueHasInteger"), - operator = CompareExpressionOperator.EQUALS, - rightArg = XsdLiteral( - value = "1", - datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + FilterPattern(expression = + CompareExpression( + leftArg = QueryVariable(variableName = "int__valueHasInteger"), + operator = CompareExpressionOperator.EQUALS, + rightArg = XsdLiteral( + value = "1", + datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + ) ) - )) + ) ), Vector( StatementPattern( @@ -1744,7 +1810,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "text"), @@ -1757,16 +1824,19 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), FilterPattern( expression = RegexFunction( textExpr = QueryVariable(variableName = "text__valueHasString"), pattern = "Abel", modifier = Some("i") - )) + ) + ) ) - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -1776,29 +1846,29 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec ) val queryToReorder: String = """ - |PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter ?linkingProp1 ?person1 . - | ?letter ?linkingProp2 ?person2 . - | ?letter beol:creationDate ?date . - |} WHERE { - | ?letter beol:creationDate ?date . - | - | ?letter ?linkingProp1 ?person1 . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient ) - | - | ?letter ?linkingProp2 ?person2 . - | FILTER(?linkingProp2 = beol:hasAuthor || ?linkingProp2 = beol:hasRecipient ) - | - | ?person1 beol:hasIAFIdentifier ?gnd1 . - | ?gnd1 knora-api:valueAsString "(DE-588)118531379" . - | - | ?person2 beol:hasIAFIdentifier ?gnd2 . - | ?gnd2 knora-api:valueAsString "(DE-588)118696149" . - |} ORDER BY ?date""".stripMargin + |PREFIX beol: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter ?linkingProp1 ?person1 . + | ?letter ?linkingProp2 ?person2 . + | ?letter beol:creationDate ?date . + |} WHERE { + | ?letter beol:creationDate ?date . + | + | ?letter ?linkingProp1 ?person1 . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient ) + | + | ?letter ?linkingProp2 ?person2 . + | FILTER(?linkingProp2 = beol:hasAuthor || ?linkingProp2 = beol:hasRecipient ) + | + | ?person1 beol:hasIAFIdentifier ?gnd1 . + | ?gnd1 knora-api:valueAsString "(DE-588)118531379" . + | + | ?person2 beol:hasIAFIdentifier ?gnd2 . + | ?gnd2 knora-api:valueAsString "(DE-588)118696149" . + |} ORDER BY ?date""".stripMargin val transformedQueryToReorder: SelectQuery = SelectQuery( fromClause = None, @@ -1885,7 +1955,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "person2"), @@ -1910,7 +1981,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "person1"), @@ -1926,7 +1998,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "person1"), @@ -1951,7 +2024,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter"), @@ -1967,7 +2041,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter"), @@ -1995,7 +2070,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter__linkingProp2__person2__LinkValue"), @@ -2011,7 +2087,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter__linkingProp2__person2__LinkValue"), @@ -2024,7 +2101,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter"), @@ -2052,7 +2130,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter__linkingProp1__person1__LinkValue"), @@ -2068,7 +2147,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter__linkingProp1__person1__LinkValue"), @@ -2081,7 +2161,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "letter"), @@ -2106,7 +2187,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "date"), @@ -2119,7 +2201,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), FilterPattern( expression = OrExpression( @@ -2139,7 +2222,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec propertyPathOperator = None ) ) - )), + ) + ), FilterPattern( expression = OrExpression( leftArg = CompareExpression( @@ -2158,7 +2242,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec propertyPathOperator = None ) ) - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -2168,16 +2253,16 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec ) val queryToReorderWithCycle: String = """ - |PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | ?thing anything:hasOtherThing ?thing1 . - | ?thing1 anything:hasOtherThing ?thing2 . - | ?thing2 anything:hasOtherThing ?thing . - |} """.stripMargin + |PREFIX anything: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | ?thing anything:hasOtherThing ?thing1 . + | ?thing1 anything:hasOtherThing ?thing2 . + | ?thing2 anything:hasOtherThing ?thing . + |} """.stripMargin val transformedQueryToReorderWithCycle: SelectQuery = SelectQuery( offset = 0, @@ -2186,7 +2271,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "thing"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -2203,7 +2289,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing1"), @@ -2239,7 +2326,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = @@ -2256,7 +2344,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = @@ -2270,7 +2359,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing2"), @@ -2286,7 +2376,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -2302,7 +2393,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -2338,7 +2430,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = @@ -2355,7 +2448,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = @@ -2369,7 +2463,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing2"), @@ -2405,7 +2500,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = @@ -2422,7 +2518,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = @@ -2436,7 +2533,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ) ), positiveEntities = Set(), @@ -2473,80 +2571,92 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "thing"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( - MinusPattern(patterns = Vector( - StatementPattern( - subj = QueryVariable(variableName = "thing"), - pred = IriRef( - iri = "http://www.knora.org/ontology/knora-base#isDeleted".toSmartIri, - propertyPathOperator = None - ), - obj = XsdLiteral( - value = "false", - datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri - ), - namedGraph = Some(IriRef( - iri = "http://www.knora.org/explicit".toSmartIri, - propertyPathOperator = None - )) - ), - StatementPattern( - subj = QueryVariable(variableName = "thing"), - pred = IriRef( - iri = "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "intVal"), - namedGraph = None - ), - StatementPattern( - subj = QueryVariable(variableName = "intVal"), - pred = IriRef( - iri = "http://www.knora.org/ontology/knora-base#isDeleted".toSmartIri, - propertyPathOperator = None + MinusPattern(patterns = + Vector( + StatementPattern( + subj = QueryVariable(variableName = "thing"), + pred = IriRef( + iri = "http://www.knora.org/ontology/knora-base#isDeleted".toSmartIri, + propertyPathOperator = None + ), + obj = XsdLiteral( + value = "false", + datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri + ), + namedGraph = Some( + IriRef( + iri = "http://www.knora.org/explicit".toSmartIri, + propertyPathOperator = None + ) + ) ), - obj = XsdLiteral( - value = "false", - datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri + StatementPattern( + subj = QueryVariable(variableName = "thing"), + pred = IriRef( + iri = "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "intVal"), + namedGraph = None ), - namedGraph = Some(IriRef( - iri = "http://www.knora.org/explicit".toSmartIri, - propertyPathOperator = None - )) - ), - StatementPattern( - subj = QueryVariable(variableName = "intVal"), - pred = IriRef( - iri = "http://www.knora.org/ontology/knora-base#valueHasInteger".toSmartIri, - propertyPathOperator = None + StatementPattern( + subj = QueryVariable(variableName = "intVal"), + pred = IriRef( + iri = "http://www.knora.org/ontology/knora-base#isDeleted".toSmartIri, + propertyPathOperator = None + ), + obj = XsdLiteral( + value = "false", + datatype = "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri + ), + namedGraph = Some( + IriRef( + iri = "http://www.knora.org/explicit".toSmartIri, + propertyPathOperator = None + ) + ) ), - obj = QueryVariable(variableName = "intVal__valueHasInteger"), - namedGraph = Some(IriRef( - iri = "http://www.knora.org/explicit".toSmartIri, - propertyPathOperator = None - )) - ), - FilterPattern(expression = OrExpression( - leftArg = CompareExpression( - leftArg = QueryVariable(variableName = "intVal__valueHasInteger"), - operator = CompareExpressionOperator.EQUALS, - rightArg = XsdLiteral( - value = "123454321", - datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + StatementPattern( + subj = QueryVariable(variableName = "intVal"), + pred = IriRef( + iri = "http://www.knora.org/ontology/knora-base#valueHasInteger".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "intVal__valueHasInteger"), + namedGraph = Some( + IriRef( + iri = "http://www.knora.org/explicit".toSmartIri, + propertyPathOperator = None + ) ) ), - rightArg = CompareExpression( - leftArg = QueryVariable(variableName = "intVal__valueHasInteger"), - operator = CompareExpressionOperator.EQUALS, - rightArg = XsdLiteral( - value = "999999999", - datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + FilterPattern(expression = + OrExpression( + leftArg = CompareExpression( + leftArg = QueryVariable(variableName = "intVal__valueHasInteger"), + operator = CompareExpressionOperator.EQUALS, + rightArg = XsdLiteral( + value = "123454321", + datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + ) + ), + rightArg = CompareExpression( + leftArg = QueryVariable(variableName = "intVal__valueHasInteger"), + operator = CompareExpressionOperator.EQUALS, + rightArg = XsdLiteral( + value = "999999999", + datatype = "http://www.w3.org/2001/XMLSchema#integer".toSmartIri + ) + ) ) ) - )) - ))), + ) + ) + ), positiveEntities = Set(), querySchema = None ), @@ -2565,7 +2675,7 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec |} WHERE { | ?thing a knora-api:Resource . | ?thing a anything:Thing . - | ?thing anything:hasInteger ?int . + | ?thing anything:hasInteger ?int . | | { | ?thing anything:hasRichtext ?richtext . @@ -2636,7 +2746,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -2661,7 +2772,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "int"), @@ -2674,7 +2786,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), UnionPattern( blocks = Vector( @@ -2714,7 +2827,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "int"), @@ -2727,7 +2841,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -2752,24 +2867,29 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), LuceneQueryPattern( subj = QueryVariable(variableName = "richtext"), obj = QueryVariable(variableName = "richtext__valueHasString"), queryString = LuceneQueryString(queryString = "test"), - literalStatement = Some(StatementPattern( - subj = QueryVariable(variableName = "richtext"), - pred = IriRef( - iri = "http://www.knora.org/ontology/knora-base#valueHasString".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "richtext__valueHasString"), - namedGraph = Some(IriRef( - iri = "http://www.knora.org/explicit".toSmartIri, - propertyPathOperator = None - )) - )) + literalStatement = Some( + StatementPattern( + subj = QueryVariable(variableName = "richtext"), + pred = IriRef( + iri = "http://www.knora.org/ontology/knora-base#valueHasString".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "richtext__valueHasString"), + namedGraph = Some( + IriRef( + iri = "http://www.knora.org/explicit".toSmartIri, + propertyPathOperator = None + ) + ) + ) + ) ) ), Vector( @@ -2808,7 +2928,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "int"), @@ -2821,7 +2942,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -2846,27 +2968,33 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), LuceneQueryPattern( subj = QueryVariable(variableName = "text"), obj = QueryVariable(variableName = "text__valueHasString"), queryString = LuceneQueryString(queryString = "test"), - literalStatement = Some(StatementPattern( - subj = QueryVariable(variableName = "text"), - pred = IriRef( - iri = "http://www.knora.org/ontology/knora-base#valueHasString".toSmartIri, - propertyPathOperator = None - ), - obj = QueryVariable(variableName = "text__valueHasString"), - namedGraph = Some(IriRef( - iri = "http://www.knora.org/explicit".toSmartIri, - propertyPathOperator = None - )) - )) + literalStatement = Some( + StatementPattern( + subj = QueryVariable(variableName = "text"), + pred = IriRef( + iri = "http://www.knora.org/ontology/knora-base#valueHasString".toSmartIri, + propertyPathOperator = None + ), + obj = QueryVariable(variableName = "text__valueHasString"), + namedGraph = Some( + IriRef( + iri = "http://www.knora.org/explicit".toSmartIri, + propertyPathOperator = None + ) + ) + ) + ) ) ) - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -2911,7 +3039,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec OrderCriterion( queryVariable = QueryVariable(variableName = "thing"), isAscending = true - )), + ) + ), whereClause = WhereClause( patterns = Vector( StatementPattern( @@ -2970,7 +3099,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "thing"), @@ -2995,7 +3125,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "standoffDateTag"), @@ -3008,7 +3139,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), StatementPattern( subj = QueryVariable(variableName = "standoffDateTag"), @@ -3021,7 +3153,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec IriRef( iri = "http://www.knora.org/explicit".toSmartIri, propertyPathOperator = None - )) + ) + ) ), FilterPattern( expression = AndExpression( @@ -3041,7 +3174,8 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec operator = CompareExpressionOperator.GREATER_THAN_OR_EQUAL_TO, rightArg = QueryVariable(variableName = "standoffDateTag__valueHasStartJDN") ) - )) + ) + ) ), positiveEntities = Set(), querySchema = None @@ -3074,10 +3208,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as a non optional sort criterion" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateNonOptionalSortCriterion, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateNonOptionalSortCriterion, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateNonOptionalSortCriterion) @@ -3086,10 +3222,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as a non optional sort criterion (submitted in complex schema)" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateNonOptionalSortCriterionComplex, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateNonOptionalSortCriterionComplex, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateNonOptionalSortCriterion) @@ -3098,10 +3236,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as non optional sort criterion and a filter" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateNonOptionalSortCriterionAndFilter, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateNonOptionalSortCriterionAndFilter, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateNonOptionalSortCriterionAndFilter) @@ -3110,10 +3250,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as non optional sort criterion and a filter (submitted in complex schema)" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateNonOptionalSortCriterionAndFilterComplex, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateNonOptionalSortCriterionAndFilterComplex, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateNonOptionalSortCriterionAndFilter) @@ -3122,10 +3264,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as an optional sort criterion" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateOptionalSortCriterion, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateOptionalSortCriterion, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateOptionalSortCriterion) @@ -3134,10 +3278,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as an optional sort criterion (submitted in complex schema)" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateOptionalSortCriterionComplex, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateOptionalSortCriterionComplex, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateOptionalSortCriterion) @@ -3146,10 +3292,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as an optional sort criterion and a filter" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateOptionalSortCriterionAndFilter, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateOptionalSortCriterionAndFilter, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateOptionalSortCriterionAndFilter) @@ -3158,10 +3306,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a date as an optional sort criterion and a filter (submitted in complex schema)" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDateOptionalSortCriterionAndFilterComplex, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDateOptionalSortCriterionAndFilterComplex, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDateOptionalSortCriterionAndFilter) @@ -3170,10 +3320,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a decimal as an optional sort criterion" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDecimalOptionalSortCriterion, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDecimalOptionalSortCriterion, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDecimalOptionalSortCriterion) } @@ -3181,10 +3333,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a decimal as an optional sort criterion (submitted in complex schema)" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDecimalOptionalSortCriterionComplex, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDecimalOptionalSortCriterionComplex, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDecimalOptionalSortCriterion) } @@ -3192,10 +3346,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a decimal as an optional sort criterion and a filter" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDecimalOptionalSortCriterionAndFilter, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDecimalOptionalSortCriterionAndFilter, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithDecimalOptionalSortCriterionAndFilter) } @@ -3203,10 +3359,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with a decimal as an optional sort criterion and a filter (submitted in complex schema)" in { val transformedQuery = - QueryHandler.transformQuery(inputQueryWithDecimalOptionalSortCriterionAndFilterComplex, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + inputQueryWithDecimalOptionalSortCriterionAndFilterComplex, + responderData, + settings, + defaultFeatureFactoryConfig + ) // TODO: user provided statements and statement generated for sorting should be unified (https://github.com/dhlab-basel/Knora/issues/1195) assert(transformedQuery === transformedQueryWithDecimalOptionalSortCriterionAndFilterComplex) @@ -3214,60 +3372,72 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query using rdfs:label and a literal in the simple schema" in { val transformedQuery = - QueryHandler.transformQuery(InputQueryWithRdfsLabelAndLiteralInSimpleSchema, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + InputQueryWithRdfsLabelAndLiteralInSimpleSchema, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery == TransformedQueryWithRdfsLabelAndLiteral) } "transform an input query using rdfs:label and a literal in the complex schema" in { val transformedQuery = - QueryHandler.transformQuery(InputQueryWithRdfsLabelAndLiteralInComplexSchema, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + InputQueryWithRdfsLabelAndLiteralInComplexSchema, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === TransformedQueryWithRdfsLabelAndLiteral) } "transform an input query using rdfs:label and a variable in the simple schema" in { val transformedQuery = - QueryHandler.transformQuery(InputQueryWithRdfsLabelAndVariableInSimpleSchema, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + InputQueryWithRdfsLabelAndVariableInSimpleSchema, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === TransformedQueryWithRdfsLabelAndVariable) } "transform an input query using rdfs:label and a variable in the complex schema" in { val transformedQuery = - QueryHandler.transformQuery(InputQueryWithRdfsLabelAndVariableInComplexSchema, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + InputQueryWithRdfsLabelAndVariableInComplexSchema, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === TransformedQueryWithRdfsLabelAndVariable) } "transform an input query using rdfs:label and a regex in the simple schema" in { val transformedQuery = - QueryHandler.transformQuery(InputQueryWithRdfsLabelAndRegexInSimpleSchema, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + InputQueryWithRdfsLabelAndRegexInSimpleSchema, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === TransformedQueryWithRdfsLabelAndRegex) } "transform an input query using rdfs:label and a regex in the complex schema" in { val transformedQuery = - QueryHandler.transformQuery(InputQueryWithRdfsLabelAndRegexInComplexSchema, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + InputQueryWithRdfsLabelAndRegexInComplexSchema, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === TransformedQueryWithRdfsLabelAndRegex) } @@ -3281,10 +3451,12 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec "transform an input query with knora-api:standoffTagHasStartAncestor" in { val transformedQuery = - QueryHandler.transformQuery(queryWithStandoffTagHasStartAncestor, - responderData, - settings, - defaultFeatureFactoryConfig) + QueryHandler.transformQuery( + queryWithStandoffTagHasStartAncestor, + responderData, + settings, + defaultFeatureFactoryConfig + ) assert(transformedQuery === transformedQueryWithStandoffTagHasStartAncestor) } @@ -3329,18 +3501,21 @@ class NonTriplestoreSpecificGravsearchToPrequeryTransformerSpec extends CoreSpec QueryHandler.transformQuery(queryWithKnoraApiResource, responderData, settings, defaultFeatureFactoryConfig) assert( - transformedQuery.whereClause.patterns.contains(StatementPattern( - subj = QueryVariable(variableName = "resource"), - pred = IriRef( - iri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri, - propertyPathOperator = None - ), - obj = IriRef( - iri = "http://www.knora.org/ontology/knora-base#Resource".toSmartIri, - propertyPathOperator = None - ), - namedGraph = None - ))) + transformedQuery.whereClause.patterns.contains( + StatementPattern( + subj = QueryVariable(variableName = "resource"), + pred = IriRef( + iri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri, + propertyPathOperator = None + ), + obj = IriRef( + iri = "http://www.knora.org/ontology/knora-base#Resource".toSmartIri, + propertyPathOperator = None + ), + namedGraph = None + ) + ) + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtilSpec.scala index 0c8be810af..da3e7ce0db 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/prequery/TopologicalSortUtilSpec.scala @@ -25,16 +25,15 @@ import scalax.collection.Graph import scalax.collection.GraphEdge._ /** - * Tests [[TopologicalSortUtil]]. - */ + * Tests [[TopologicalSortUtil]]. + */ class TopologicalSortUtilSpec extends CoreSpec() { type NodeT = Graph[Int, DiHyperEdge]#NodeT - private def nodesToValues(orders: Set[Vector[NodeT]]): Set[Vector[Int]] = { + private def nodesToValues(orders: Set[Vector[NodeT]]): Set[Vector[Int]] = orders.map { order: Vector[NodeT] => order.map(_.value) } - } "TopologicalSortUtilSpec" should { @@ -44,7 +43,8 @@ class TopologicalSortUtilSpec extends CoreSpec() { val allOrders: Set[Vector[Int]] = nodesToValues( TopologicalSortUtil - .findAllTopologicalOrderPermutations(graph)) + .findAllTopologicalOrderPermutations(graph) + ) val expectedOrders = Set( Vector(2, 7, 4, 5) @@ -55,11 +55,18 @@ class TopologicalSortUtilSpec extends CoreSpec() { "return all topological orders of a graph with multiple leaves" in { val graph: Graph[Int, DiHyperEdge] = - Graph[Int, DiHyperEdge](DiHyperEdge[Int](2, 4), DiHyperEdge[Int](2, 7), DiHyperEdge[Int](2, 8), DiHyperEdge[Int](4, 5), DiHyperEdge[Int](7, 3)) + Graph[Int, DiHyperEdge]( + DiHyperEdge[Int](2, 4), + DiHyperEdge[Int](2, 7), + DiHyperEdge[Int](2, 8), + DiHyperEdge[Int](4, 5), + DiHyperEdge[Int](7, 3) + ) val allOrders: Set[Vector[Int]] = nodesToValues( TopologicalSortUtil - .findAllTopologicalOrderPermutations(graph)) + .findAllTopologicalOrderPermutations(graph) + ) val expectedOrders = Set( Vector(2, 8, 4, 7, 5, 3), @@ -74,7 +81,8 @@ class TopologicalSortUtilSpec extends CoreSpec() { val allOrders: Set[Vector[Int]] = nodesToValues( TopologicalSortUtil - .findAllTopologicalOrderPermutations(graph)) + .findAllTopologicalOrderPermutations(graph) + ) assert(allOrders.isEmpty) } @@ -85,7 +93,8 @@ class TopologicalSortUtilSpec extends CoreSpec() { val allOrders: Set[Vector[Int]] = nodesToValues( TopologicalSortUtil - .findAllTopologicalOrderPermutations(graph)) + .findAllTopologicalOrderPermutations(graph) + ) assert(allOrders.isEmpty) } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectorSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectorSpec.scala index f6573b6747..1262644b50 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/search/gravsearch/types/GravsearchTypeInspectorSpec.scala @@ -33,8 +33,8 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} /** - * Tests Gravsearch type inspection. - */ + * Tests Gravsearch type inspection. + */ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { private val anythingAdminUser = SharedTestDataADM.anythingAdminUser @@ -44,66 +44,74 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val QueryWithExplicitTypeAnnotations: String = """ - |PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | - | ?letter knora-api:hasLinkTo . - | ?letter ?linkingProp1 . - | - | ?letter knora-api:hasLinkTo . - | ?letter ?linkingProp2 . - | - |} WHERE { - | ?letter a knora-api:Resource . - | ?letter a beol:letter . - | - | # Scheuchzer, Johann Jacob 1672-1733 - | ?letter ?linkingProp1 . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | a knora-api:Resource . - | - | # Hermann, Jacob 1678-1733 - | ?letter ?linkingProp2 . - | ?linkingProp2 knora-api:objectType knora-api:Resource . - | - | FILTER(?linkingProp2 = beol:hasAuthor || ?linkingProp2 = beol:hasRecipient) - | - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | beol:hasRecipient knora-api:objectType knora-api:Resource . - | - | a knora-api:Resource . - |} + |PREFIX beol: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | + | ?letter knora-api:hasLinkTo . + | ?letter ?linkingProp1 . + | + | ?letter knora-api:hasLinkTo . + | ?letter ?linkingProp2 . + | + |} WHERE { + | ?letter a knora-api:Resource . + | ?letter a beol:letter . + | + | # Scheuchzer, Johann Jacob 1672-1733 + | ?letter ?linkingProp1 . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | a knora-api:Resource . + | + | # Hermann, Jacob 1678-1733 + | ?letter ?linkingProp2 . + | ?linkingProp2 knora-api:objectType knora-api:Resource . + | + | FILTER(?linkingProp2 = beol:hasAuthor || ?linkingProp2 = beol:hasRecipient) + | + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | beol:hasRecipient knora-api:objectType knora-api:Resource . + | + | a knora-api:Resource . + |} """.stripMargin val SimpleTypeInspectionResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "linkingProp1") -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableIri(iri = "http://rdfh.ch/beol/oU8fMNDJQ9SGblfBl5JamA".toSmartIri) -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "letter") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "linkingProp2") -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableIri(iri = "http://rdfh.ch/beol/6edJwtTSR8yjAWnYmt6AtA".toSmartIri) -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - objectIsResourceType = true) - )) + objectIsResourceType = true + ) + ) + ) val WhereClauseWithoutAnnotations: WhereClause = WhereClause( patterns = Vector( @@ -122,310 +130,315 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { pred = QueryVariable(variableName = "linkingProp2"), subj = QueryVariable(variableName = "letter") ), - FilterPattern(expression = OrExpression( - rightArg = CompareExpression( - rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri), - operator = CompareExpressionOperator.EQUALS, - leftArg = QueryVariable(variableName = "linkingProp2") - ), - leftArg = CompareExpression( - rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri), - operator = CompareExpressionOperator.EQUALS, - leftArg = QueryVariable(variableName = "linkingProp2") + FilterPattern(expression = + OrExpression( + rightArg = CompareExpression( + rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri), + operator = CompareExpressionOperator.EQUALS, + leftArg = QueryVariable(variableName = "linkingProp2") + ), + leftArg = CompareExpression( + rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri), + operator = CompareExpressionOperator.EQUALS, + leftArg = QueryVariable(variableName = "linkingProp2") + ) ) - )), - FilterPattern(expression = OrExpression( - rightArg = CompareExpression( - rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri), - operator = CompareExpressionOperator.EQUALS, - leftArg = QueryVariable(variableName = "linkingProp1") - ), - leftArg = CompareExpression( - rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri), - operator = CompareExpressionOperator.EQUALS, - leftArg = QueryVariable(variableName = "linkingProp1") + ), + FilterPattern(expression = + OrExpression( + rightArg = CompareExpression( + rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri), + operator = CompareExpressionOperator.EQUALS, + leftArg = QueryVariable(variableName = "linkingProp1") + ), + leftArg = CompareExpression( + rightArg = IriRef(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri), + operator = CompareExpressionOperator.EQUALS, + leftArg = QueryVariable(variableName = "linkingProp1") + ) ) - )) - )) + ) + ) + ) val QueryRdfTypeRule: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | beol:creationDate knora-api:objectType knora-api:Date . - | ?date a knora-api:Date . - | ?letter ?linkingProp1 . - | a beol:person . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | beol:hasFamilyName ?name . - | beol:hasFamilyName knora-api:objectType xsd:string . - | ?name a xsd:string . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | beol:creationDate knora-api:objectType knora-api:Date . + | ?date a knora-api:Date . + | ?letter ?linkingProp1 . + | a beol:person . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | beol:hasFamilyName ?name . + | beol:hasFamilyName knora-api:objectType xsd:string . + | ?name a xsd:string . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date """.stripMargin val QueryKnoraObjectTypeFromPropertyIriRule: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | ?date a knora-api:Date . - | ?letter ?linkingProp1 . - | a beol:person . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | beol:hasFamilyName ?name . - | ?name a xsd:string . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | ?date a knora-api:Date . + | ?letter ?linkingProp1 . + | a beol:person . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | beol:hasFamilyName ?name . + | ?name a xsd:string . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date """.stripMargin val QueryTypeOfObjectFromPropertyRule: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | a beol:person . - | ?linkingProp1 knora-api:objectType knora-api:Resource . - | beol:hasFamilyName ?name . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | a beol:person . + | ?linkingProp1 knora-api:objectType knora-api:Resource . + | beol:hasFamilyName ?name . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date """.stripMargin val QueryKnoraObjectTypeFromObjectRule: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | a beol:person . - | beol:hasFamilyName ?name . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | a beol:person . + | beol:hasFamilyName ?name . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + |} ORDER BY ?date """.stripMargin val QueryTypeOfSubjectFromPropertyRule: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - |} WHERE { - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | beol:hasFamilyName ?name . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - | - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + |} WHERE { + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | beol:hasFamilyName ?name . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + | + |} ORDER BY ?date """.stripMargin val QueryPropertyVarTypeFromFilterRule: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - |} WHERE { - | ?letter beol:creationDate ?date . - | ?letter ?linkingProp1 . - | - | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) - | - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + |} WHERE { + | ?letter beol:creationDate ?date . + | ?letter ?linkingProp1 . + | + | FILTER(?linkingProp1 = beol:hasAuthor || ?linkingProp1 = beol:hasRecipient) + | + |} ORDER BY ?date """.stripMargin val QueryNonPropertyVarTypeFromFilterRule: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true ; - | incunabula:title ?title ; - | incunabula:pubdate ?pubdate . - |} WHERE { - | ?book a incunabula:book ; - | incunabula:title ?title ; - | incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate = "JULIAN:1497-03-01"^^knora-api:Date) . - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true ; + | incunabula:title ?title ; + | incunabula:pubdate ?pubdate . + |} WHERE { + | ?book a incunabula:book ; + | incunabula:title ?title ; + | incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate = "JULIAN:1497-03-01"^^knora-api:Date) . + |} """.stripMargin val QueryVarTypeFromFunction: String = """ - | PREFIX incunabula: - | PREFIX knora-api: - | - | CONSTRUCT { - | - | ?mainRes knora-api:isMainResource true . - | - | ?mainRes incunabula:title ?propVal0 . - | - | } WHERE { - | - | ?mainRes a incunabula:book . - | - | ?mainRes ?titleProp ?propVal0 . - | - | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein") - | - | } + | PREFIX incunabula: + | PREFIX knora-api: + | + | CONSTRUCT { + | + | ?mainRes knora-api:isMainResource true . + | + | ?mainRes incunabula:title ?propVal0 . + | + | } WHERE { + | + | ?mainRes a incunabula:book . + | + | ?mainRes ?titleProp ?propVal0 . + | + | FILTER knora-api:matchText(?propVal0, "Zeitglöcklein") + | + | } """.stripMargin val QueryNonKnoraTypeWithoutAnnotation: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - |PREFIX dcterms: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true ; - | dcterms:title ?title . - | - |} WHERE { - | - | ?book dcterms:title ?title . - |} + |PREFIX incunabula: + |PREFIX knora-api: + |PREFIX dcterms: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true ; + | dcterms:title ?title . + | + |} WHERE { + | + | ?book dcterms:title ?title . + |} """.stripMargin val QueryNonKnoraTypeWithAnnotation: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - |PREFIX dcterms: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true ; - | dcterms:title ?title . - | - |} WHERE { - | - | ?book rdf:type incunabula:book ; - | dcterms:title ?title . - | - | ?title a xsd:string . - |} + |PREFIX incunabula: + |PREFIX knora-api: + |PREFIX dcterms: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true ; + | dcterms:title ?title . + | + |} WHERE { + | + | ?book rdf:type incunabula:book ; + | dcterms:title ?title . + | + | ?title a xsd:string . + |} """.stripMargin val QueryIriTypeFromFunction: String = """ - |PREFIX knora-api: - |PREFIX standoff: - |PREFIX beol: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasText ?text . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasText ?text . - | ?text knora-api:textValueHasStandoff ?standoffLinkTag . - | ?standoffLinkTag a knora-api:StandoffLinkTag . - | - | FILTER knora-api:standoffLink(?letter, ?standoffLinkTag, ) - |} + |PREFIX knora-api: + |PREFIX standoff: + |PREFIX beol: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasText ?text . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasText ?text . + | ?text knora-api:textValueHasStandoff ?standoffLinkTag . + | ?standoffLinkTag a knora-api:StandoffLinkTag . + | + | FILTER knora-api:standoffLink(?letter, ?standoffLinkTag, ) + |} """.stripMargin val QueryWithInconsistentTypes1: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?page incunabula:title ?book . - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?page incunabula:title ?book . + |} """.stripMargin val QueryWithInconsistentTypes2: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true ; - | incunabula:title ?title ; - | incunabula:pubdate ?pubdate . - |} WHERE { - | ?book a incunabula:book ; - | incunabula:title ?title ; - | incunabula:pubdate ?pubdate . - | - | FILTER(?pubdate = "JULIAN:1497-03-01") . - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true ; + | incunabula:title ?title ; + | incunabula:pubdate ?pubdate . + |} WHERE { + | ?book a incunabula:book ; + | incunabula:title ?title ; + | incunabula:pubdate ?pubdate . + | + | FILTER(?pubdate = "JULIAN:1497-03-01") . + |} """.stripMargin val QueryComparingResourcesInSimpleSchema: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter ?prop ?person2 . - | FILTER(?person1 != ?person2) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter ?prop ?person2 . + | FILTER(?person1 != ?person2) . + |} + |OFFSET 0""".stripMargin val QueryComparingResourcesInSimpleSchemaResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( @@ -461,23 +474,25 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, isResourceType = true, isValueType = false - ))) + ) + ) + ) ) val QueryComparingResourcesInComplexSchema: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - |} WHERE { - | ?letter a beol:letter . - | ?letter ?prop ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person2 != ?person1) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + |} WHERE { + | ?letter a beol:letter . + | ?letter ?prop ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person2 != ?person1) . + |} + |OFFSET 0""".stripMargin val QueryComparingResourcesInComplexSchemaResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( @@ -513,29 +528,33 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { typeIri = "http://0.0.0.0:3333/ontology/0801/beol/v2#person".toSmartIri, isResourceType = true, isValueType = false - ))) + ) + ) + ) ) val QueryComparingResourceIriInSimpleSchema: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person1 != ) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person1 != ) . + |} + |OFFSET 0""".stripMargin val QueryComparingResourceIriInSimpleSchemaResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( - TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri) -> PropertyTypeInfo( + TypeableIri(iri = + "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri + ) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, objectIsResourceType = true, objectIsValueType = false @@ -572,31 +591,33 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, isResourceType = true, isValueType = false - )), + ) + ), TypeableVariable(variableName = "person1") -> Set( NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, isResourceType = true, isValueType = false - )) + ) + ) ) ) val QueryComparingResourceIriInComplexSchema: String = """PREFIX beol: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - |} WHERE { - | ?letter a beol:letter . - | ?letter beol:hasAuthor ?person1 . - | ?letter beol:hasRecipient ?person2 . - | FILTER(?person1 != ) . - |} - |OFFSET 0""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + |} WHERE { + | ?letter a beol:letter . + | ?letter beol:hasAuthor ?person1 . + | ?letter beol:hasRecipient ?person2 . + | FILTER(?person1 != ) . + |} + |OFFSET 0""".stripMargin val QueryComparingResourceIriInComplexSchemaResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( @@ -638,28 +659,30 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { typeIri = "http://0.0.0.0:3333/ontology/0801/beol/v2#person".toSmartIri, isResourceType = true, isValueType = false - )), + ) + ), TypeableVariable(variableName = "person2") -> Set( NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/v2#person".toSmartIri, isResourceType = true, isValueType = false - )) + ) + ) ) ) val QueryWithFilterComparison: String = """PREFIX knora-api: - |PREFIX beol: - | - |CONSTRUCT { - | ?person knora-api:isMainResource true . - | ?document beol:hasAuthor ?person . - |} WHERE { - | ?person a beol:person . - | ?document beol:hasAuthor ?person . - | FILTER(?document != ) - |}""".stripMargin + |PREFIX beol: + | + |CONSTRUCT { + | ?person knora-api:isMainResource true . + | ?document beol:hasAuthor ?person . + |} WHERE { + | ?person a beol:person . + | ?document beol:hasAuthor ?person . + | FILTER(?document != ) + |}""".stripMargin val QueryWithFilterComparisonResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( @@ -691,76 +714,78 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, isResourceType = true, isValueType = false - )), + ) + ), TypeableVariable(variableName = "person") -> Set( NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, isResourceType = true, isValueType = false - )) + ) + ) ) ) val PathologicalQuery: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?linkObj knora-api:isMainResource true . - | - | ?linkObj ?linkProp1 . - | ?linkObj ?linkProp1 . - | ?linkObj ?linkProp1 . - | ?linkObj ?linkProp1 . - | - | ?linkProp2 ?page1 . - | ?linkProp2 ?page2 . - | ?linkProp2 ?page3 . - | ?linkProp2 ?page4 . - | - | ?page1 ?partOfProp ?book1 . - | ?page2 ?partOfProp ?book2 . - | ?page3 ?partOfProp ?book3 . - | ?page4 ?partOfProp ?book4 . - | - | ?book1 ?titleProp1 ?title1 . - | ?book2 ?titleProp2 ?title2 . - | ?book3 ?titleProp3 ?title3 . - | ?book4 ?titleProp4 ?title4 . - |} WHERE { - | BIND( AS ?linkObj) - | ?linkObj knora-api:hasLinkTo . - | - | ?linkObj ?linkProp1 . - | ?linkObj ?linkProp1 . - | ?linkObj ?linkProp1 . - | ?linkObj ?linkProp1 . - | - | knora-api:isRegionOf ?page1 . - | - | ?linkProp2 ?page1 . - | ?linkProp2 ?page2 . - | ?linkProp2 ?page3 . - | ?linkProp2 ?page4 . - | - | ?page1 incunabula:partOf ?book1 . - | - | ?page1 ?partOfProp ?book1 . - | ?page2 ?partOfProp ?book2 . - | ?page3 ?partOfProp ?book3 . - | ?page4 ?partOfProp ?book4 . - | - | ?book1 ?titleProp1 ?title1 . - | ?book1 ?titleProp2 ?title1 . - | ?book2 ?titleProp2 ?title2 . - | ?book2 ?titleProp3 ?title2 . - | ?book3 ?titleProp3 ?title3 . - | ?book3 ?titleProp4 ?title3 . - | ?book4 ?titleProp4 ?title4 . - | - | FILTER(?title4 = "[Das] Narrenschiff (lat.)" || ?title4 = "Stultifera navis (...)") - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?linkObj knora-api:isMainResource true . + | + | ?linkObj ?linkProp1 . + | ?linkObj ?linkProp1 . + | ?linkObj ?linkProp1 . + | ?linkObj ?linkProp1 . + | + | ?linkProp2 ?page1 . + | ?linkProp2 ?page2 . + | ?linkProp2 ?page3 . + | ?linkProp2 ?page4 . + | + | ?page1 ?partOfProp ?book1 . + | ?page2 ?partOfProp ?book2 . + | ?page3 ?partOfProp ?book3 . + | ?page4 ?partOfProp ?book4 . + | + | ?book1 ?titleProp1 ?title1 . + | ?book2 ?titleProp2 ?title2 . + | ?book3 ?titleProp3 ?title3 . + | ?book4 ?titleProp4 ?title4 . + |} WHERE { + | BIND( AS ?linkObj) + | ?linkObj knora-api:hasLinkTo . + | + | ?linkObj ?linkProp1 . + | ?linkObj ?linkProp1 . + | ?linkObj ?linkProp1 . + | ?linkObj ?linkProp1 . + | + | knora-api:isRegionOf ?page1 . + | + | ?linkProp2 ?page1 . + | ?linkProp2 ?page2 . + | ?linkProp2 ?page3 . + | ?linkProp2 ?page4 . + | + | ?page1 incunabula:partOf ?book1 . + | + | ?page1 ?partOfProp ?book1 . + | ?page2 ?partOfProp ?book2 . + | ?page3 ?partOfProp ?book3 . + | ?page4 ?partOfProp ?book4 . + | + | ?book1 ?titleProp1 ?title1 . + | ?book1 ?titleProp2 ?title1 . + | ?book2 ?titleProp2 ?title2 . + | ?book2 ?titleProp3 ?title2 . + | ?book3 ?titleProp3 ?title3 . + | ?book3 ?titleProp4 ?title3 . + | ?book4 ?titleProp4 ?title4 . + | + | FILTER(?title4 = "[Das] Narrenschiff (lat.)" || ?title4 = "Stultifera navis (...)") + |} """.stripMargin val PathologicalTypeInferenceResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( @@ -935,28 +960,32 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { isResourceType = true, isValueType = false, isStandoffTagType = false - )), + ) + ), TypeableVariable(variableName = "linkObj") -> Set( NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, isResourceType = true, isValueType = false, isStandoffTagType = false - )), + ) + ), TypeableIri(iri = "http://rdfh.ch/8d3d8f94ab06".toSmartIri) -> Set( NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Region".toSmartIri, isResourceType = true, isValueType = false, isStandoffTagType = false - )), + ) + ), TypeableVariable(variableName = "book1") -> Set( NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#book".toSmartIri, isResourceType = true, isValueType = false, isStandoffTagType = false - )) + ) + ) ) ) @@ -964,133 +993,181 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { entities = Map( TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "linkingProp1") -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "date") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - isValueType = true), + isValueType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#creationDate".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - objectIsValueType = true), - TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasFamilyName".toSmartIri) -> PropertyTypeInfo( + objectIsValueType = true + ), + TypeableIri(iri = + "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasFamilyName".toSmartIri + ) -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableIri(iri = "http://rdfh.ch/0801/H7s3FmuWTkaCXa54eFANOA".toSmartIri) -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "name") -> NonPropertyTypeInfo( typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - isValueType = true), + isValueType = true + ), TypeableVariable(variableName = "letter") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true) - )) + objectIsResourceType = true + ) + ) + ) val TypeInferenceResultNoSubject: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "linkingProp1") -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "date") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - isValueType = true), + isValueType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#creationDate".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - objectIsValueType = true), - TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasFamilyName".toSmartIri) -> PropertyTypeInfo( + objectIsValueType = true + ), + TypeableIri(iri = + "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasFamilyName".toSmartIri + ) -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableIri(iri = "http://rdfh.ch/0801/H7s3FmuWTkaCXa54eFANOA".toSmartIri) -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "name") -> NonPropertyTypeInfo( typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - isValueType = true), + isValueType = true + ), TypeableVariable(variableName = "letter") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true) - )) + objectIsResourceType = true + ) + ) + ) val TypeInferenceResult2: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "linkingProp1") -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + objectIsResourceType = true + ), TypeableVariable(variableName = "date") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - isValueType = true), + isValueType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#creationDate".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableIri(iri = "http://rdfh.ch/0801/H7s3FmuWTkaCXa54eFANOA".toSmartIri) -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "letter") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true) - )) + objectIsResourceType = true + ) + ) + ) val TypeInferenceResult3: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "book") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#book".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://purl.org/dc/terms/title".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableVariable(variableName = "title") -> NonPropertyTypeInfo( typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - isValueType = true) - )) + isValueType = true + ) + ) + ) val TypeInferenceResult4: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableIri(iri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true), - TypeableIri(iri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#pubdate".toSmartIri) -> PropertyTypeInfo( + objectIsValueType = true + ), + TypeableIri(iri = + "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#pubdate".toSmartIri + ) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableVariable(variableName = "book") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#book".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "pubdate") -> NonPropertyTypeInfo( typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Date".toSmartIri, - isValueType = true), + isValueType = true + ), TypeableVariable(variableName = "title") -> NonPropertyTypeInfo( typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - isValueType = true) - )) + isValueType = true + ) + ) + ) val TypeInferenceResult5: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "mainRes") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#book".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableVariable(variableName = "titleProp") -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableVariable(variableName = "propVal0") -> NonPropertyTypeInfo( typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - isValueType = true) - )) + isValueType = true + ) + ) + ) val TypeInferenceResult6: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( @@ -1118,7 +1195,9 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { isValueType = false, isStandoffTagType = false ), - TypeableIri(iri = "http://api.knora.org/ontology/knora-api/v2#textValueHasStandoff".toSmartIri) -> PropertyTypeInfo( + TypeableIri(iri = + "http://api.knora.org/ontology/knora-api/v2#textValueHasStandoff".toSmartIri + ) -> PropertyTypeInfo( objectTypeIri = "http://api.knora.org/ontology/knora-api/v2#StandoffTag".toSmartIri, objectIsResourceType = false, objectIsValueType = false, @@ -1138,120 +1217,130 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { isResourceType = false, isValueType = true, isStandoffTagType = false - ))) + ) + ) + ) ) val QueryWithRdfsLabelAndLiteral: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label "Zeitglöcklein des Lebens und Leidens Christi" . + |} """.stripMargin val QueryWithRdfsLabelAndVariable: String = """ - |PREFIX incunabula: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?book knora-api:isMainResource true . - | - |} WHERE { - | ?book rdf:type incunabula:book . - | ?book rdfs:label ?label . - | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") - |} + |PREFIX incunabula: + |PREFIX knora-api: + | + |CONSTRUCT { + | ?book knora-api:isMainResource true . + | + |} WHERE { + | ?book rdf:type incunabula:book . + | ?book rdfs:label ?label . + | FILTER(?label = "Zeitglöcklein des Lebens und Leidens Christi") + |} """.stripMargin val RdfsLabelWithLiteralResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "book") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#book".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true) - )) + objectIsValueType = true + ) + ) + ) val RdfsLabelWithVariableResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "book") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#book".toSmartIri, - isResourceType = true), + isResourceType = true + ), TypeableIri(iri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri) -> PropertyTypeInfo( objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true), + objectIsValueType = true + ), TypeableVariable(variableName = "label") -> NonPropertyTypeInfo( typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - isValueType = true) - )) + isValueType = true + ) + ) + ) val QueryWithRedundantTypes: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?letter knora-api:isMainResource true . - | ?author knora-api:isMainResource true . - |} WHERE { - | ?letter rdf:type beol:writtenSource . - | - | ?letter rdf:type beol:letter . - | - | ?letter beol:hasAuthor ?author . - | - | ?author beol:hasFamilyName ?familyName . - | - | FILTER knora-api:matchText(?familyName, "Bernoulli") - | - |} ORDER BY ?date + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?letter knora-api:isMainResource true . + | ?author knora-api:isMainResource true . + |} WHERE { + | ?letter rdf:type beol:writtenSource . + | + | ?letter rdf:type beol:letter . + | + | ?letter beol:hasAuthor ?author . + | + | ?author beol:hasFamilyName ?familyName . + | + | FILTER knora-api:matchText(?familyName, "Bernoulli") + | + |} ORDER BY ?date """.stripMargin val QueryWithInconsistentTypes3: String = """ - |PREFIX knora-api: - |PREFIX beol: - | - |CONSTRUCT { - | ?person knora-api:isMainResource true . - | ?document beol:hasAuthor ?person . - |} WHERE { - | ?person a knora-api:Resource . - | ?person a beol:person . - | - | ?document beol:hasAuthor ?person . - | beol:hasAuthor knora-api:objectType knora-api:Resource . - | ?document a knora-api:Resource . - | { ?document a beol:manuscript . } UNION { ?document a beol:letter .} - |} + |PREFIX knora-api: + |PREFIX beol: + | + |CONSTRUCT { + | ?person knora-api:isMainResource true . + | ?document beol:hasAuthor ?person . + |} WHERE { + | ?person a knora-api:Resource . + | ?person a beol:person . + | + | ?document beol:hasAuthor ?person . + | beol:hasAuthor knora-api:objectType knora-api:Resource . + | ?document a knora-api:Resource . + | { ?document a beol:manuscript . } UNION { ?document a beol:letter .} + |} """.stripMargin val QueryWithGravsearchOptions: String = """PREFIX anything: - |PREFIX knora-api: - | - |CONSTRUCT { - | ?thing knora-api:isMainResource true . - |} WHERE { - | knora-api:GravsearchOptions knora-api:useInference false . - | ?thing a anything:Thing . - |}""".stripMargin + |PREFIX knora-api: + | + |CONSTRUCT { + | ?thing knora-api:isMainResource true . + |} WHERE { + | knora-api:GravsearchOptions knora-api:useInference false . + | ?thing a anything:Thing . + |}""".stripMargin val GravsearchOptionsResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "thing") -> NonPropertyTypeInfo( typeIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri, isResourceType = true - )), + ) + ), entitiesInferredFromProperties = Map() ) @@ -1279,44 +1368,66 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val multipleDetectedTypes: IntermediateTypeInspectionResult = IntermediateTypeInspectionResult( entities = Map( TypeableVariable(variableName = "mainRes") -> Set( - NonPropertyTypeInfo(typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true) + NonPropertyTypeInfo( + typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ) ) ), entitiesInferredFromPropertyIris = Map( TypeableVariable(variableName = "mainRes") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true) - )) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ) + ) + ) ) // remove type basicLetter val intermediateTypesWithoutResource: IntermediateTypeInspectionResult = multipleDetectedTypes.removeType( TypeableVariable(variableName = "mainRes"), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ) ) // Is it removed from entities? intermediateTypesWithoutResource.entities should contain( TypeableVariable(variableName = "mainRes") -> Set( - NonPropertyTypeInfo(typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true) - )) + NonPropertyTypeInfo( + typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) + ) // Is it removed from entitiesInferredFromProperties? intermediateTypesWithoutResource.entitiesInferredFromPropertyIris should contain( TypeableVariable(variableName = "mainRes") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true))) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) + ) } "refine the inspected types for each typeableEntity" in { @@ -1325,22 +1436,32 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val parsedQuery = GravsearchParser.parseQuery(QueryRdfTypeRule) val (_, entityInfo) = Await.result( typeInspectionRunner.getUsageIndexAndEntityInfos(parsedQuery.whereClause, requestingUser = anythingAdminUser), - timeout) + timeout + ) val multipleDetectedTypes: IntermediateTypeInspectionResult = IntermediateTypeInspectionResult( entities = Map( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ) ) ), entitiesInferredFromPropertyIris = Map( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true)) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ) + ) ) ) @@ -1352,8 +1473,12 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { assert(refinedIntermediateResults.entities.size == 1) refinedIntermediateResults.entities should contain( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true))) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) + ) assert(refinedIntermediateResults.entitiesInferredFromPropertyIris.isEmpty) } @@ -1363,49 +1488,72 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val parsedQuery = GravsearchParser.parseQuery(QueryRdfTypeRule) val (usageIndex, entityInfo) = Await.result( typeInspectionRunner.getUsageIndexAndEntityInfos(parsedQuery.whereClause, requestingUser = anythingAdminUser), - timeout) + timeout + ) val inconsistentTypes: IntermediateTypeInspectionResult = IntermediateTypeInspectionResult( entities = Map( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + isResourceType = true + ) ), TypeableVariable(variableName = "linkingProp1") -> Set( - PropertyTypeInfo(objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - objectIsResourceType = true), - PropertyTypeInfo(objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true) + PropertyTypeInfo( + objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + objectIsResourceType = true + ), + PropertyTypeInfo( + objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + objectIsResourceType = true + ) ) ), entitiesInferredFromPropertyIris = Map( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true))) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) + ) ) val refinedIntermediateResults = typeInspectionRunner.refineDeterminedTypes(intermediateResult = inconsistentTypes, entityInfo = entityInfo) - val sanitizedResults = typeInspectionRunner.sanitizeInconsistentResourceTypes(lastResults = - refinedIntermediateResults, - usageIndex.querySchema, - entityInfo = entityInfo) + val sanitizedResults = typeInspectionRunner.sanitizeInconsistentResourceTypes( + lastResults = refinedIntermediateResults, + usageIndex.querySchema, + entityInfo = entityInfo + ) val expectedResult: IntermediateTypeInspectionResult = IntermediateTypeInspectionResult( entities = Map( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - isResourceType = true)), + NonPropertyTypeInfo( + typeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, + isResourceType = true + ) + ), TypeableVariable(variableName = "linkingProp1") -> Set( - PropertyTypeInfo(objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, - objectIsResourceType = true)) + PropertyTypeInfo( + objectTypeIri = "http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, + objectIsResourceType = true + ) + ) ), entitiesInferredFromPropertyIris = Map( TypeableVariable(variableName = "letter") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true)) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) ) ) @@ -1418,40 +1566,56 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val parsedQuery = GravsearchParser.parseQuery(QueryWithInconsistentTypes3) val (usageIndex, entityInfo) = Await.result( typeInspectionRunner.getUsageIndexAndEntityInfos(parsedQuery.whereClause, requestingUser = anythingAdminUser), - timeout) + timeout + ) val inconsistentTypes: IntermediateTypeInspectionResult = IntermediateTypeInspectionResult( entities = Map( TypeableVariable(variableName = "document") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true), - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#manuscript".toSmartIri, - isResourceType = true) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#manuscript".toSmartIri, + isResourceType = true + ) ) ), entitiesInferredFromPropertyIris = Map( TypeableVariable(variableName = "document") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true))) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) + ) ) val refinedIntermediateResults = typeInspectionRunner.refineDeterminedTypes(intermediateResult = inconsistentTypes, entityInfo = entityInfo) - val sanitizedResults = typeInspectionRunner.sanitizeInconsistentResourceTypes(lastResults = - refinedIntermediateResults, - usageIndex.querySchema, - entityInfo = entityInfo) + val sanitizedResults = typeInspectionRunner.sanitizeInconsistentResourceTypes( + lastResults = refinedIntermediateResults, + usageIndex.querySchema, + entityInfo = entityInfo + ) val expectedResult: IntermediateTypeInspectionResult = IntermediateTypeInspectionResult( entities = Map( TypeableVariable(variableName = "document") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#writtenSource".toSmartIri, - isResourceType = true)) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#writtenSource".toSmartIri, + isResourceType = true + ) + ) ), entitiesInferredFromPropertyIris = Map( TypeableVariable(variableName = "document") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, - isResourceType = true)) + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#letter".toSmartIri, + isResourceType = true + ) + ) ) ) @@ -1468,19 +1632,28 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val expectedResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "person") -> - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + isResourceType = true + ), TypeableVariable(variableName = "document") -> - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#writtenSource".toSmartIri, - isResourceType = true), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#writtenSource".toSmartIri, + isResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasAuthor".toSmartIri) -> - PropertyTypeInfo(objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true) + PropertyTypeInfo( + objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + objectIsResourceType = true + ) ), entitiesInferredFromProperties = Map( TypeableVariable(variableName = "person") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true)), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + isResourceType = true + ) + ) ) ) @@ -1490,23 +1663,23 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { "types resulted from a query with optional" in { val queryWithOptional: String = """ - |PREFIX beol: - |PREFIX knora-api: - |PREFIX xsd: - | - |CONSTRUCT { - | ?document knora-api:isMainResource true . - |} WHERE { - | ?document rdf:type beol:writtenSource . - | - | OPTIONAL { - | ?document beol:hasRecipient ?recipient . - | - | ?recipient beol:hasFamilyName ?familyName . - | - | FILTER knora-api:matchText(?familyName, "Bernoulli") - |} - |} + |PREFIX beol: + |PREFIX knora-api: + |PREFIX xsd: + | + |CONSTRUCT { + | ?document knora-api:isMainResource true . + |} WHERE { + | ?document rdf:type beol:writtenSource . + | + | OPTIONAL { + | ?document beol:hasRecipient ?recipient . + | + | ?recipient beol:hasFamilyName ?familyName . + | + | FILTER knora-api:matchText(?familyName, "Bernoulli") + |} + |} """.stripMargin val typeInspectionRunner = new GravsearchTypeInspectionRunner(responderData = responderData, inferTypes = true) @@ -1522,31 +1695,46 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { val expectedResult: GravsearchTypeInspectionResult = GravsearchTypeInspectionResult( entities = Map( TypeableVariable(variableName = "document") -> - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ), TypeableVariable(variableName = "familyName") -> NonPropertyTypeInfo(typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, isValueType = true), TypeableVariable(variableName = "recipient") -> - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + isResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasRecipient".toSmartIri) -> - PropertyTypeInfo(objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - objectIsResourceType = true), + PropertyTypeInfo( + objectTypeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + objectIsResourceType = true + ), TypeableIri(iri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#hasFamilyName".toSmartIri) -> - PropertyTypeInfo(objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, - objectIsValueType = true) + PropertyTypeInfo( + objectTypeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, + objectIsValueType = true + ) ), entitiesInferredFromProperties = Map( TypeableVariable(variableName = "document") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, - isResourceType = true)), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#basicLetter".toSmartIri, + isResourceType = true + ) + ), TypeableVariable(variableName = "familyName") -> Set( - NonPropertyTypeInfo(typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, isValueType = true)), + NonPropertyTypeInfo(typeIri = "http://www.w3.org/2001/XMLSchema#string".toSmartIri, isValueType = true) + ), TypeableVariable(variableName = "recipient") -> Set( - NonPropertyTypeInfo(typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, - isResourceType = true)), + NonPropertyTypeInfo( + typeIri = "http://0.0.0.0:3333/ontology/0801/beol/simple/v2#person".toSmartIri, + isResourceType = true + ) + ) ) ) @@ -1573,7 +1761,8 @@ class GravsearchTypeInspectorSpec extends CoreSpec() with ImplicitSender { assert(result.entities == TypeInferenceResult1.entities) result.entitiesInferredFromProperties.keySet should contain(TypeableVariable("date")) result.entitiesInferredFromProperties.keySet should contain( - TypeableIri("http://rdfh.ch/0801/H7s3FmuWTkaCXa54eFANOA".toSmartIri)) + TypeableIri("http://rdfh.ch/0801/H7s3FmuWTkaCXa54eFANOA".toSmartIri) + ) result.entitiesInferredFromProperties.keySet should not contain TypeableVariable("linkingProp1") } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2Spec.scala index 1a1ac4588b..21d74cb1d2 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/StandoffTagUtilV2Spec.scala @@ -33,8 +33,8 @@ import scala.concurrent.Await import scala.concurrent.duration._ /** - * Tests [[StandoffTagUtilV2]]. - */ + * Tests [[StandoffTagUtilV2]]. + */ class StandoffTagUtilV2Spec extends CoreSpec { private implicit val timeout: Timeout = 10.seconds private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -205,7 +205,8 @@ class StandoffTagUtilV2Spec extends CoreSpec { StandoffTagStringAttributeV2( standoffPropertyIri = "http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType".toSmartIri, value = "letter" - )), + ) + ), startIndex = 0, endIndex = None, dataType = None, @@ -248,7 +249,8 @@ class StandoffTagUtilV2Spec extends CoreSpec { StandoffTagInternalReferenceAttributeV2( standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasInternalReference".toSmartIri, value = "first" - )), + ) + ), startIndex = 3, endIndex = None, dataType = Some(StandoffDataTypeClasses.StandoffInternalReferenceTag), @@ -268,7 +270,8 @@ class StandoffTagUtilV2Spec extends CoreSpec { StandoffTagStringAttributeV2( standoffPropertyIri = "http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType".toSmartIri, value = "letter" - )), + ) + ), startIndex = 0, endIndex = None, dataType = None, @@ -335,11 +338,13 @@ class StandoffTagUtilV2Spec extends CoreSpec { val standoffTagsV1: Vector[StandoffTagV2] = Await.result( StandoffTagUtilV2.createStandoffTagsV2FromSelectResults(sparqlResultsV1, responderManager, anythingUserProfile), - 10.seconds) + 10.seconds + ) val standoffTagsV2: Vector[StandoffTagV2] = Await.result( StandoffTagUtilV2.createStandoffTagsV2FromSelectResults(sparqlResultsV2, responderManager, anythingUserProfile), - 10.seconds) + 10.seconds + ) assert(standoffTagsV1 == expectedStandoffTagsV1) assert(standoffTagsV2 == expectedStandoffTagsV2) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtilSpec.scala index 313e8cbaf5..b606c03470 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtilSpec.scala @@ -28,8 +28,8 @@ import org.xmlunit.builder.{DiffBuilder, Input} import org.xmlunit.diff.Diff /** - * Tests [[XMLToStandoffUtil]]. - */ + * Tests [[XMLToStandoffUtil]]. + */ class XMLToStandoffUtilSpec extends CoreSpec { "The XML to standoff utility" should { @@ -130,24 +130,24 @@ class XMLToStandoffUtilSpec extends CoreSpec { val diplomaticTranscription = """ - | - |
CLI
. - |Examen modi Renaldiniani inscribendi q̌vis polygona regularia in circulo, - |depromti ex Lib. II. de Resol. & Composi: Mathem: p. 367. (vid. Sturmii - |Mathesin enucleatam p. 38.) - |
+ | + |
CLI
. + |Examen modi Renaldiniani inscribendi q̌vis polygona regularia in circulo, + |depromti ex Lib. II. de Resol. & Composi: Mathem: p. 367. (vid. Sturmii + |Mathesin enucleatam p. 38.) + |
""".stripMargin val criticalText = """ - | - |
- |CLI - |Examen modi Renaldiniani inscribendi quaevis polygona regularia in circulo, - |depromti ex Lib. II. de Resolutione & Compositione Mathematica p. 367. - |(vide Sturmii Mathesin enucleatam p. 38.) - |
- |
+ | + |
+ |CLI + |Examen modi Renaldiniani inscribendi quaevis polygona regularia in circulo, + |depromti ex Lib. II. de Resolutione & Compositione Mathematica p. 367. + |(vide Sturmii Mathesin enucleatam p. 38.) + |
+ |
""".stripMargin val diploTextWithStandoff: TextWithStandoff = @@ -167,15 +167,15 @@ class XMLToStandoffUtilSpec extends CoreSpec { val expectedCriticalTextDiffsAsXml = """ - | - | - |CLI. - |Examen modi Renaldiniani inscribendi q̌uaevis polygona regularia in circulo, - |depromti ex Lib. II. de Resol.utione & Composi:tione Mathem:atica p. 367. - |(vid.e Sturmii - | Mathesin enucleatam p. 38.) - | - | + | + | + |CLI. + |Examen modi Renaldiniani inscribendi q̌uaevis polygona regularia in circulo, + |depromti ex Lib. II. de Resol.utione & Composi:tione Mathem:atica p. 367. + |(vid.e Sturmii + | Mathesin enucleatam p. 38.) + | + | """.stripMargin val xmlDiff: Diff = DiffBuilder @@ -205,21 +205,21 @@ class XMLToStandoffUtilSpec extends CoreSpec { val diplomaticTranscription = """ - | - |Modus hic est: Fiat triang: æquil: ABD, divisâq́ diametro AB in tot partes æquales, - |quot laterum est figura inscribenda, duabusq́ earum p̃termissis et à B versùs A, ducat, - |per initium tertiæ recta DF, & hinc recta FB, quam putat esse latus polygoni optati - |Anal: Sit secta diameter utcunq́ in E, e , ductæq́ DEF, {FA,} & FB & demissa in diametrum per- - |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} - | + | + |Modus hic est: Fiat triang: æquil: ABD, divisâq́ diametro AB in tot partes æquales, + |quot laterum est figura inscribenda, duabusq́ earum p̃termissis et à B versùs A, ducat, + |per initium tertiæ recta DF, & hinc recta FB, quam putat esse latus polygoni optati + |Anal: Sit secta diameter utcunq́ in E, e , ductæq́ DEF, {FA,} & FB & demissa in diametrum per- + |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} + | """.stripMargin val criticalText = """ - | - |Modus hic est: Fiat triang[ulum] aequil[aterum] ABD divisaque diametro AB in tot partes aequales, quot laterum est figura inscribenda, duabusque earum praetermissis a B versus A, ducatur per initium tertiae recta DF, & hinc recta FB, quam putat esse latus polygoni optati. - |Anal[ysis]: Sit secta diameter utcunque in E, e, ductaeque DEF, FA, & FB & demissa in diametrum perpendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{4aa-xx} - | + | + |Modus hic est: Fiat triang[ulum] aequil[aterum] ABD divisaque diametro AB in tot partes aequales, quot laterum est figura inscribenda, duabusque earum praetermissis a B versus A, ducatur per initium tertiae recta DF, & hinc recta FB, quam putat esse latus polygoni optati. + |Anal[ysis]: Sit secta diameter utcunque in E, e, ductaeque DEF, FA, & FB & demissa in diametrum perpendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{4aa-xx} + | """.stripMargin val diploTextWithStandoff: TextWithStandoff = @@ -241,13 +241,13 @@ class XMLToStandoffUtilSpec extends CoreSpec { val expectedCriticalTextDiffsAsXml = """ - | - |Modus hic est: Fiat triang: æquil:[ulum] aequil[aterum] ABD, divisâq́ aque diametro AB in tot partes æaequales, - | quot laterum est figura inscribenda, duabusq́ ue earum p̃raetermissis et àa B versùus A, ducat, - |ur per initium tertiæae recta DF, & hinc recta FB, quam putat esse latus polygoni optati. - |Anal[ysis]: Sit secta diameter utcunq́ ue in E, e , ductæq́ aeque DEF, {FA,} & FB & demissa in diametrum per- - |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} - | + | + |Modus hic est: Fiat triang: æquil:[ulum] aequil[aterum] ABD, divisâq́ aque diametro AB in tot partes æaequales, + | quot laterum est figura inscribenda, duabusq́ ue earum p̃raetermissis et àa B versùus A, ducat, + |ur per initium tertiæae recta DF, & hinc recta FB, quam putat esse latus polygoni optati. + |Anal[ysis]: Sit secta diameter utcunq́ ue in E, e , ductæq́ aeque DEF, {FA,} & FB & demissa in diametrum per- + |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} + | """.stripMargin val xmlDiff: Diff = DiffBuilder @@ -283,7 +283,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { // strikethrough, and a repeated word (which could be the author's mistake or the transcriber's mistake). val diplomaticTranscription1 = """ - |Ich habe d Bus heute genommen, weil ich ich verspätet war. + |Ich habe d Bus heute genommen, weil ich ich verspätet war. """.stripMargin // Convert the markup in the transcription to standoff and back again to check that it's correct. @@ -303,7 +303,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { // and corrects the repeated word. val editorialText1 = """ - |Ich habe den Bus genommen, weil ich verspätet war. + |Ich habe den Bus genommen, weil ich verspätet war. """.stripMargin val edito1TextWithStandoff: TextWithStandoff = standoffUtil.xml2TextWithStandoff(editorialText1, log = log) @@ -320,7 +320,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { val expectedEditorialDiffs1AsXml = """ - |Ich habe den Bus heute genommen, weil ich ich verspätet war. + |Ich habe den Bus heute genommen, weil ich ich verspätet war. """.stripMargin val editorialDiffs1AsXml: String = standoffUtil.standoffDiffs2Xml( @@ -340,7 +340,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { // colour. val diplomaticTranscription2 = """ - |Ich habe d Bahn heute genommen, weil ich verspätet war. + |Ich habe d Bahn heute genommen, weil ich verspätet war. """.stripMargin // The editor now rebases the editorial text against the revised transcription, by making new diffs. @@ -361,7 +361,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { val expectedEditorialDiffs2AsXml = """ - |Ich habe d Bahn heuteen Bus genommen, weil ich verspätet war. + |Ich habe d Bahn heuteen Bus genommen, weil ich verspätet war. """.stripMargin val editorialDiffs2AsXml: String = standoffUtil.standoffDiffs2Xml( @@ -390,7 +390,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { val editorialText2 = """ - |Ich habe die Bahn genommen, weil ich verspätet war. + |Ich habe die Bahn genommen, weil ich verspätet war. """.stripMargin val edito2TextWithStandoff: TextWithStandoff = standoffUtil.xml2TextWithStandoff(editorialText2, log = log) @@ -407,7 +407,7 @@ class XMLToStandoffUtilSpec extends CoreSpec { val expectedEditorialDiffs3AsXml = """ - |Ich habe die Bahn heute genommen, weil ich verspätet war. + |Ich habe die Bahn heute genommen, weil ich verspätet war. """.stripMargin val editorialDiffs3AsXml: String = standoffUtil.standoffDiffs2Xml( @@ -436,22 +436,22 @@ class XMLToStandoffUtilSpec extends CoreSpec { val diplomaticTranscription = """ - | - |CLI. - |Examen modi Renaldiniani inscribendi q̌vis polygona regularia in circulo, - |depromti ex Lib. II. de Resol. & Composi: Mathem: p. 367. (vid. Sturmii - |Mathesin enucleatam p. 38.) - | + | + |CLI. + |Examen modi Renaldiniani inscribendi q̌vis polygona regularia in circulo, + |depromti ex Lib. II. de Resol. & Composi: Mathem: p. 367. (vid. Sturmii + |Mathesin enucleatam p. 38.) + | """.stripMargin val criticalText = """ - | - |CLI. - |Examen modi Renaldiniani inscribendi q̌vis polygona regularia in circulo, - |depromti ex Lib. II. de Resol. & Composi: Mathem: p. 367. (vid. Sturmii - |Mathesin enucleatam p. 38.) - | + | + |CLI. + |Examen modi Renaldiniani inscribendi q̌vis polygona regularia in circulo, + |depromti ex Lib. II. de Resol. & Composi: Mathem: p. 367. (vid. Sturmii + |Mathesin enucleatam p. 38.) + | """.stripMargin val diploTextWithStandoff: TextWithStandoff = @@ -493,24 +493,24 @@ class XMLToStandoffUtilSpec extends CoreSpec { val diplomaticTranscription = """ - | - |Modus hic est: Fiat triang: æquil: ABD, divisâq́ diametro AB in tot partes æquales, - |quot laterum est figura inscribenda, duabusq́ earum p̃termissis et à B versùs A, ducat, - |per initium tertiæ recta DF, & hinc recta FB, quam putat esse latus polygoni optati - |Anal: Sit secta diameter utcunq́ in E, e , ductæq́ DEF, FA, & FB & demissa in diametrum per- - |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} - | + | + |Modus hic est: Fiat triang: æquil: ABD, divisâq́ diametro AB in tot partes æquales, + |quot laterum est figura inscribenda, duabusq́ earum p̃termissis et à B versùs A, ducat, + |per initium tertiæ recta DF, & hinc recta FB, quam putat esse latus polygoni optati + |Anal: Sit secta diameter utcunq́ in E, e , ductæq́ DEF, FA, & FB & demissa in diametrum per- + |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} + | """.stripMargin val criticalText = """ - | - |Modus hic est: Fiat triang: æquil: ABD, divisâq́ diametro AB in tot partes æquales, - |quot laterum est figura inscribenda, duabusq́ earum p̃termissis et à B versùs A, ducat, - |per initium tertiæ recta DF, & hinc recta FB, quam putat esse latus polygoni optati - |Anal: Sit secta diameter utcunq́ in E, e , ductæq́ DEF, FA, & FB & demissa in diametrum per- - |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} - | + | + |Modus hic est: Fiat triang: æquil: ABD, divisâq́ diametro AB in tot partes æquales, + |quot laterum est figura inscribenda, duabusq́ earum p̃termissis et à B versùs A, ducat, + |per initium tertiæ recta DF, & hinc recta FB, quam putat esse latus polygoni optati + |Anal: Sit secta diameter utcunq́ in E, e , ductæq́ DEF, FA, & FB & demissa in diametrum per- + |pendicularis FG, fiat CB=a, CE vel Ce=b, FB=x, unde AF=\sqrt{\mathstrut}\,\overline{4aa-xx} + | """.stripMargin val diploTextWithStandoff: TextWithStandoff = @@ -544,13 +544,13 @@ class XMLToStandoffUtilSpec extends CoreSpec { val BEBBXML = """ - | - |

- | Vir Celeberrime, Fautor et Amice Honoratissime.

- |

Accepi, post multorum annorum silentium, jam ante aliquot menses gratissimas Litteras TuasJohannes Scheuchzer an Johann I Bernoulli von 1735.02.11. cum tribus exemplis Dissertationis eruditissimae de Tesseris Badensibus,Scheuchzer, Johannes, Dissertatio philosophica de tesseris Badensibus, disquisitioni publicae exposita a Johanne Scheuchzero, M. D. Philosophiae naturalis Professore publico, Acad. Naturae Curiosorum dicto Philippo. Ac respp. pro consequendo rite examine philosophico Hartmanno Friderico Oerio, Rodolfo Huldrico, Marco Wyssio, Johanne Melchiore Boeschio ..., Tiguri [Zürich] (Heidegger) 1735. quorum unum mihi servatum perlegi summa cum voluptate, reliqua duo distribui, ut jussisti, Adgnato meo Nicolao meoque Filio Danieli,Nicolaus I Bernoulli (1687-1759) und Daniel I Bernoulli (1700-1782). qui ambo gratias mecum Tibi agunt maximas: Perlectio hujus speciminis mihi sustulit scrupulum, ex quorundamIm Manuskript steht "quorundum". opinione subnatum, qui credunt tesseras illas esse opus a natura productum, ob ingentem earum jam repertarum multitudinem, sed ratiocinia Tua tam valida tamque certa mihi esse videntur, ut amplius dubitare non possim easdem illas tesseras ab arte humana provenisse, quem autem in usum tanta copia fuerit fabrefacta et tam exili sub forma saltem plerasque quas egomet ipse vidi divinare non possum. Distuli responsum ad litteras Tuas Vir Clarissime, quia scio Te multis negotiis esse obrutum, sicuti et ego sum, hoc praesertim tempore quo mihi de novo impositum est Decanatus in facultate nostra munus annuum humeris meis onerosissimum, tum et silentii mei causa fuit tenuitas mearum litterarum vix portorium merentium, quam ob rationem respondere forsan diutius distulissem commodam expectans mittendi litteras occasionem, nisi me ad scribendum impulisset iterata sollicitatio Clariss. Menckenii Actorum Lips. Editoris, qui impense me urget ut sibi procurem Fratris TuiJohann Jakob Scheuchzer (1672-1733). b.m. Vitae Historiam; En ipsa ejus verba "Confido Tua opera fieri posse, ut ab Haeredibus Celeberrimi Scheuchzeri brevem vitae Scheuchzerianae narrationem impetrare possim, quo magno me tibi beneficio obstrinxeris" etc. Memini jam ante annum me eadem de re sollicitatum statim scripsisse ad Cl. Gessnerum Tuum Collegam, sed nihil responsi obtinuisse: Spero nunc Te qui Manes beatissimi Fratris, Viri celeberrimi et de republica litter. longe maxime meriti, etiamnum flagrantissime colis, non commissurum, ut Tanti Viri memoria cum exuviis sepulte maneat; Quare si desiderio Orbis eruditi non minus quam Menckenii satisfacere volueris, rogo ut succinctam Fratris defuncti Biographiam quantocyus ad me mittas, quam porro Lipsiam mittendi occasionem habebo post octo decemve dies aut ad summum in fine hujus mensis. Vale Vir Amicissime et fave T. T. Joh. Bernoulli

- |

Basil. a.d. 18. Junj 1735.

- |
- | + | + |

+ | Vir Celeberrime, Fautor et Amice Honoratissime.

+ |

Accepi, post multorum annorum silentium, jam ante aliquot menses gratissimas Litteras TuasJohannes Scheuchzer an Johann I Bernoulli von 1735.02.11. cum tribus exemplis Dissertationis eruditissimae de Tesseris Badensibus,Scheuchzer, Johannes, Dissertatio philosophica de tesseris Badensibus, disquisitioni publicae exposita a Johanne Scheuchzero, M. D. Philosophiae naturalis Professore publico, Acad. Naturae Curiosorum dicto Philippo. Ac respp. pro consequendo rite examine philosophico Hartmanno Friderico Oerio, Rodolfo Huldrico, Marco Wyssio, Johanne Melchiore Boeschio ..., Tiguri [Zürich] (Heidegger) 1735. quorum unum mihi servatum perlegi summa cum voluptate, reliqua duo distribui, ut jussisti, Adgnato meo Nicolao meoque Filio Danieli,Nicolaus I Bernoulli (1687-1759) und Daniel I Bernoulli (1700-1782). qui ambo gratias mecum Tibi agunt maximas: Perlectio hujus speciminis mihi sustulit scrupulum, ex quorundamIm Manuskript steht "quorundum". opinione subnatum, qui credunt tesseras illas esse opus a natura productum, ob ingentem earum jam repertarum multitudinem, sed ratiocinia Tua tam valida tamque certa mihi esse videntur, ut amplius dubitare non possim easdem illas tesseras ab arte humana provenisse, quem autem in usum tanta copia fuerit fabrefacta et tam exili sub forma saltem plerasque quas egomet ipse vidi divinare non possum. Distuli responsum ad litteras Tuas Vir Clarissime, quia scio Te multis negotiis esse obrutum, sicuti et ego sum, hoc praesertim tempore quo mihi de novo impositum est Decanatus in facultate nostra munus annuum humeris meis onerosissimum, tum et silentii mei causa fuit tenuitas mearum litterarum vix portorium merentium, quam ob rationem respondere forsan diutius distulissem commodam expectans mittendi litteras occasionem, nisi me ad scribendum impulisset iterata sollicitatio Clariss. Menckenii Actorum Lips. Editoris, qui impense me urget ut sibi procurem Fratris TuiJohann Jakob Scheuchzer (1672-1733). b.m. Vitae Historiam; En ipsa ejus verba "Confido Tua opera fieri posse, ut ab Haeredibus Celeberrimi Scheuchzeri brevem vitae Scheuchzerianae narrationem impetrare possim, quo magno me tibi beneficio obstrinxeris" etc. Memini jam ante annum me eadem de re sollicitatum statim scripsisse ad Cl. Gessnerum Tuum Collegam, sed nihil responsi obtinuisse: Spero nunc Te qui Manes beatissimi Fratris, Viri celeberrimi et de republica litter. longe maxime meriti, etiamnum flagrantissime colis, non commissurum, ut Tanti Viri memoria cum exuviis sepulte maneat; Quare si desiderio Orbis eruditi non minus quam Menckenii satisfacere volueris, rogo ut succinctam Fratris defuncti Biographiam quantocyus ad me mittas, quam porro Lipsiam mittendi occasionem habebo post octo decemve dies aut ad summum in fine hujus mensis. Vale Vir Amicissime et fave T. T. Joh. Bernoulli

+ |

Basil. a.d. 18. Junj 1735.

+ |
+ | """.stripMargin val standoffUtil = new XMLToStandoffUtil() @@ -559,7 +559,8 @@ class XMLToStandoffUtilSpec extends CoreSpec { val textWithStandoff: TextWithStandoff = standoffUtil.xml2TextWithStandoff( BEBBXML, tagsWithSeparator = List(XMLTagSeparatorRequired(maybeNamespace = None, tagname = "p", maybeClassname = None)), - log = log) + log = log + ) // make sure that there are as many information separator two as there are paragraphs (there are three paragraphs) assert(StringFormatter.INFORMATION_SEPARATOR_TWO.toString.r.findAllIn(textWithStandoff.text).length == 3) @@ -570,14 +571,14 @@ class XMLToStandoffUtilSpec extends CoreSpec { val testXML = """ - | - | - |
- | This an element that has a class and it separates words. - |
- |
- |
- | + | + | + |
+ | This an element that has a class and it separates words. + |
+ |
+ |
+ | """.stripMargin val standoffUtil = new XMLToStandoffUtil() @@ -587,7 +588,8 @@ class XMLToStandoffUtilSpec extends CoreSpec { testXML, tagsWithSeparator = List(XMLTagSeparatorRequired(maybeNamespace = None, tagname = "div", maybeClassname = Some("paragraph"))), - log = log) + log = log + ) // make sure that there are as many information separator two as there are paragraphs (there are three paragraphs) assert(StringFormatter.INFORMATION_SEPARATOR_TWO.toString.r.findAllIn(textWithStandoff.text).length == 1) @@ -600,97 +602,97 @@ object XMLToStandoffUtilSpec { val simpleXmlDoc: String = """ - |
- | Special Relativity - | - | - | In physics, special relativity is the generally accepted and experimentally well confirmed physical - | theory regarding the relationship between space and time. In Albert Einstein's - | original pedagogical treatment, it is based on two postulates: - | - | - | that the laws of physics are invariant (i.e. identical) in all inertial systems - | (non-accelerating frames of reference). - | that the speed of light in a vacuum is the same for all observers, regardless of the - | motion of the light source. - | - | - | - | - | Einstein originally proposed it in - | 1905 in . - | - | Here is a sentence with a sequence of empty tags: . And then some more text. - | - |
""".stripMargin + |
+ | Special Relativity + | + | + | In physics, special relativity is the generally accepted and experimentally well confirmed physical + | theory regarding the relationship between space and time. In Albert Einstein's + | original pedagogical treatment, it is based on two postulates: + | + | + | that the laws of physics are invariant (i.e. identical) in all inertial systems + | (non-accelerating frames of reference). + | that the speed of light in a vacuum is the same for all observers, regardless of the + | motion of the light source. + | + | + | + | + | Einstein originally proposed it in + | 1905 in . + | + | Here is a sentence with a sequence of empty tags: . And then some more text. + | + |
""".stripMargin val simpleXmlDocWithNestedEmptyTag: String = """ - |
- | Special Relativity - | - | - | In physics, special relativity is the generally accepted and experimentally well confirmed physical - | theory regarding the relationship between space and time. In Albert Einstein's - | original pedagogical treatment, it is based on two postulates: - | - | - | that the laws of physics are invariant (i.e. identical) in all inertial systems - | (non-accelerating frames of reference). - | that the speed of light in a vacuum is the same for all observers, regardless of the - | motion of the light source. - | - | - | - | - | Einstein originally proposed it in - | 1905 in . - | - | Here is a sentence with a sequence of
empty tags: . And then some more text. - |
- |
""".stripMargin + |
+ | Special Relativity + | + | + | In physics, special relativity is the generally accepted and experimentally well confirmed physical + | theory regarding the relationship between space and time. In Albert Einstein's + | original pedagogical treatment, it is based on two postulates: + | + | + | that the laws of physics are invariant (i.e. identical) in all inertial systems + | (non-accelerating frames of reference). + | that the speed of light in a vacuum is the same for all observers, regardless of the + | motion of the light source. + | + | + | + | + | Einstein originally proposed it in + | 1905 in . + | + | Here is a sentence with a sequence of
empty tags: . And then some more text. + |
+ |
""".stripMargin val simpleXmlDocWithNestedEmptyTags: String = """ - |
- | Special Relativity - | - | - | In physics, special relativity is the generally accepted and experimentally well confirmed physical - | theory regarding the relationship between space and time. In Albert Einstein's - | original pedagogical treatment, it is based on two postulates: - | - | - | that the laws of physics are invariant (i.e. identical) in all inertial systems - | (non-accelerating frames of reference). - | that the speed of light in a vacuum is the same for all observers, regardless of the - | motion of the light source. - | - | - | - | - | Einstein originally proposed it in - | 1905 in . - | - | Here is a sentence with a sequence of

empty tags: . And then some more text. - |
- |
""".stripMargin + |
+ | Special Relativity + | + | + | In physics, special relativity is the generally accepted and experimentally well confirmed physical + | theory regarding the relationship between space and time. In Albert Einstein's + | original pedagogical treatment, it is based on two postulates: + | + | + | that the laws of physics are invariant (i.e. identical) in all inertial systems + | (non-accelerating frames of reference). + | that the speed of light in a vacuum is the same for all observers, regardless of the + | motion of the light source. + | + | + | + | + | Einstein originally proposed it in + | 1905 in . + | + | Here is a sentence with a sequence of

empty tags: . And then some more text. + |
+ |
""".stripMargin val xmlDocWithClix: String = """ - | - | - | Scorn not the sonnet; - | critic, you have frowned, - | Mindless of its just honours; - | with this key - | Shakespeare unlocked his heart; - | the melody - | Of this small lute gave ease to Petrarch's wound. - | - | + | + | + | Scorn not the sonnet; + | critic, you have frowned, + | Mindless of its just honours; + | with this key + | Shakespeare unlocked his heart; + | the melody + | Of this small lute gave ease to Petrarch's wound. + | + | """.stripMargin } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLUtilSpec.scala index 18f7f8df67..9d8fdcd30a 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/standoff/XMLUtilSpec.scala @@ -29,8 +29,8 @@ import org.xmlunit.builder.{DiffBuilder, Input} import org.xmlunit.diff.Diff /** - * Tests [[org.knora.webapi.messages.util.standoff.XMLToStandoffUtil]]. - */ + * Tests [[org.knora.webapi.messages.util.standoff.XMLToStandoffUtil]]. + */ class XMLUtilSpec extends CoreSpec { "The XML to standoff utility" should { @@ -39,20 +39,20 @@ class XMLUtilSpec extends CoreSpec { val xml = """ - |test + |test """.stripMargin val xslt = """ - | - | - | - | - | - |
- | - | - |
+ | + | + | + | + | + |
+ | + | + |
""".stripMargin val expected = @@ -73,21 +73,21 @@ class XMLUtilSpec extends CoreSpec { val xml = """ - |test + |test """.stripMargin // closing root tag is invalid val xsltInvalid = """ - | - | - | - | - | - |
- | - | - |
+ | + | + | + |
+ | + | + | diff --git a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/ontologymessages/InputOntologyV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/ontologymessages/InputOntologyV2Spec.scala index 71bf48547e..65427ab421 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/ontologymessages/InputOntologyV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/ontologymessages/InputOntologyV2Spec.scala @@ -30,8 +30,8 @@ import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality.Knora import org.knora.webapi.{ApiV2Complex, CoreSpec} /** - * Tests [[InputOntologyV2]]. - */ + * Tests [[InputOntologyV2]]. + */ class InputOntologyV2Spec extends CoreSpec { import InputOntologyV2Spec._ @@ -40,51 +40,51 @@ class InputOntologyV2Spec extends CoreSpec { "parse a property definition" in { val params = """ - |{ - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2017-12-19T15:23:42.166Z" - | }, - | "@graph" : [ { - | "@id" : "anything:hasName", - | "@type" : "owl:ObjectProperty", - | "knora-api:subjectType" : { - | "@id" : "anything:Thing" - | }, - | "knora-api:objectType" : { - | "@id" : "knora-api:TextValue" - | }, - | "rdfs:comment" : [ { - | "@language" : "en", - | "@value" : "The name of a 'Thing'" - | }, { - | "@language" : "de", - | "@value" : "Der Name eines Dinges" - | } ], - | "rdfs:label" : [ { - | "@language" : "en", - | "@value" : "has name" - | }, { - | "@language" : "de", - | "@value" : "hat Namen" - | } ], - | "rdfs:subPropertyOf" : [ { - | "@id" : "knora-api:hasValue" - | }, { - | "@id" : "http://schema.org/name" - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "owl" : "http://www.w3.org/2002/07/owl#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + |{ + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2017-12-19T15:23:42.166Z" + | }, + | "@graph" : [ { + | "@id" : "anything:hasName", + | "@type" : "owl:ObjectProperty", + | "knora-api:subjectType" : { + | "@id" : "anything:Thing" + | }, + | "knora-api:objectType" : { + | "@id" : "knora-api:TextValue" + | }, + | "rdfs:comment" : [ { + | "@language" : "en", + | "@value" : "The name of a 'Thing'" + | }, { + | "@language" : "de", + | "@value" : "Der Name eines Dinges" + | } ], + | "rdfs:label" : [ { + | "@language" : "en", + | "@value" : "has name" + | }, { + | "@language" : "de", + | "@value" : "hat Namen" + | } ], + | "rdfs:subPropertyOf" : [ { + | "@id" : "knora-api:hasValue" + | }, { + | "@id" : "http://schema.org/name" + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "owl" : "http://www.w3.org/2002/07/owl#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape @@ -94,43 +94,43 @@ class InputOntologyV2Spec extends CoreSpec { "parse a class definition" in { val params = s""" - |{ - | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2017-12-19T15:23:42.166Z" - | }, - | "@graph" : [ { - | "@id" : "anything:WildThing", - | "@type" : "owl:Class", - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "wild thing" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "A thing that is wild" - | }, - | "rdfs:subClassOf" : [ { - | "@id" : "anything:Thing" - | }, { - | "@type": "owl:Restriction", - | "owl:maxCardinality": 1, - | "owl:onProperty": { - | "@id" : "anything:hasName" - | } - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "owl" : "http://www.w3.org/2002/07/owl#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + |{ + | "@id" : "http://0.0.0.0:3333/ontology/0001/anything/v2", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2017-12-19T15:23:42.166Z" + | }, + | "@graph" : [ { + | "@id" : "anything:WildThing", + | "@type" : "owl:Class", + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "wild thing" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "A thing that is wild" + | }, + | "rdfs:subClassOf" : [ { + | "@id" : "anything:Thing" + | }, { + | "@type": "owl:Restriction", + | "owl:maxCardinality": 1, + | "owl:onProperty": { + | "@id" : "anything:hasName" + | } + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "owl" : "http://www.w3.org/2002/07/owl#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin val paramsAsInput: InputOntologyV2 = InputOntologyV2.fromJsonLD(JsonLDUtil.parseJsonLD(params)).unescape @@ -140,43 +140,43 @@ class InputOntologyV2Spec extends CoreSpec { "reject an entity definition in the wrong ontology" in { val params = s""" - |{ - | "@id" : "http://0.0.0.0:3333/ontology/0001/incunabula/v2", - | "@type" : "owl:Ontology", - | "knora-api:lastModificationDate" : { - | "@type" : "xsd:dateTimeStamp", - | "@value" : "2017-12-19T15:23:42.166Z" - | }, - | "@graph" : [ { - | "@id" : "anything:WildThing", - | "@type" : "owl:Class", - | "rdfs:label" : { - | "@language" : "en", - | "@value" : "wild thing" - | }, - | "rdfs:comment" : { - | "@language" : "en", - | "@value" : "A thing that is wild" - | }, - | "rdfs:subClassOf" : [ { - | "@id" : "anything:Thing" - | }, { - | "@type": "owl:Restriction", - | "owl:maxCardinality": 1, - | "owl:onProperty": { - | "@id" : "anything:hasName" - | } - | } ] - | } ], - | "@context" : { - | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - | "owl" : "http://www.w3.org/2002/07/owl#", - | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - | "xsd" : "http://www.w3.org/2001/XMLSchema#", - | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |} + |{ + | "@id" : "http://0.0.0.0:3333/ontology/0001/incunabula/v2", + | "@type" : "owl:Ontology", + | "knora-api:lastModificationDate" : { + | "@type" : "xsd:dateTimeStamp", + | "@value" : "2017-12-19T15:23:42.166Z" + | }, + | "@graph" : [ { + | "@id" : "anything:WildThing", + | "@type" : "owl:Class", + | "rdfs:label" : { + | "@language" : "en", + | "@value" : "wild thing" + | }, + | "rdfs:comment" : { + | "@language" : "en", + | "@value" : "A thing that is wild" + | }, + | "rdfs:subClassOf" : [ { + | "@id" : "anything:Thing" + | }, { + | "@type": "owl:Restriction", + | "owl:maxCardinality": 1, + | "owl:onProperty": { + | "@id" : "anything:hasName" + | } + | } ] + | } ], + | "@context" : { + | "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "owl" : "http://www.w3.org/2002/07/owl#", + | "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |} """.stripMargin assertThrows[BadRequestException] { @@ -201,15 +201,15 @@ object InputOntologyV2Spec { predicates = Map( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri -> PredicateInfoV2( predicateIri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".toSmartIri, - objects = Seq(SmartIriLiteralV2("http://www.w3.org/2002/07/owl#ObjectProperty".toSmartIri)), + objects = Seq(SmartIriLiteralV2("http://www.w3.org/2002/07/owl#ObjectProperty".toSmartIri)) ), "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#subjectType".toSmartIri, - objects = Seq(SmartIriLiteralV2("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri)), + objects = Seq(SmartIriLiteralV2("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri)) ), "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri -> PredicateInfoV2( predicateIri = "http://api.knora.org/ontology/knora-api/v2#objectType".toSmartIri, - objects = Seq(SmartIriLiteralV2("http://api.knora.org/ontology/knora-api/v2#TextValue".toSmartIri)), + objects = Seq(SmartIriLiteralV2("http://api.knora.org/ontology/knora-api/v2#TextValue".toSmartIri)) ), "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri -> PredicateInfoV2( predicateIri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri, @@ -231,7 +231,8 @@ object InputOntologyV2Spec { "http://schema.org/name".toSmartIri ), ontologySchema = ApiV2Complex - )) + ) + ) ) val ClassDef = InputOntologyV2( @@ -255,9 +256,12 @@ object InputOntologyV2Spec { ontologySchema = ApiV2Complex, directCardinalities = Map( "http://0.0.0.0:3333/ontology/0001/anything/v2#hasName".toSmartIri -> KnoraCardinalityInfo( - Cardinality.MayHaveOne)), + Cardinality.MayHaveOne + ) + ), subClassOf = Set("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri) - )), + ) + ), ontologyMetadata = OntologyMetadataV2( ontologyIri = "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri, lastModificationDate = Some(Instant.parse("2017-12-19T15:23:42.166Z")) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala index d8d0965bfb..f6b45543a1 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala @@ -6,8 +6,8 @@ import org.knora.webapi.messages.v2.responder.resourcemessages._ import org.knora.webapi.sharedtestdata._ /** - * Tests [[ResourceMessagesV2]]. - */ + * Tests [[ResourceMessagesV2]]. + */ class ResourcesMessagesV2Spec extends CoreSpec() { "Get history events of all resources of a project" should { "fail if given project IRI is not valid" in { diff --git a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala index e17f69b045..077db371a6 100644 --- a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala @@ -36,33 +36,47 @@ object DrawingsGodsV1E2ESpec { } /** - * End-to-End (E2E) test specification for additional testing of permissions. - */ + * End-to-End (E2E) test specification for additional testing of permissions. + */ class DrawingsGodsV1E2ESpec extends E2ESpec(DrawingsGodsV1E2ESpec.config) with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1E2ESpec/rvp-admin-data.ttl", - name = "http://www.knora.org/data/admin"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1E2ESpec/rvp-permissions-data.ttl", - name = "http://www.knora.org/data/permissions"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_admin-data.ttl", - name = "http://www.knora.org/data/admin"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_permissions-data.ttl", - name = "http://www.knora.org/data/permissions"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_ontology.ttl", - name = "http://www.knora.org/ontology/0105/drawings-gods"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_data.ttl", - name = "http://www.knora.org/data/0105/drawings-gods"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/parole-religieuse-dummy-onto.ttl", - name = "http://www.knora.org/ontology/0106/parole-religieuse") + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1E2ESpec/rvp-admin-data.ttl", + name = "http://www.knora.org/data/admin" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1E2ESpec/rvp-permissions-data.ttl", + name = "http://www.knora.org/data/permissions" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_admin-data.ttl", + name = "http://www.knora.org/data/admin" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_permissions-data.ttl", + name = "http://www.knora.org/data/permissions" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_ontology.ttl", + name = "http://www.knora.org/ontology/0105/drawings-gods" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_data.ttl", + name = "http://www.knora.org/data/0105/drawings-gods" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/parole-religieuse-dummy-onto.ttl", + name = "http://www.knora.org/ontology/0106/parole-religieuse" + ) ) /** - * 1a. parole-religieuse user creates a resource - * 1b. parole-religieuse user create a value - * 2a. drawings-gods user changes existing value - * 2b. drawings-gods user creates a new value (inside parole-religieuse project) - */ + * 1a. parole-religieuse user creates a resource + * 1b. parole-religieuse user create a value + * 2a. drawings-gods user changes existing value + * 2b. drawings-gods user creates a new value (inside parole-religieuse project) + */ "issue: https://github.com/dhlab-basel/Knora/issues/408" should { val drawingsOfGodsUserEmail = "ddd1@unil.ch" @@ -76,16 +90,18 @@ class DrawingsGodsV1E2ESpec extends E2ESpec(DrawingsGodsV1E2ESpec.config) with T val params = s""" - |{ - | "restype_id": "http://www.knora.org/ontology/0106/parole-religieuse#Thing", - | "label": "A thing", - | "project_id": "http://rdfh.ch/projects/0106", - | "properties": {} - |} + |{ + | "restype_id": "http://www.knora.org/ontology/0106/parole-religieuse#Thing", + | "label": "A thing", + | "project_id": "http://rdfh.ch/projects/0106", + | "properties": {} + |} """.stripMargin - val request = Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(paroleReligieuseUserEmail, testPass)) + val request = + Post(baseApiUrl + s"/v1/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(paroleReligieuseUserEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -98,15 +114,17 @@ class DrawingsGodsV1E2ESpec extends E2ESpec(DrawingsGodsV1E2ESpec.config) with T "allow the parole-religieuse user to add an integer value to a previously created resource (1b)" in { val params = s""" - |{ - | "res_id": "${thingIri.get}", - | "prop": "http://www.knora.org/ontology/0106/parole-religieuse#hasInteger", - | "int_value": 1234 - |} + |{ + | "res_id": "${thingIri.get}", + | "prop": "http://www.knora.org/ontology/0106/parole-religieuse#hasInteger", + | "int_value": 1234 + |} """.stripMargin - val request = Post(baseApiUrl + s"/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(paroleReligieuseUserEmail, testPass)) + val request = + Post(baseApiUrl + s"/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(paroleReligieuseUserEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -119,16 +137,17 @@ class DrawingsGodsV1E2ESpec extends E2ESpec(DrawingsGodsV1E2ESpec.config) with T "allow the drawings-gods user to change the existing value (2a)" in { val params = s""" - |{ - | "res_id": "${thingIri.get}", - | "prop": "http://www.knora.org/ontology/0106/parole-religieuse#hasInteger", - | "int_value": 1111 - |} + |{ + | "res_id": "${thingIri.get}", + | "prop": "http://www.knora.org/ontology/0106/parole-religieuse#hasInteger", + | "int_value": 1111 + |} """.stripMargin - val request = Put(baseApiUrl + s"/v1/values/${URLEncoder.encode(firstValueIri.get, "UTF-8")}", - HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(drawingsOfGodsUserEmail, testPass)) + val request = Put( + baseApiUrl + s"/v1/values/${URLEncoder.encode(firstValueIri.get, "UTF-8")}", + HttpEntity(ContentTypes.`application/json`, params) + ) ~> addCredentials(BasicHttpCredentials(drawingsOfGodsUserEmail, testPass)) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) @@ -141,15 +160,17 @@ class DrawingsGodsV1E2ESpec extends E2ESpec(DrawingsGodsV1E2ESpec.config) with T "allow the drawings-gods user to create a new value inside the parole-religieuse project (2b)" in { val params = s""" - |{ - | "res_id": "${thingIri.get}", - | "prop": "http://www.knora.org/ontology/0106/parole-religieuse#hasInteger", - | "int_value": 2222 - |} + |{ + | "res_id": "${thingIri.get}", + | "prop": "http://www.knora.org/ontology/0106/parole-religieuse#hasInteger", + | "int_value": 2222 + |} """.stripMargin - val request = Post(baseApiUrl + s"/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(drawingsOfGodsUserEmail, testPass)) + val request = + Post(baseApiUrl + s"/v1/values", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(drawingsOfGodsUserEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) diff --git a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala index 7bbcc6e7a4..ba1937ff9f 100644 --- a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala @@ -38,8 +38,8 @@ object DrawingsGodsV1Spec { } /** - * Test specification for testing a complex permissions structure of the drawings-gods-project. - */ + * Test specification for testing a complex permissions structure of the drawings-gods-project. + */ class DrawingsGodsV1Spec extends CoreSpec(DrawingsGodsV1Spec.config) with TriplestoreJsonProtocol { private val timeout = 5.seconds @@ -47,21 +47,29 @@ class DrawingsGodsV1Spec extends CoreSpec(DrawingsGodsV1Spec.config) with Triple implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_admin-data.ttl", - name = "http://www.knora.org/data/admin"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_permissions-data.ttl", - name = "http://www.knora.org/data/permissions"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_ontology.ttl", - name = "http://www.knora.org/ontology/0105/drawings-gods"), - RdfDataObject(path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_data.ttl", - name = "http://www.knora.org/data/0105/drawings-gods") + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_admin-data.ttl", + name = "http://www.knora.org/data/admin" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_permissions-data.ttl", + name = "http://www.knora.org/data/permissions" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_ontology.ttl", + name = "http://www.knora.org/ontology/0105/drawings-gods" + ), + RdfDataObject( + path = "test_data/other.v1.DrawingsGodsV1Spec/drawings-gods_data.ttl", + name = "http://www.knora.org/data/0105/drawings-gods" + ) ) /** - * issues: - * - https://github.com/dhlab-basel/Knora/issues/416 - * - https://github.com/dhlab-basel/Knora/issues/610 - */ + * issues: + * - https://github.com/dhlab-basel/Knora/issues/416 + * - https://github.com/dhlab-basel/Knora/issues/610 + */ "Using the DrawingsGods project data" should { val drawingsGodsProjectIri = "http://rdfh.ch/projects/0105" @@ -108,9 +116,13 @@ class DrawingsGodsV1Spec extends CoreSpec(DrawingsGodsV1Spec.config) with Triple drawingsGodsProjectIri, qualityDataResourceClass, targetUser = ddd2.get, - requestingUser = KnoraSystemInstances.Users.SystemUser) - expectMsg(DefaultObjectAccessPermissionsStringResponseADM( - "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team,knora-admin:Creator|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings,http://rdfh.ch/groups/0105/drawings-gods-meta-annotators")) + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team,knora-admin:Creator|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings,http://rdfh.ch/groups/0105/drawings-gods-meta-annotators" + ) + ) } "return correct drawings-gods:Person resource class permissions string for drawings-gods-test-ddd1 user" in { @@ -119,60 +131,78 @@ class DrawingsGodsV1Spec extends CoreSpec(DrawingsGodsV1Spec.config) with Triple drawingsGodsProjectIri, personResourceClass, targetUser = ddd1.get, - requestingUser = KnoraSystemInstances.Users.SystemUser) - expectMsg(DefaultObjectAccessPermissionsStringResponseADM( - "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team,knora-admin:Creator|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings,http://rdfh.ch/groups/0105/drawings-gods-meta-annotators|V knora-admin:KnownUser,knora-admin:ProjectMember,knora-admin:UnknownUser")) + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team,knora-admin:Creator|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings,http://rdfh.ch/groups/0105/drawings-gods-meta-annotators|V knora-admin:KnownUser,knora-admin:ProjectMember,knora-admin:UnknownUser" + ) + ) } "return correct drawings-gods:hasLastname property permissions string for drawings-gods-test-ddd1 user" in { val personResourceClass = s"$drawingsGodsOntologyIri#Person" val hasLastnameProperty = s"$drawingsGodsOntologyIri#hasLastname" - responderManager ! DefaultObjectAccessPermissionsStringForPropertyGetADM(drawingsGodsProjectIri, - personResourceClass, - hasLastnameProperty, - targetUser = ddd1.get, - requestingUser = - KnoraSystemInstances.Users.SystemUser) + responderManager ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + drawingsGodsProjectIri, + personResourceClass, + hasLastnameProperty, + targetUser = ddd1.get, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) expectMsg( DefaultObjectAccessPermissionsStringResponseADM( - "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team")) + "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team" + ) + ) } "return correct drawings-gods:DrawingPublic / knora-base:hasStillImageFileValue combination permissions string for drawings-gods-test-ddd1 user" in { val drawingPublicResourceClass = s"$drawingsGodsOntologyIri#DrawingPublic" val hasStillImageFileValue = OntologyConstants.KnoraBase.HasStillImageFileValue - responderManager ! DefaultObjectAccessPermissionsStringForPropertyGetADM(drawingsGodsProjectIri, - drawingPublicResourceClass, - hasStillImageFileValue, - targetUser = ddd1.get, - requestingUser = - KnoraSystemInstances.Users.SystemUser) - expectMsg(DefaultObjectAccessPermissionsStringResponseADM( - "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings|V http://rdfh.ch/groups/0105/drawings-gods-meta-annotators,knora-admin:KnownUser,knora-admin:ProjectMember,knora-admin:UnknownUser")) + responderManager ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + drawingsGodsProjectIri, + drawingPublicResourceClass, + hasStillImageFileValue, + targetUser = ddd1.get, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings|V http://rdfh.ch/groups/0105/drawings-gods-meta-annotators,knora-admin:KnownUser,knora-admin:ProjectMember,knora-admin:UnknownUser" + ) + ) } "return correct drawings-gods:DrawingPrivate / knora-base:hasStillImageFileValue combination permissions string for drawings-gods-test-ddd1 user" in { val drawingPrivateResourceClass = s"$drawingsGodsOntologyIri#DrawingPrivate" val hasStillImageFileValue = OntologyConstants.KnoraBase.HasStillImageFileValue - responderManager ! DefaultObjectAccessPermissionsStringForPropertyGetADM(drawingsGodsProjectIri, - drawingPrivateResourceClass, - hasStillImageFileValue, - targetUser = ddd1.get, - requestingUser = - KnoraSystemInstances.Users.SystemUser) - expectMsg(DefaultObjectAccessPermissionsStringResponseADM( - "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings,http://rdfh.ch/groups/0105/drawings-gods-meta-annotators|V knora-admin:ProjectMember")) + responderManager ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + drawingsGodsProjectIri, + drawingPrivateResourceClass, + hasStillImageFileValue, + targetUser = ddd1.get, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR http://rdfh.ch/groups/0105/drawings-gods-admin|D http://rdfh.ch/groups/0105/drawings-gods-snf-team|M http://rdfh.ch/groups/0105/drawings-gods-add-drawings,http://rdfh.ch/groups/0105/drawings-gods-meta-annotators|V knora-admin:ProjectMember" + ) + ) } "allow drawings-gods-test-ddd1 user to create a resource, then query it and see its label and properties" in { val valuesToBeCreated = Map( s"$drawingsGodsOntologyIri#hasLastname" -> Vector( - CreateValueV1WithComment(TextValueSimpleV1("PersonTest DDD1"))), + CreateValueV1WithComment(TextValueSimpleV1("PersonTest DDD1")) + ), s"$drawingsGodsOntologyIri#hasCodePerson" -> Vector(CreateValueV1WithComment(TextValueSimpleV1("Code"))), s"$drawingsGodsOntologyIri#hasPersonGender" -> Vector( CreateValueV1WithComment( - HierarchicalListValueV1("http://rdfh.ch/lists/0105/drawings-gods-2016-list-FiguresHList-polysexual"))), + HierarchicalListValueV1("http://rdfh.ch/lists/0105/drawings-gods-2016-list-FiguresHList-polysexual") + ) + ), s"$drawingsGodsOntologyIri#hasDrawingChildTotal" -> Vector(CreateValueV1WithComment(IntegerValueV1(99))) ) @@ -212,11 +242,14 @@ class DrawingsGodsV1Spec extends CoreSpec(DrawingsGodsV1Spec.config) with Triple val valuesToBeCreated = Map( s"$drawingsGodsOntologyIri#hasLastname" -> Vector( - CreateValueV1WithComment(TextValueSimpleV1("PersonTest DDD1"))), + CreateValueV1WithComment(TextValueSimpleV1("PersonTest DDD1")) + ), s"$drawingsGodsOntologyIri#hasCodePerson" -> Vector(CreateValueV1WithComment(TextValueSimpleV1("Code"))), s"$drawingsGodsOntologyIri#hasPersonGender" -> Vector( CreateValueV1WithComment( - HierarchicalListValueV1("http://rdfh.ch/lists/0105/drawings-gods-2016-list-FiguresHList-polysexual"))), + HierarchicalListValueV1("http://rdfh.ch/lists/0105/drawings-gods-2016-list-FiguresHList-polysexual") + ) + ), s"$drawingsGodsOntologyIri#hasDrawingChildTotal" -> Vector(CreateValueV1WithComment(IntegerValueV1(99))) ) diff --git a/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala index 4ee79ee512..335c7a6977 100644 --- a/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala @@ -33,19 +33,27 @@ object LumieresLausanneV2E2ESpec { } /** - * End-to-End (E2E) test specification for additional testing of permissions. - */ + * End-to-End (E2E) test specification for additional testing of permissions. + */ class LumieresLausanneV2E2ESpec extends E2ESpec(LumieresLausanneV2E2ESpec.config) with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne_admin.ttl", - name = "http://www.knora.org/data/admin"), - RdfDataObject(path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne_permissions.ttl", - name = "http://www.knora.org/data/permissions"), - RdfDataObject(path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne-onto.ttl", - name = "http://www.knora.org/ontology/0113/lumieres-lausanne"), - RdfDataObject(path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne-data-lists.ttl", - name = "http://www.knora.org/data/0113/lumieres-lausanne") + RdfDataObject( + path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne_admin.ttl", + name = "http://www.knora.org/data/admin" + ), + RdfDataObject( + path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne_permissions.ttl", + name = "http://www.knora.org/data/permissions" + ), + RdfDataObject( + path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne-onto.ttl", + name = "http://www.knora.org/ontology/0113/lumieres-lausanne" + ), + RdfDataObject( + path = "test_data/other.v2.LumieresLausanneV2E2ESpec/lumieres-lausanne-data-lists.ttl", + name = "http://www.knora.org/data/0113/lumieres-lausanne" + ) ) "For project Lumieres Lausanne" should { @@ -57,31 +65,33 @@ class LumieresLausanneV2E2ESpec extends E2ESpec(LumieresLausanneV2E2ESpec.config val params = s""" - |{ - | "@type": "onto:User", - | "rdfs:label": "Test", - | "knora-api:attachedToProject": { - | "@id": "http://rdfh.ch/projects/0113" - | }, - | "onto:isKnoraUser": { - | "@type": "knora-api:UriValue", - | "knora-api:uriValueAsUri": { - | "@type": "xsd:anyURI", - | "@value": "http://rdfh.ch/users/lumieres-lausanne-gfaucherand" - | } - | }, - | "@context": { - | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - | "xsd": "http://www.w3.org/2001/XMLSchema#", - | "knora-api": "http://api.knora.org/ontology/knora-api/v2#", - | "onto": "http://0.0.0.0:3333/ontology/0113/lumieres-lausanne/v2#" - | } - |} + |{ + | "@type": "onto:User", + | "rdfs:label": "Test", + | "knora-api:attachedToProject": { + | "@id": "http://rdfh.ch/projects/0113" + | }, + | "onto:isKnoraUser": { + | "@type": "knora-api:UriValue", + | "knora-api:uriValueAsUri": { + | "@type": "xsd:anyURI", + | "@value": "http://rdfh.ch/users/lumieres-lausanne-gfaucherand" + | } + | }, + | "@context": { + | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + | "xsd": "http://www.w3.org/2001/XMLSchema#", + | "knora-api": "http://api.knora.org/ontology/knora-api/v2#", + | "onto": "http://0.0.0.0:3333/ontology/0113/lumieres-lausanne/v2#" + | } + |} """.stripMargin - val request = Post(baseApiUrl + s"/v2/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( - BasicHttpCredentials(gfUserEmail, testPass)) + val request = + Post(baseApiUrl + s"/v2/resources", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials( + BasicHttpCredentials(gfUserEmail, testPass) + ) val response: HttpResponse = singleAwaitingRequest(request) assert(response.status === StatusCodes.OK) } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/IriLockerSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/IriLockerSpec.scala index c9fb756594..91ce8bc424 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/IriLockerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/IriLockerSpec.scala @@ -11,8 +11,8 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} /** - * Tests [[IriLocker]]. - */ + * Tests [[IriLocker]]. + */ class IriLockerSpec extends AnyWordSpecLike with Matchers { import scala.concurrent.ExecutionContext.Implicits.global @@ -50,18 +50,19 @@ class IriLockerSpec extends AnyWordSpecLike with Matchers { task = () => runShortTask() ) - val secondTaskFailedWithLockTimeout = try { - Await.result(secondTaskResultFuture, 20.seconds) - false - } catch { - case ale: ApplicationLockException => true - } + val secondTaskFailedWithLockTimeout = + try { + Await.result(secondTaskResultFuture, 20.seconds) + false + } catch { + case ale: ApplicationLockException => true + } assert(secondTaskFailedWithLockTimeout, "Second task did not get a lock timeout") } "provide reentrant locks" in { - def runRecursiveTask(iri: IRI, apiRequestID: UUID, count: Int): Future[String] = { + def runRecursiveTask(iri: IRI, apiRequestID: UUID, count: Int): Future[String] = if (count > 0) { IriLocker.runWithIriLock( apiRequestID = apiRequestID, @@ -71,7 +72,6 @@ class IriLockerSpec extends AnyWordSpecLike with Matchers { } else { Future(SUCCESS) } - } val testIri: IRI = "http://example.org/test2" @@ -103,12 +103,13 @@ class IriLockerSpec extends AnyWordSpecLike with Matchers { task = () => runTask(false) ) - val firstTaskFailed = try { - Await.result(firstTaskResultFuture, 1.second) - false - } catch { - case e: Exception => true - } + val firstTaskFailed = + try { + Await.result(firstTaskResultFuture, 1.second) + false + } catch { + case e: Exception => true + } assert(firstTaskFailed, "First task did not fail") @@ -127,13 +128,12 @@ class IriLockerSpec extends AnyWordSpecLike with Matchers { "release a lock when a task throws an exception instead of returning a future" in { // If succeed is true, returns a successful future, otherwise throws an exception. - def runTask(succeed: Boolean): Future[String] = { + def runTask(succeed: Boolean): Future[String] = if (succeed) { Future(SUCCESS) } else { throw new Exception(FAILURE) } - } val testIri: IRI = "http://example.org/test4" @@ -145,12 +145,13 @@ class IriLockerSpec extends AnyWordSpecLike with Matchers { task = () => runTask(false) ) - val firstTaskFailed = try { - Await.result(firstTaskResultFuture, 1.second) - false - } catch { - case e: Exception => true - } + val firstTaskFailed = + try { + Await.result(firstTaskResultFuture, 1.second) + false + } catch { + case e: Exception => true + } assert(firstTaskFailed, "First task did not fail") diff --git a/webapi/src/test/scala/org/knora/webapi/responders/MockableResponderManager.scala b/webapi/src/test/scala/org/knora/webapi/responders/MockableResponderManager.scala index 1e11a86bf0..f127884e06 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/MockableResponderManager.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/MockableResponderManager.scala @@ -24,17 +24,18 @@ import org.knora.webapi.core.{ActorMaker, LiveActorMaker} import org.knora.webapi.messages.util.ResponderData /** - * A subclass of [[ResponderManager]] that allows tests to substitute custom responders for the standard ones. - * - * @param mockRespondersOrStoreConnectors a [[Map]] containing the mock responders to be used instead of the live ones. - * The name of the actor (a constant from [[org.knora.webapi.responders]] is - * used as the key in the map. - * @param appActor the main application actor. - */ -class MockableResponderManager(mockRespondersOrStoreConnectors: Map[String, ActorRef], - appActor: ActorRef, - responderData: ResponderData) - extends ResponderManager(appActor, responderData) + * A subclass of [[ResponderManager]] that allows tests to substitute custom responders for the standard ones. + * + * @param mockRespondersOrStoreConnectors a [[Map]] containing the mock responders to be used instead of the live ones. + * The name of the actor (a constant from [[org.knora.webapi.responders]] is + * used as the key in the map. + * @param appActor the main application actor. + */ +class MockableResponderManager( + mockRespondersOrStoreConnectors: Map[String, ActorRef], + appActor: ActorRef, + responderData: ResponderData +) extends ResponderManager(appActor, responderData) with LiveActorMaker { this: ActorMaker => diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala index 0e628b6eb5..4b10da6a5f 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala @@ -18,9 +18,9 @@ */ /** - * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder - * extend ResponderADM which messes up testing, as we cannot inject the TestActor system. - */ + * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder + * extend ResponderADM which messes up testing, as we cannot inject the TestActor system. + */ package org.knora.webapi.responders.admin import java.util.UUID @@ -46,8 +46,8 @@ object GroupsResponderADMSpec { } /** - * This spec is used to test the messages received by the [[org.knora.webapi.responders.admin.UsersResponderADM]] actor. - */ + * This spec is used to test the messages received by the [[org.knora.webapi.responders.admin.UsersResponderADM]] actor. + */ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) with ImplicitSender { private val timeout = 5.seconds @@ -90,8 +90,8 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit requestingUser = rootUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } } @@ -129,18 +129,20 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit "return a 'DuplicateValueException' if the supplied group name is not unique" in { responderManager ! GroupCreateRequestADM( - createRequest = CreateGroupApiRequestADM(name = "NewGroup", - description = Some("NewGroupDescription"), - project = SharedTestDataADM.IMAGES_PROJECT_IRI, - status = true, - selfjoin = false), + createRequest = CreateGroupApiRequestADM( + name = "NewGroup", + description = Some("NewGroupDescription"), + project = SharedTestDataADM.IMAGES_PROJECT_IRI, + status = true, + selfjoin = false + ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -148,11 +150,13 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit /* missing group name */ responderManager ! GroupCreateRequestADM( - createRequest = CreateGroupApiRequestADM(name = "", - description = Some("NoNameGroupDescription"), - project = SharedTestDataADM.IMAGES_PROJECT_IRI, - status = true, - selfjoin = false), + createRequest = CreateGroupApiRequestADM( + name = "", + description = Some("NoNameGroupDescription"), + project = SharedTestDataADM.IMAGES_PROJECT_IRI, + status = true, + selfjoin = false + ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, apiRequestID = UUID.randomUUID @@ -161,11 +165,13 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit /* missing project */ responderManager ! GroupCreateRequestADM( - createRequest = CreateGroupApiRequestADM(name = "OtherNewGroup", - description = Some("OtherNewGroupDescription"), - project = "", - status = true, - selfjoin = false), + createRequest = CreateGroupApiRequestADM( + name = "OtherNewGroup", + description = Some("OtherNewGroupDescription"), + project = "", + status = true, + selfjoin = false + ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, apiRequestID = UUID.randomUUID @@ -176,8 +182,10 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit "UPDATE a group" in { responderManager ! GroupChangeRequestADM( groupIri = newGroupIri.get, - changeGroupRequest = ChangeGroupApiRequestADM(Some("UpdatedGroupName"), - Some("""UpdatedDescription with "quotes" and """)), + changeGroupRequest = ChangeGroupApiRequestADM( + Some("UpdatedGroupName"), + Some("""UpdatedDescription with "quotes" and """) + ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, apiRequestID = UUID.randomUUID @@ -202,8 +210,8 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -216,8 +224,8 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -282,8 +290,8 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit requestingUser = SharedTestDataADM.rootUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala index 19d3a340d8..2e447cb3da 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala @@ -36,8 +36,8 @@ import org.knora.webapi.util.MutableTestIri import scala.concurrent.duration._ /** - * Static data for testing [[ListsResponderADM]]. - */ + * Static data for testing [[ListsResponderADM]]. + */ object ListsResponderADMSpec { val config: Config = ConfigFactory.parseString(""" akka.loglevel = "DEBUG" @@ -46,8 +46,8 @@ object ListsResponderADMSpec { } /** - * Tests [[ListsResponderADM]]. - */ + * Tests [[ListsResponderADM]]. + */ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with ImplicitSender { // The default timeout for receiving reply messages from actors. @@ -217,7 +217,7 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with projectIri = IMAGES_PROJECT_IRI, name = Some(nameWithSpecialCharacter), labels = Seq(StringLiteralV2(value = labelWithSpecialCharacter, language = Some("de"))), - comments = Seq(StringLiteralV2(value = commentWithSpecialCharacter, language = Some("de"))), + comments = Seq(StringLiteralV2(value = commentWithSpecialCharacter, language = Some("de"))) ).escape, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, @@ -257,12 +257,14 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with Seq( StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")), StringLiteralV2(value = "Changed List", language = Some("en")) - )), + ) + ), comments = Some( Seq( StringLiteralV2(value = "Neuer Kommentar", language = Some("de")), StringLiteralV2(value = "New Comment", language = Some("en")) - )) + ) + ) ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, @@ -281,7 +283,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with Seq( StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")), StringLiteralV2(value = "Changed List", language = Some("en")) - ).sorted) + ).sorted + ) val comments = listInfo.comments.stringLiterals comments.size should be(2) @@ -289,22 +292,29 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with Seq( StringLiteralV2(value = "Neuer Kommentar", language = Some("de")), StringLiteralV2(value = "New Comment", language = Some("en")) - ).sorted) + ).sorted + ) } "not update basic list information if name is duplicate" in { responderManager ! NodeInfoChangeRequestADM( listIri = newListIri.get, - changeNodeRequest = ChangeNodeInfoApiRequestADM(listIri = newListIri.get, - projectIri = IMAGES_PROJECT_IRI, - name = Some("sommer")), + changeNodeRequest = ChangeNodeInfoApiRequestADM( + listIri = newListIri.get, + projectIri = IMAGES_PROJECT_IRI, + name = Some("sommer") + ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, apiRequestID = UUID.randomUUID ) expectMsg( - Failure(DuplicateValueException( - "The name sommer is already used by a list inside the project http://rdfh.ch/projects/00FF."))) + Failure( + DuplicateValueException( + "The name sommer is already used by a list inside the project http://rdfh.ch/projects/00FF." + ) + ) + ) } "add child to list - to the root node" in { @@ -339,7 +349,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with val comments = childNodeInfo.comments.stringLiterals comments.size should be(1) comments.sorted should be( - Seq(StringLiteralV2(value = "New First Child List Node Comment", language = Some("en")))) + Seq(StringLiteralV2(value = "New First Child List Node Comment", language = Some("en"))) + ) // check position val position = childNodeInfo.position @@ -385,7 +396,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with val comments = childNodeInfo.comments.stringLiterals comments.size should be(1) comments.sorted should be( - Seq(StringLiteralV2(value = "New Second Child List Node Comment", language = Some("en")))) + Seq(StringLiteralV2(value = "New Second Child List Node Comment", language = Some("en"))) + ) // check position val position = childNodeInfo.position @@ -430,7 +442,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with val comments = childNodeInfo.comments.stringLiterals comments.size should be(1) comments.sorted should be( - Seq(StringLiteralV2(value = "New Third Child List Node Comment", language = Some("en")))) + Seq(StringLiteralV2(value = "New Third Child List Node Comment", language = Some("en"))) + ) // check position val position = childNodeInfo.position @@ -459,7 +472,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with apiRequestID = UUID.randomUUID ) expectMsg( - Failure(BadRequestException(s"Invalid position given ${givenPosition}, maximum allowed position is = 2."))) + Failure(BadRequestException(s"Invalid position given ${givenPosition}, maximum allowed position is = 2.")) + ) } } @@ -603,7 +617,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with // last node of new parent must be shifted one place to right val isShifted = childrenOfNewParent.exists(child => - child.id == "http://rdfh.ch/lists/0001/notUsedList03" && child.position == 3) + child.id == "http://rdfh.ch/lists/0001/notUsedList03" && child.position == 3 + ) isShifted should be(true) /* check old parent node */ @@ -653,7 +668,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with // last node of new parent must have remained in its current position val isShifted = childrenOfNewParent.exists(child => - child.id == "http://rdfh.ch/lists/0001/notUsedList03" && child.position == 3) + child.id == "http://rdfh.ch/lists/0001/notUsedList03" && child.position == 3 + ) isShifted should be(true) /* check old parent node */ @@ -824,7 +840,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with ) val usedChild = "http://rdfh.ch/lists/0001/treeList10" expectMsg( - Failure(BadRequestException(s"Node ${nodeIri} cannot be deleted, because its child ${usedChild} is in use."))) + Failure(BadRequestException(s"Node ${nodeIri} cannot be deleted, because its child ${usedChild} is in use.")) + ) } @@ -837,7 +854,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with apiRequestID = UUID.randomUUID ) expectMsg( - Failure(BadRequestException(s"Node ${nodeInUseInOntologyIri} cannot be deleted, because it is in use."))) + Failure(BadRequestException(s"Node ${nodeInUseInOntologyIri} cannot be deleted, because it is in use.")) + ) } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala index 2049ba375b..7cc52ad120 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala @@ -49,8 +49,8 @@ object PermissionsResponderADMSpec { } /** - * This spec is used to test the [[PermissionsResponderADM]] actor. - */ + * This spec is used to test the [[PermissionsResponderADM]] actor. + */ class PermissionsResponderADMSpec extends CoreSpec(PermissionsResponderADMSpec.config) with ImplicitSender @@ -69,8 +69,10 @@ class PermissionsResponderADMSpec PrivateMethod[Future[Set[PermissionADM]]](Symbol("defaultObjectAccessPermissionsForGroupsGetADM")) override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/responders.admin.PermissionsResponderV1Spec/additional_permissions-data.ttl", - name = "http://www.knora.org/data/permissions"), + RdfDataObject( + path = "test_data/responders.admin.PermissionsResponderV1Spec/additional_permissions-data.ttl", + name = "http://www.knora.org/data/permissions" + ), RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") ) @@ -177,9 +179,10 @@ class PermissionsResponderADMSpec } "ask for userAdministrativePermissionsGetADM" should { "return user's administrative permissions (helper method used in queries before)" in { - val f - : Future[Map[IRI, Set[PermissionADM]]] = responderUnderTest invokePrivate userAdministrativePermissionsGetADM( - multiuserUser.permissions.groupsPerProject) + val f: Future[Map[IRI, Set[PermissionADM]]] = + responderUnderTest invokePrivate userAdministrativePermissionsGetADM( + multiuserUser.permissions.groupsPerProject + ) val result: Map[IRI, Set[PermissionADM]] = Await.result(f, 1.seconds) result should equal(multiuserUser.permissions.administrativePermissionsPerProject) } @@ -196,7 +199,8 @@ class PermissionsResponderADMSpec expectMsg( AdministrativePermissionsForProjectGetResponseADM( Seq(perm002_a2.p, perm002_a3.p, perm002_a1.p) - )) + ) + ) } "return AdministrativePermission for project and group" in { @@ -224,8 +228,12 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(ForbiddenException( - s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) } } @@ -241,11 +249,16 @@ class PermissionsResponderADMSpec requestingUser = rootUser, apiRequestID = UUID.randomUUID() ) - expectMsg(Failure(DuplicateValueException( - s"An administrative permission for project: '${SharedTestDataADM.IMAGES_PROJECT_IRI}' and group: '${OntologyConstants.KnoraAdmin.ProjectMember}' combination already exists. " + - s"This permission currently has the scope '${PermissionUtilADM - .formatPermissionADMs(perm002_a1.p.hasPermissions, PermissionType.AP)}'. " + - s"Use its IRI ${perm002_a1.iri} to modify it, if necessary."))) + expectMsg( + Failure( + DuplicateValueException( + s"An administrative permission for project: '${SharedTestDataADM.IMAGES_PROJECT_IRI}' and group: '${OntologyConstants.KnoraAdmin.ProjectMember}' combination already exists. " + + s"This permission currently has the scope '${PermissionUtilADM + .formatPermissionADMs(perm002_a1.p.hasPermissions, PermissionType.AP)}'. " + + s"Use its IRI ${perm002_a1.iri} to modify it, if necessary." + ) + ) + ) } "create and return an administrative permission with a custom IRI" in { @@ -275,13 +288,15 @@ class PermissionsResponderADMSpec name = OntologyConstants.KnoraAdmin.ProjectResourceCreateAllPermission, additionalInformation = Some("blabla"), permissionCode = Some(8) - )) + ) + ) val expectedHasPermissions = Set( PermissionADM( name = OntologyConstants.KnoraAdmin.ProjectResourceCreateAllPermission, additionalInformation = None, permissionCode = None - )) + ) + ) responderManager ! AdministrativePermissionCreateRequestADM( createRequest = CreateAdministrativePermissionAPIRequestADM( id = Some(customIri), @@ -318,7 +333,8 @@ class PermissionsResponderADMSpec requestingUser = SharedTestDataADM.incunabulaMemberUser ) expectMsg( - Failure(ForbiddenException("Object access permissions can only be queried by system and project admin."))) + Failure(ForbiddenException("Object access permissions can only be queried by system and project admin.")) + ) } "return object access permissions for a value" in { @@ -336,7 +352,8 @@ class PermissionsResponderADMSpec requestingUser = SharedTestDataADM.incunabulaMemberUser ) expectMsg( - Failure(ForbiddenException("Object access permissions can only be queried by system and project admin."))) + Failure(ForbiddenException("Object access permissions can only be queried by system and project admin.")) + ) } } @@ -352,7 +369,8 @@ class PermissionsResponderADMSpec expectMsg( DefaultObjectAccessPermissionsForProjectGetResponseADM( defaultObjectAccessPermissions = Seq(perm002_d2.p, perm002_d1.p, perm0003_a4.p) - )) + ) + ) } "return DefaultObjectAccessPermission for IRI" in { @@ -364,7 +382,8 @@ class PermissionsResponderADMSpec expectMsg( DefaultObjectAccessPermissionGetResponseADM( defaultObjectAccessPermission = perm002_d1.p - )) + ) + ) } "return 'ForbiddenException' if the user requesting DefaultObjectAccessPermissionForIriGetRequestADM is not System or project Admin" in { @@ -376,8 +395,12 @@ class PermissionsResponderADMSpec ) expectMsg( - Failure(ForbiddenException( - s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) } "return DefaultObjectAccessPermission for project and group" in { @@ -391,7 +414,8 @@ class PermissionsResponderADMSpec expectMsg( DefaultObjectAccessPermissionGetResponseADM( defaultObjectAccessPermission = perm003_d1.p - )) + ) + ) } "return DefaultObjectAccessPermission for project and resource class ('incunabula:Page')" in { @@ -405,7 +429,8 @@ class PermissionsResponderADMSpec expectMsg( DefaultObjectAccessPermissionGetResponseADM( defaultObjectAccessPermission = perm003_d2.p - )) + ) + ) } "return DefaultObjectAccessPermission for project and property ('knora-base:hasStillImageFileValue') (system property)" in { @@ -419,7 +444,8 @@ class PermissionsResponderADMSpec expectMsg( DefaultObjectAccessPermissionGetResponseADM( defaultObjectAccessPermission = perm001_d3.p - )) + ) + ) } "cache DefaultObjectAccessPermission" in { @@ -433,7 +459,8 @@ class PermissionsResponderADMSpec expectMsg( DefaultObjectAccessPermissionGetResponseADM( defaultObjectAccessPermission = perm001_d3.p - )) + ) + ) val key = perm001_d3.p.cacheKey val maybePermission = @@ -461,7 +488,8 @@ class PermissionsResponderADMSpec assert(received.defaultObjectAccessPermission.forGroup.contains(SharedTestDataADM.thingSearcherGroup.id)) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.restrictedViewPermission(SharedTestDataADM.thingSearcherGroup.id))) + .contains(PermissionADM.restrictedViewPermission(SharedTestDataADM.thingSearcherGroup.id)) + ) } "create a DefaultObjectAccessPermission for project and group with custom IRI" in { @@ -484,7 +512,8 @@ class PermissionsResponderADMSpec assert(received.defaultObjectAccessPermission.forGroup.contains(OntologyConstants.KnoraAdmin.UnknownUser)) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser))) + .contains(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser)) + ) } "create a DefaultObjectAccessPermission for project and resource class" in { @@ -503,10 +532,12 @@ class PermissionsResponderADMSpec assert(received.defaultObjectAccessPermission.forProject == SharedTestDataADM.IMAGES_PROJECT_IRI) assert( received.defaultObjectAccessPermission.forResourceClass - .contains(SharedOntologyTestDataADM.IMAGES_BILD_RESOURCE_CLASS)) + .contains(SharedOntologyTestDataADM.IMAGES_BILD_RESOURCE_CLASS) + ) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.KnownUser))) + .contains(PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.KnownUser)) + ) } @@ -525,10 +556,12 @@ class PermissionsResponderADMSpec expectMsgType[DefaultObjectAccessPermissionCreateResponseADM] assert(received.defaultObjectAccessPermission.forProject == SharedTestDataADM.IMAGES_PROJECT_IRI) assert( - received.defaultObjectAccessPermission.forProperty.contains(SharedOntologyTestDataADM.IMAGES_TITEL_PROPERTY)) + received.defaultObjectAccessPermission.forProperty.contains(SharedOntologyTestDataADM.IMAGES_TITEL_PROPERTY) + ) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator))) + .contains(PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator)) + ) } "fail and return a 'DuplicateValueException' when a doap permission for project and group combination already exists" in { @@ -542,12 +575,17 @@ class PermissionsResponderADMSpec requestingUser = rootUser, apiRequestID = UUID.randomUUID() ) - expectMsg(Failure(DuplicateValueException( - s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and group: '${OntologyConstants.KnoraAdmin.ProjectMember}' " + - "combination already exists. " + - s"This permission currently has the scope '${PermissionUtilADM - .formatPermissionADMs(perm003_d1.p.hasPermissions, PermissionType.OAP)}'. " + - s"Use its IRI ${perm003_d1.iri} to modify it, if necessary."))) + expectMsg( + Failure( + DuplicateValueException( + s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and group: '${OntologyConstants.KnoraAdmin.ProjectMember}' " + + "combination already exists. " + + s"This permission currently has the scope '${PermissionUtilADM + .formatPermissionADMs(perm003_d1.p.hasPermissions, PermissionType.OAP)}'. " + + s"Use its IRI ${perm003_d1.iri} to modify it, if necessary." + ) + ) + ) } "fail and return a 'DuplicateValueException' when a doap permission for project and resourceClass combination already exists" in { @@ -564,12 +602,17 @@ class PermissionsResponderADMSpec requestingUser = rootUser, apiRequestID = UUID.randomUUID() ) - expectMsg(Failure(DuplicateValueException( - s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and resourceClass: '${SharedOntologyTestDataADM.INCUNABULA_BOOK_RESOURCE_CLASS}' " + - "combination already exists. " + - s"This permission currently has the scope '${PermissionUtilADM - .formatPermissionADMs(perm003_d2.p.hasPermissions, PermissionType.OAP)}'. " + - s"Use its IRI ${perm003_d2.iri} to modify it, if necessary."))) + expectMsg( + Failure( + DuplicateValueException( + s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and resourceClass: '${SharedOntologyTestDataADM.INCUNABULA_BOOK_RESOURCE_CLASS}' " + + "combination already exists. " + + s"This permission currently has the scope '${PermissionUtilADM + .formatPermissionADMs(perm003_d2.p.hasPermissions, PermissionType.OAP)}'. " + + s"Use its IRI ${perm003_d2.iri} to modify it, if necessary." + ) + ) + ) } "fail and return a 'DuplicateValueException' when a doap permission for project and property combination already exists" in { @@ -585,12 +628,17 @@ class PermissionsResponderADMSpec requestingUser = rootUser, apiRequestID = UUID.randomUUID() ) - expectMsg(Failure(DuplicateValueException( - s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and property: '${SharedOntologyTestDataADM.INCUNABULA_PartOf_Property}' " + - "combination already exists. " + - s"This permission currently has the scope '${PermissionUtilADM - .formatPermissionADMs(perm003_d4.p.hasPermissions, PermissionType.OAP)}'. " + - s"Use its IRI ${perm003_d4.iri} to modify it, if necessary."))) + expectMsg( + Failure( + DuplicateValueException( + s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and property: '${SharedOntologyTestDataADM.INCUNABULA_PartOf_Property}' " + + "combination already exists. " + + s"This permission currently has the scope '${PermissionUtilADM + .formatPermissionADMs(perm003_d4.p.hasPermissions, PermissionType.OAP)}'. " + + s"Use its IRI ${perm003_d4.iri} to modify it, if necessary." + ) + ) + ) } "fail and return a 'DuplicateValueException' when a doap permission for project, resource class, and property combination already exists" in { @@ -609,13 +657,17 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(DuplicateValueException( - s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and resourceClass: '${SharedOntologyTestDataADM.INCUNABULA_PAGE_RESOURCE_CLASS}' " + - s"and property: '${SharedOntologyTestDataADM.INCUNABULA_PartOf_Property}' " + - "combination already exists. " + - s"This permission currently has the scope '${PermissionUtilADM - .formatPermissionADMs(perm003_d5.p.hasPermissions, PermissionType.OAP)}'. " + - s"Use its IRI ${perm003_d5.iri} to modify it, if necessary."))) + Failure( + DuplicateValueException( + s"A default object access permission for project: '${SharedTestDataV1.INCUNABULA_PROJECT_IRI}' and resourceClass: '${SharedOntologyTestDataADM.INCUNABULA_PAGE_RESOURCE_CLASS}' " + + s"and property: '${SharedOntologyTestDataADM.INCUNABULA_PartOf_Property}' " + + "combination already exists. " + + s"This permission currently has the scope '${PermissionUtilADM + .formatPermissionADMs(perm003_d5.p.hasPermissions, PermissionType.OAP)}'. " + + s"Use its IRI ${perm003_d5.iri} to modify it, if necessary." + ) + ) + ) } "create a DefaultObjectAccessPermission for project and property even if name of a permission was missing" in { @@ -624,7 +676,8 @@ class PermissionsResponderADMSpec name = "", additionalInformation = Some(OntologyConstants.KnoraAdmin.UnknownUser), permissionCode = Some(1) - )) + ) + ) responderManager ! DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( forProject = SharedTestDataADM.IMAGES_PROJECT_IRI, @@ -641,7 +694,8 @@ class PermissionsResponderADMSpec assert(received.defaultObjectAccessPermission.forGroup == Some(OntologyConstants.KnoraAdmin.UnknownUser)) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser))) + .contains(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser)) + ) } "create a DefaultObjectAccessPermission for project and property even if permissionCode of a permission was missing" in { @@ -650,13 +704,15 @@ class PermissionsResponderADMSpec name = OntologyConstants.KnoraBase.DeletePermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = None - )) + ) + ) val expectedPermissions = Set( PermissionADM( name = OntologyConstants.KnoraBase.DeletePermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = Some(7) - )) + ) + ) responderManager ! DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( forProject = SharedTestDataADM.IMAGES_PROJECT_IRI, @@ -1015,7 +1071,8 @@ class PermissionsResponderADMSpec name = OntologyConstants.KnoraAdmin.ProjectAdminAllPermission, additionalInformation = Some("aIRI"), permissionCode = Some(1) - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, changePermissionHasPermissionsRequest = ChangePermissionHasPermissionsApiRequestADM( @@ -1062,14 +1119,16 @@ class PermissionsResponderADMSpec name = "", additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), permissionCode = Some(8) - )) + ) + ) val expectedHasPermissions = Set( PermissionADM( name = OntologyConstants.KnoraBase.ChangeRightsPermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), permissionCode = Some(8) - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, @@ -1093,14 +1152,16 @@ class PermissionsResponderADMSpec name = OntologyConstants.KnoraBase.DeletePermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = None - )) + ) + ) val expectedHasPermissions = Set( PermissionADM( name = OntologyConstants.KnoraBase.DeletePermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = Some(7) - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, @@ -1126,7 +1187,8 @@ class PermissionsResponderADMSpec name = name, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = Some(code) - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, @@ -1137,7 +1199,8 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(BadRequestException(s"Given permission code $code and permission name $name are not consistent."))) + Failure(BadRequestException(s"Given permission code $code and permission name $name are not consistent.")) + ) } @@ -1149,7 +1212,8 @@ class PermissionsResponderADMSpec name = name, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = None - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, @@ -1161,8 +1225,12 @@ class PermissionsResponderADMSpec ) expectMsg( Failure( - BadRequestException(s"Invalid value for name parameter of hasPermissions: $name, it should be one of " + - s"${EntityPermissionAbbreviations.toString}"))) + BadRequestException( + s"Invalid value for name parameter of hasPermissions: $name, it should be one of " + + s"${EntityPermissionAbbreviations.toString}" + ) + ) + ) } @@ -1174,7 +1242,8 @@ class PermissionsResponderADMSpec name = OntologyConstants.KnoraBase.DeletePermission, additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = Some(code) - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, @@ -1188,7 +1257,10 @@ class PermissionsResponderADMSpec Failure( BadRequestException( s"Invalid value for permissionCode parameter of hasPermissions: $code, it should be one of " + - s"${PermissionTypeAndCodes.values.toString}"))) + s"${PermissionTypeAndCodes.values.toString}" + ) + ) + ) } @@ -1199,7 +1271,8 @@ class PermissionsResponderADMSpec name = "", additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), permissionCode = None - )) + ) + ) responderManager ! PermissionChangeHasPermissionsRequestADM( permissionIri = permissionIri, @@ -1210,8 +1283,12 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(BadRequestException( - s"One of permission code or permission name must be provided for a default object access permission."))) + Failure( + BadRequestException( + s"One of permission code or permission name must be provided for a default object access permission." + ) + ) + ) } } @@ -1229,8 +1306,12 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(ForbiddenException( - s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) } "update resource class of a default object access permission" in { val permissionIri = "http://rdfh.ch/permissions/0803/003-d2" @@ -1284,8 +1365,13 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(BadRequestException(s"Permission $permissionIri is of type administrative permission. " + - s"Only a default object access permission defined for a resource class can be updated."))) + Failure( + BadRequestException( + s"Permission $permissionIri is of type administrative permission. " + + s"Only a default object access permission defined for a resource class can be updated." + ) + ) + ) } } "ask to update property of a permission" should { @@ -1302,8 +1388,13 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(BadRequestException(s"Permission $permissionIri is of type administrative permission. " + - s"Only a default object access permission defined for a property can be updated."))) + Failure( + BadRequestException( + s"Permission $permissionIri is of type administrative permission. " + + s"Only a default object access permission defined for a property can be updated." + ) + ) + ) } "throw ForbiddenException for PermissionChangePropertyRequestADM if requesting user is not system or project Admin" in { val permissionIri = "http://rdfh.ch/permissions/0000/001-d3" @@ -1318,8 +1409,12 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(ForbiddenException( - s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) } "update property of a default object access permission" in { val permissionIri = "http://rdfh.ch/permissions/0000/001-d3" @@ -1380,8 +1475,12 @@ class PermissionsResponderADMSpec apiRequestID = UUID.randomUUID() ) expectMsg( - Failure(ForbiddenException( - s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) } "erase a permission with given IRI" in { diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index a47603d7d2..bb29eddfc0 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -18,9 +18,9 @@ */ /** - * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder - * extend ResponderADM which messes up testing, as we cannot inject the TestActor system. - */ + * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder + * extend ResponderADM which messes up testing, as we cannot inject the TestActor system. + */ package org.knora.webapi.responders.admin import java.util.UUID @@ -60,8 +60,8 @@ object ProjectsResponderADMSpec { } /** - * This spec is used to test the messages received by the [[ProjectsResponderADM]] actor. - */ + * This spec is used to test the messages received by the [[ProjectsResponderADM]] actor. + */ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -239,7 +239,8 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) received.project.shortcode should be(shortCode.toUpperCase) // upper case received.project.longname should contain("project longname") received.project.description should be( - Seq(StringLiteralV2(value = "project description", language = Some("en")))) + Seq(StringLiteralV2(value = "project description", language = Some("en"))) + ) newProjectIri.set(received.project.id) @@ -280,26 +281,34 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) // Check Default Object Access permission of ProjectAdmin val hasDOAPForProjectAdmin = receivedDoaps.defaultObjectAccessPermissions.filter { doap: DefaultObjectAccessPermissionADM => - doap.forProject == received.project.id && doap.forGroup.contains(OntologyConstants.KnoraAdmin.ProjectAdmin) && - doap.hasPermissions.equals(Set( - PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.deletePermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin) - )) + doap.forProject == received.project.id && doap.forGroup.contains( + OntologyConstants.KnoraAdmin.ProjectAdmin + ) && + doap.hasPermissions.equals( + Set( + PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.deletePermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin) + ) + ) } hasDOAPForProjectAdmin.size shouldBe 1 // Check Default Object Access permission of ProjectMember val hasDOAPForProjectMember = receivedDoaps.defaultObjectAccessPermissions.filter { doap: DefaultObjectAccessPermissionADM => - doap.forProject == received.project.id && doap.forGroup.contains(OntologyConstants.KnoraAdmin.ProjectMember) && - doap.hasPermissions.equals(Set( - PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember), - PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectMember), - PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectMember) - )) + doap.forProject == received.project.id && doap.forGroup.contains( + OntologyConstants.KnoraAdmin.ProjectMember + ) && + doap.hasPermissions.equals( + Set( + PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember), + PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectMember), + PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectMember) + ) + ) } hasDOAPForProjectMember.size shouldBe 1 } @@ -326,7 +335,8 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) received.project.shortcode should be("1112") received.project.longname should contain("project longname") received.project.description should be( - Seq(StringLiteralV2(value = "project description", language = Some("en")))) + Seq(StringLiteralV2(value = "project description", language = Some("en"))) + ) } @@ -354,8 +364,13 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) received.project.longname should contain(stringFormatter.fromSparqlEncodedString(longnameWithSpecialCharacter)) received.project.description should be( - Seq(StringLiteralV2(value = stringFormatter.fromSparqlEncodedString(descriptionWithSpecialCharacter), - language = Some("en")))) + Seq( + StringLiteralV2( + value = stringFormatter.fromSparqlEncodedString(descriptionWithSpecialCharacter), + language = Some("en") + ) + ) + ) received.project.keywords should contain(stringFormatter.fromSparqlEncodedString(keywordWithSpecialCharacter)) } @@ -405,8 +420,13 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) shortname = None, longname = Some("updated project longname"), description = Some( - Seq(StringLiteralV2(value = """updated project description with "quotes" and """, - language = Some("en")))), + Seq( + StringLiteralV2( + value = """updated project description with "quotes" and """, + language = Some("en") + ) + ) + ), keywords = Some(Seq("updated", "keywords")), logo = Some("/fu/bar/baz-updated.jpg"), status = Some(false), @@ -421,8 +441,13 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) received.project.shortcode should be("111C") received.project.longname should be(Some("updated project longname")) received.project.description should be( - Seq(StringLiteralV2(value = """updated project description with "quotes" and """, - language = Some("en")))) + Seq( + StringLiteralV2( + value = """updated project description with "quotes" and """, + language = Some("en") + ) + ) + ) received.project.keywords.sorted should be(Seq("updated", "keywords").sorted) received.project.logo should be(Some("/fu/bar/baz-updated.jpg")) received.project.status should be(false) @@ -439,7 +464,9 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) ) expectMsg( Failure( - NotFoundException(s"Project 'http://rdfh.ch/projects/notexisting' not found. Aborting update request."))) + NotFoundException(s"Project 'http://rdfh.ch/projects/notexisting' not found. Aborting update request.") + ) + ) } "return 'BadRequest' if nothing would be changed during the update" in { @@ -454,7 +481,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) UUID.randomUUID() ) expectMsg(Failure(BadRequestException("No data would be changed. Aborting update request."))) - */ + */ } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala index a7a27a5147..cd64c9d7f8 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala @@ -37,8 +37,8 @@ object SipiResponderADMSpec { } /** - * Tests [[SipiResponderADM]]. - */ + * Tests [[SipiResponderADM]]. + */ class SipiResponderADMSpec extends CoreSpec(SipiResponderADMSpec.config) with ImplicitSender { override lazy val rdfDataObjects = List( @@ -70,10 +70,13 @@ class SipiResponderADMSpec extends CoreSpec(SipiResponderADMSpec.config) with Im requestingUser = SharedTestDataADM.anonymousUser ) - expectMsg(timeout, - SipiFileInfoGetResponseADM( - permissionCode = 1, - Some(ProjectRestrictedViewSettingsADM(size = Some("!512,512"), watermark = Some("path_to_image"))))) + expectMsg( + timeout, + SipiFileInfoGetResponseADM( + permissionCode = 1, + Some(ProjectRestrictedViewSettingsADM(size = Some("!512,512"), watermark = Some("path_to_image"))) + ) + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala index 0bed67bcd7..feb077aa03 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala @@ -52,13 +52,13 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with private val timeout: FiniteDuration = 8.seconds - private val rootUser = SharedTestDataADM.rootUser + private val rootUser = SharedTestDataADM.rootUser private val anythingAdminUser = SharedTestDataADM.anythingAdminUser - private val normalUser = SharedTestDataADM.normalUser + private val normalUser = SharedTestDataADM.normalUser private val incunabulaUser = SharedTestDataADM.incunabulaProjectAdminUser - private val imagesProject = SharedTestDataADM.imagesProject + private val imagesProject = SharedTestDataADM.imagesProject private val imagesReviewerGroup = SharedTestDataADM.imagesReviewerGroup implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -402,7 +402,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with "UPDATE the user's password (by himself)" in { val requesterPassword = Password.create("test").fold(error => throw error, value => value) - val newPassword = Password.create("test123456").fold(error => throw error, value => value) + val newPassword = Password.create("test123456").fold(error => throw error, value => value) responderManager ! UserChangePasswordRequestADM( userIri = SharedTestDataADM.normalUser.id, userUpdatePasswordPayload = UserUpdatePasswordPayloadADM( @@ -430,7 +430,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with "UPDATE the user's password (by a system admin)" in { val requesterPassword = Password.create("test").fold(error => throw error, value => value) - val newPassword = Password.create("test654321").fold(error => throw error, value => value) + val newPassword = Password.create("test654321").fold(error => throw error, value => value) responderManager ! UserChangePasswordRequestADM( userIri = SharedTestDataADM.normalUser.id, diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala index 9a9512a87f..7e99f14643 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala @@ -29,8 +29,8 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import scala.concurrent.duration._ /** - * Static data for testing [[ListsResponderV1]]. - */ + * Static data for testing [[ListsResponderV1]]. + */ object ListsResponderV1Spec { val config: Config = ConfigFactory.parseString(""" akka.loglevel = "DEBUG" @@ -39,8 +39,8 @@ object ListsResponderV1Spec { } /** - * Tests [[ListsResponderV1]]. - */ + * Tests [[ListsResponderV1]]. + */ class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with ImplicitSender { // The default timeout for receiving reply messages from actors. @@ -97,7 +97,8 @@ class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with Im label = Some("Personen"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/a5f66db8a7" - )), + ) + ), label = Some("SCHWEIZ"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/0cc31a7fa7" @@ -113,7 +114,8 @@ class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with Im label = Some("Personen"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/d75d142ba8" - )), + ) + ), label = Some("GRAUB\u00DCNDEN"), name = Some("2"), id = "http://rdfh.ch/lists/00FF/3e2ac1f1a7" @@ -1392,11 +1394,13 @@ class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with Im label = Some("St. Moritz Kirchen"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/af007b34ca" - )), + ) + ), label = Some("RELIGION UND KIRCHEN"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/16cd27fbc9" - )), + ) + ), label = Some("RELIGION"), name = Some("7REL"), id = "http://rdfh.ch/lists/00FF/7d99d4c1c9" @@ -2022,7 +2026,8 @@ class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with Im label = Some("Personen"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/4db9d4abdb" - )), + ) + ), label = Some("Curling"), name = Some("5"), id = "http://rdfh.ch/lists/00FF/b4858172db" @@ -2688,7 +2693,8 @@ class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with Im label = Some("Eisenbahnen und Bahnh\u00F6fe"), name = Some("1"), id = "http://rdfh.ch/lists/00FF/1dd9d495ed" - )), + ) + ), label = Some("EISENBAHNEN"), name = Some("1-1"), id = "http://rdfh.ch/lists/00FF/84a5815ced" diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/OntologyResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/OntologyResponderV1Spec.scala index 4ccecf9e0f..ef8100f380 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/OntologyResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/OntologyResponderV1Spec.scala @@ -30,8 +30,8 @@ import org.knora.webapi.sharedtestdata.{SharedOntologyTestDataADM, SharedTestDat import scala.concurrent.duration._ /** - * Static data for testing [[OntologyResponderV1]]. - */ + * Static data for testing [[OntologyResponderV1]]. + */ object OntologyResponderV1Spec { // A test user that prefers responses in German. @@ -43,8 +43,8 @@ object OntologyResponderV1Spec { } /** - * Tests [[OntologyResponderV1]]. - */ + * Tests [[OntologyResponderV1]]. + */ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { // The default timeout for receiving reply messages from actors. @@ -85,7 +85,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { occurrence = "1", vocabulary = "http://www.knora.org/ontology/0803/incunabula", description = Some( - "Diese Property bezeichnet eine Verbindung zu einer anderen Resource, in dem ausgesagt wird, dass die vorliegende Resource ein integraler Teil der anderen Resource ist. Zum Beispiel ist eine Buchseite ein integraler Bestandteil genau eines Buches."), + "Diese Property bezeichnet eine Verbindung zu einer anderen Resource, in dem ausgesagt wird, dass die vorliegende Resource ein integraler Teil der anderen Resource ist. Zum Beispiel ist eine Buchseite ein integraler Bestandteil genau eines Buches." + ), label = Some("ist ein Teil von"), name = "http://www.knora.org/ontology/0803/incunabula#partOf", id = "http://www.knora.org/ontology/0803/incunabula#partOf" @@ -241,7 +242,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { occurrence = "0-n", vocabulary = "http://www.knora.org/ontology/0803/incunabula", description = Some( - "Ein Verlag ist ein Medienunternehmen, das Werke der Literatur, Kunst, Musik oder Wissenschaft vervielf\u00E4ltigt und verbreitet. Der Verkauf kann \u00FCber den Handel (Kunst-, Buchhandel etc.) oder durch den Verlag selbst erfolgen. Das Wort \u201Everlegen\u201C bedeutet im Mittelhochdeutschen \u201EGeld ausgeben\u201C oder \u201Eetwas auf seine Rechnung nehmen\u201C. (Wikipedia http://de.wikipedia.org/wiki/Verlag)"), + "Ein Verlag ist ein Medienunternehmen, das Werke der Literatur, Kunst, Musik oder Wissenschaft vervielf\u00E4ltigt und verbreitet. Der Verkauf kann \u00FCber den Handel (Kunst-, Buchhandel etc.) oder durch den Verlag selbst erfolgen. Das Wort \u201Everlegen\u201C bedeutet im Mittelhochdeutschen \u201EGeld ausgeben\u201C oder \u201Eetwas auf seine Rechnung nehmen\u201C. (Wikipedia http://de.wikipedia.org/wiki/Verlag)" + ), label = Some("Verleger"), name = "http://www.knora.org/ontology/0803/incunabula#publisher", id = "http://www.knora.org/ontology/0803/incunabula#publisher" @@ -752,7 +754,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { valuetype_id = "http://www.knora.org/ontology/knora-base#LinkValue", vocabulary = "http://www.knora.org/ontology/0803/incunabula", description = Some( - "Diese Property bezeichnet eine Verbindung zu einer anderen Resource, in dem ausgesagt wird, dass die vorliegende Resource ein integraler Teil der anderen Resource ist. Zum Beispiel ist eine Buchseite ein integraler Bestandteil genau eines Buches."), + "Diese Property bezeichnet eine Verbindung zu einer anderen Resource, in dem ausgesagt wird, dass die vorliegende Resource ein integraler Teil der anderen Resource ist. Zum Beispiel ist eine Buchseite ein integraler Bestandteil genau eines Buches." + ), label = Some("is a part of"), name = "http://www.knora.org/ontology/0803/incunabula#partOf", id = "http://www.knora.org/ontology/0803/incunabula#partOf" @@ -923,7 +926,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { valuetype_id = "http://www.knora.org/ontology/knora-base#TextValue", vocabulary = "http://www.knora.org/ontology/0803/incunabula", description = Some( - "Publishing is the process of production and dissemination of literature or information \u2013 the activity of making information available for public view. In some cases authors may be their own publishers, meaning: originators and developers of content also provide media to deliver and display the content. (Wikipedia http://en.wikipedia.org/wiki/Publisher)"), + "Publishing is the process of production and dissemination of literature or information \u2013 the activity of making information available for public view. In some cases authors may be their own publishers, meaning: originators and developers of content also provide media to deliver and display the content. (Wikipedia http://en.wikipedia.org/wiki/Publisher)" + ), label = Some("Publisher"), name = "http://www.knora.org/ontology/0803/incunabula#publisher", id = "http://www.knora.org/ontology/0803/incunabula#publisher" @@ -962,10 +966,14 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { } } - private def checkResourceTypesForNamedGraphResponseV1(received: ResourceTypesForNamedGraphResponseV1, - expected: ResourceTypesForNamedGraphResponseV1) = { - assert(received.resourcetypes.size == expected.resourcetypes.size, - s"${expected.resourcetypes.size} were expected, but ${received.resourcetypes.size} given.") + private def checkResourceTypesForNamedGraphResponseV1( + received: ResourceTypesForNamedGraphResponseV1, + expected: ResourceTypesForNamedGraphResponseV1 + ) = { + assert( + received.resourcetypes.size == expected.resourcetypes.size, + s"${expected.resourcetypes.size} were expected, but ${received.resourcetypes.size} given." + ) received.resourcetypes.sortBy(_.id).zip(expected.resourcetypes.sortBy(_.id)).foreach { case (receivedResType, expectedResType) => @@ -981,18 +989,19 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { } - private def checkPropertyTypesForNamedGraphIncunabula(received: PropertyTypesForNamedGraphResponseV1, - expected: PropertyTypesForNamedGraphResponseV1) = { + private def checkPropertyTypesForNamedGraphIncunabula( + received: PropertyTypesForNamedGraphResponseV1, + expected: PropertyTypesForNamedGraphResponseV1 + ) = { assert(received.properties.size == expected.properties.size, s"Sizes of properties did not match") val sortedReceivedProperties = received.properties.sortBy(_.id) val sortedExpectedProperties = expected.properties.sortBy(_.id) - sortedReceivedProperties.zip(sortedExpectedProperties).foreach { - case (receivedProp, expectedProp) => - assert(receivedProp.id == expectedProp.id, "The properties' IRIs did not match.") - assert(receivedProp.valuetype_id == expectedProp.valuetype_id, "The properties' valuetypes did not match.") - assert(receivedProp.attributes == expectedProp.attributes, "The properties' attributes did not match.") + sortedReceivedProperties.zip(sortedExpectedProperties).foreach { case (receivedProp, expectedProp) => + assert(receivedProp.id == expectedProp.id, "The properties' IRIs did not match.") + assert(receivedProp.valuetype_id == expectedProp.valuetype_id, "The properties' valuetypes did not match.") + assert(receivedProp.attributes == expectedProp.attributes, "The properties' attributes did not match.") } } @@ -1005,9 +1014,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { resourceTypeIri = "http://www.knora.org/ontology/0803/incunabula#page" ) - expectMsgPF(timeout) { - case msg: ResourceTypeResponseV1 => - checkResourceTypeResponseV1(received = msg, expected = page) + expectMsgPF(timeout) { case msg: ResourceTypeResponseV1 => + checkResourceTypeResponseV1(received = msg, expected = page) } } @@ -1018,9 +1026,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { resourceTypeIri = "http://www.knora.org/ontology/0803/incunabula#book" ) - expectMsgPF(timeout) { - case msg: ResourceTypeResponseV1 => - checkResourceTypeResponseV1(received = msg, expected = book) + expectMsgPF(timeout) { case msg: ResourceTypeResponseV1 => + checkResourceTypeResponseV1(received = msg, expected = book) } } @@ -1031,9 +1038,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { resourceTypeIri = "http://www.knora.org/ontology/knora-base#Region" ) - expectMsgPF(timeout) { - case msg: ResourceTypeResponseV1 => - checkResourceTypeResponseV1(received = msg, expected = region) + expectMsgPF(timeout) { case msg: ResourceTypeResponseV1 => + checkResourceTypeResponseV1(received = msg, expected = region) } } @@ -1044,9 +1050,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { resourceTypeIri = "http://www.knora.org/ontology/knora-base#LinkObj" ) - expectMsgPF(timeout) { - case msg: ResourceTypeResponseV1 => - checkResourceTypeResponseV1(received = msg, expected = linkObject) + expectMsgPF(timeout) { case msg: ResourceTypeResponseV1 => + checkResourceTypeResponseV1(received = msg, expected = linkObject) } } @@ -1058,8 +1063,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { resourceTypeIri = "http://www.knora.org/ontology/0803/incunabula#image" ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -1069,13 +1074,16 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { userProfile = OntologyResponderV1Spec.userProfileWithGerman // irrelevant ) - expectMsgPF(timeout) { - case msg: EntityInfoGetResponseV1 => - val titleContent = msg.propertyInfoMap("http://www.knora.org/ontology/0803/incunabula#title") - titleContent.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(("de", "en"))) should ===(Some("Titel")) - titleContent.getPredicateObject(predicateIri = OntologyConstants.Rdfs.Label, - preferredLangs = Some(("fr", "en"))) should ===(Some("Titre")) + expectMsgPF(timeout) { case msg: EntityInfoGetResponseV1 => + val titleContent = msg.propertyInfoMap("http://www.knora.org/ontology/0803/incunabula#title") + titleContent.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(("de", "en")) + ) should ===(Some("Titel")) + titleContent.getPredicateObject( + predicateIri = OntologyConstants.Rdfs.Label, + preferredLangs = Some(("fr", "en")) + ) should ===(Some("Titre")) } } @@ -1098,9 +1106,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { userADM = OntologyResponderV1Spec.userProfileWithEnglish ) - expectMsgPF(timeout) { - case msg: ResourceTypesForNamedGraphResponseV1 => - checkResourceTypesForNamedGraphResponseV1(received = msg, expected = resourceTypesForNamedGraphIncunabula) + expectMsgPF(timeout) { case msg: ResourceTypesForNamedGraphResponseV1 => + checkResourceTypesForNamedGraphResponseV1(received = msg, expected = resourceTypesForNamedGraphIncunabula) } } @@ -1112,9 +1119,8 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { userADM = OntologyResponderV1Spec.userProfileWithEnglish ) - expectMsgPF(timeout) { - case msg: PropertyTypesForNamedGraphResponseV1 => - checkPropertyTypesForNamedGraphIncunabula(received = msg, expected = propertyTypesForNamedGraphIncunabula) + expectMsgPF(timeout) { case msg: PropertyTypesForNamedGraphResponseV1 => + checkPropertyTypesForNamedGraphIncunabula(received = msg, expected = propertyTypesForNamedGraphIncunabula) } } @@ -1125,10 +1131,9 @@ class OntologyResponderV1Spec extends CoreSpec() with ImplicitSender { userADM = OntologyResponderV1Spec.userProfileWithEnglish ) - expectMsgPF(timeout) { - case msg: PropertyTypesForNamedGraphResponseV1 => - // simply checks that no error occurred when getting the property definitions for all vocabularies - () + expectMsgPF(timeout) { case msg: PropertyTypesForNamedGraphResponseV1 => + // simply checks that no error occurred when getting the property definitions for all vocabularies + () } } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala index 1e0cb9a78f..24d974bb66 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala @@ -18,9 +18,9 @@ */ /** - * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder - * extend ResponderV1 which messes up testing, as we cannot inject the TestActor system. - */ + * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder + * extend ResponderV1 which messes up testing, as we cannot inject the TestActor system. + */ package org.knora.webapi.responders.v1 import akka.actor.Status.Failure @@ -42,8 +42,8 @@ object ProjectsResponderV1Spec { } /** - * This spec is used to test the messages received by the [[ProjectsResponderV1]] actor. - */ + * This spec is used to test the messages received by the [[ProjectsResponderV1]] actor. + */ class ProjectsResponderV1Spec extends CoreSpec(ProjectsResponderV1Spec.config) with ImplicitSender { private val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala index b44377874e..815b29338e 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala @@ -742,7 +742,7 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) propValues.sortBy { valueObject: ResourceCreateValueResponseV1 => val stringValue = valueObject.value.textval.map { case (valType: LiteralValueType.Value, value: String) => value // get string and ignore value type - }.head // each value is represented by a map consisting of only one item (e.g. string -> "book title") + }.head // each value is represented by a map consisting of only one item (e.g. string -> "book title") stringValue } ) @@ -1193,7 +1193,7 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) val pubdate = DateUtilV1.createJDNValueV1FromDateString("GREGORIAN:2015-12-03") val valuesToBeCreated: Map[IRI, Seq[CreateValueV1WithComment]] = Map( - "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), + "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(CreateValueV1WithComment(pubdate)), "http://www.knora.org/ontology/0803/incunabula#citation" -> Vector( CreateValueV1WithComment(citation1, None) @@ -1267,7 +1267,7 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val valuesToBeCreated: Map[IRI, Seq[CreateValueV1WithComment]] = Map( - "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), + "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(CreateValueV1WithComment(title1)), "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(CreateValueV1WithComment(pubdateRequest)), "http://www.knora.org/ontology/0803/incunabula#citation" -> Vector( CreateValueV1WithComment(citation4, None), @@ -1279,10 +1279,10 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val valuesExpected = Map( - "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(title1), - "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(pubdateResponse), + "http://www.knora.org/ontology/0803/incunabula#title" -> Vector(title1), + "http://www.knora.org/ontology/0803/incunabula#pubdate" -> Vector(pubdateResponse), "http://www.knora.org/ontology/0803/incunabula#citation" -> Vector(citation3, citation1, citation4, citation2), - "http://www.knora.org/ontology/0803/incunabula#publoc" -> Vector(publoc) + "http://www.knora.org/ontology/0803/incunabula#publoc" -> Vector(publoc) ) responderManager ! ResourceCreateRequestV1( @@ -1318,9 +1318,9 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) } "create an incunabula:page with a resource pointer" in { - val recto = TextValueSimpleV1("recto") + val recto = TextValueSimpleV1("recto") val origname = TextValueSimpleV1("Blatt") - val seqnum = IntegerValueV1(1) + val seqnum = IntegerValueV1(1) val fileValue = StillImageFileValueV1( internalMimeType = "image/jp2", @@ -1338,10 +1338,10 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) "http://www.knora.org/ontology/0803/incunabula#hasRightSideband" -> Vector( CreateValueV1WithComment(LinkUpdateV1(targetResourceIri = "http://rdfh.ch/0803/482a33d65c36")) ), - "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(CreateValueV1WithComment(recto)), - "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(CreateValueV1WithComment(LinkUpdateV1(book))), + "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(CreateValueV1WithComment(recto)), + "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(CreateValueV1WithComment(LinkUpdateV1(book))), "http://www.knora.org/ontology/0803/incunabula#origname" -> Vector(CreateValueV1WithComment(origname)), - "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(CreateValueV1WithComment(seqnum)) + "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(CreateValueV1WithComment(seqnum)) ) val expected = Map( @@ -1351,11 +1351,11 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) valueResourceClass = Some("http://www.knora.org/ontology/0803/incunabula#Sideband") ) ), - "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(recto), - "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(LinkV1(book)), + "http://www.knora.org/ontology/0803/incunabula#pagenum" -> Vector(recto), + "http://www.knora.org/ontology/0803/incunabula#partOf" -> Vector(LinkV1(book)), "http://www.knora.org/ontology/0803/incunabula#origname" -> Vector(origname), - "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(seqnum), - OntologyConstants.KnoraBase.HasStillImageFileValue -> Vector(fileValue) + "http://www.knora.org/ontology/0803/incunabula#seqnum" -> Vector(seqnum), + OntologyConstants.KnoraBase.HasStillImageFileValue -> Vector(fileValue) ) responderManager ! ResourceCreateRequestV1( @@ -1674,8 +1674,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val response = expectMsgType[GraphDataGetResponseV1](timeout) - val edges = response.edges - val nodes = response.nodes + val edges = response.edges + val nodes = response.nodes edges should contain theSameElementsAs graphForAnythingUser1.edges nodes should contain theSameElementsAs graphForAnythingUser1.nodes @@ -1689,8 +1689,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) val response = expectMsgType[GraphDataGetResponseV1](timeout) - val edges = response.edges - val nodes = response.nodes + val edges = response.edges + val nodes = response.nodes edges should contain theSameElementsAs graphForIncunabulaUser.edges nodes should contain theSameElementsAs graphForIncunabulaUser.nodes diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecContextData.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecContextData.scala index 017ba6ef86..cc8c2b3b48 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecContextData.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecContextData.scala @@ -61,14 +61,16 @@ object ResourcesResponderV1SpecContextData { locdata = None, locations = None, preview = None, - restype_iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/book.gif"), + restype_iconsrc = + Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/book.gif"), restype_description = Some("Diese Resource-Klasse beschreibt ein Buch"), restype_label = Some("Buch"), restype_name = Some("http://www.knora.org/ontology/0803/incunabula#book"), restype_id = "http://www.knora.org/ontology/0803/incunabula#book", person_id = "http://rdfh.ch/users/91e19f1e01", project_id = "http://rdfh.ch/projects/0803" - )), + ) + ), parent_res_id = Some("http://rdfh.ch/0803/c5058f3a"), resinfo = Some( ResourceInfoV1( @@ -78,106 +80,116 @@ object ResourcesResponderV1SpecContextData { lastmod = "0000-00-00 00:00:00", resclass_has_location = true, resclass_name = "object", - locdata = Some(LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", - ny = Some(3505), - nx = Some(2613), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - )), - locations = Some(Vector( + locdata = Some( LocationV1( protocol = "file", duration = 0, fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/95,128/0/default.jpg", - ny = Some(128), - nx = Some(95), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/82,110/0/default.jpg", - ny = Some(110), - nx = Some(82), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/163,219/0/default.jpg", - ny = Some(219), - nx = Some(163), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/327,438/0/default.jpg", - ny = Some(438), - nx = Some(327), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/653,876/0/default.jpg", - ny = Some(876), - nx = Some(653), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/1307,1753/0/default.jpg", - ny = Some(1753), - nx = Some(1307), + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", + ny = Some(3505), + nx = Some(2613), origname = Some("ad+s167_druck1=0001.tif"), format_name = "JPEG2000" - ), + ) + ), + locations = Some( + Vector( + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/95,128/0/default.jpg", + ny = Some(128), + nx = Some(95), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/82,110/0/default.jpg", + ny = Some(110), + nx = Some(82), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/163,219/0/default.jpg", + ny = Some(219), + nx = Some(163), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/327,438/0/default.jpg", + ny = Some(438), + nx = Some(327), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/653,876/0/default.jpg", + ny = Some(876), + nx = Some(653), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = + s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/1307,1753/0/default.jpg", + ny = Some(1753), + nx = Some(1307), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = + s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", + ny = Some(3505), + nx = Some(2613), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ) + ) + ), + preview = Some( LocationV1( protocol = "file", duration = 0, fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", - ny = Some(3505), - nx = Some(2613), + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/95,128/0/default.jpg", + ny = Some(128), + nx = Some(95), origname = Some("ad+s167_druck1=0001.tif"), format_name = "JPEG2000" ) - )), - preview = Some(LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/95,128/0/default.jpg", - ny = Some(128), - nx = Some(95), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - )), - restype_iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/page.gif"), + ), + restype_iconsrc = + Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/page.gif"), restype_description = Some("Eine Seite ist ein Teil eines Buchs"), restype_label = Some("Seite"), restype_name = Some("http://www.knora.org/ontology/0803/incunabula#page"), restype_id = "http://www.knora.org/ontology/0803/incunabula#page", person_id = "http://rdfh.ch/users/91e19f1e01", project_id = "http://rdfh.ch/projects/0803" - )), + ) + ), canonical_res_id = "http://rdfh.ch/0803/8a0b1e75", context = ResourceContextCodeV1.RESOURCE_CONTEXT_IS_PARTOF, region = None, diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecFullData.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecFullData.scala index f1a27b6fc0..2ee8eefe40 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecFullData.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1SpecFullData.scala @@ -48,7 +48,8 @@ object ResourcesResponderV1SpecFullData { locdata = None, locations = None, preview = None, - restype_iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "knora-base/link.gif"), + restype_iconsrc = + Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "knora-base/link.gif"), restype_description = Some("Verkn\u00FCpfung mehrerer Resourcen"), restype_label = Some("Verkn\u00FCpfungsobjekt"), restype_name = Some("http://www.knora.org/ontology/knora-base#LinkObj"), @@ -73,7 +74,8 @@ object ResourcesResponderV1SpecFullData { locdata = None, locations = None, preview = None, - restype_iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "knora-base/link.gif"), + restype_iconsrc = + Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "knora-base/link.gif"), restype_description = Some("Verkn\u00FCpfung mehrerer Resourcen"), restype_label = Some("Verkn\u00FCpfungsobjekt"), restype_name = Some("http://www.knora.org/ontology/knora-base#LinkObj"), @@ -88,310 +90,328 @@ object ResourcesResponderV1SpecFullData { ) ), props = Some( - PropsV1(properties = Vector( - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/8653a672"), - values = Vector(TextValueSimpleV1( - utf8str = "Berthold, der Bruder" - )), - occurrence = Some("0-n"), - attributes = "maxlength=255;size=60", - label = Some("Creator"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(2), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#hasAuthor" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/c3295339"), - values = Vector(TextValueSimpleV1( - utf8str = "Zeitgl\u00F6cklein des Lebens und Leidens Christi" - )), - occurrence = Some("1-n"), - attributes = "size=80;maxlength=255", - label = Some("Titel"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(1), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#title" - ), - PropertyV1( - locations = Nil, - value_rights = Vector( - Some(6), - Some(6), - Some(6) + PropsV1(properties = + Vector( + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/8653a672"), + values = Vector( + TextValueSimpleV1( + utf8str = "Berthold, der Bruder" + ) + ), + occurrence = Some("0-n"), + attributes = "maxlength=255;size=60", + label = Some("Creator"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(2), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#hasAuthor" ), - value_firstprops = Vector( - None, - None, - None + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/c3295339"), + values = Vector( + TextValueSimpleV1( + utf8str = "Zeitgl\u00F6cklein des Lebens und Leidens Christi" + ) + ), + occurrence = Some("1-n"), + attributes = "size=80;maxlength=255", + label = Some("Titel"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(1), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#title" ), - value_iconsrcs = Vector( - None, - None, - None + PropertyV1( + locations = Nil, + value_rights = Vector( + Some(6), + Some(6), + Some(6) + ), + value_firstprops = Vector( + None, + None, + None + ), + value_iconsrcs = Vector( + None, + None, + None + ), + value_restype = Vector( + None, + None, + None + ), + comments = Vector( + None, + None, + None + ), + value_ids = Vector( + "http://rdfh.ch/0803/c5058f3a/values/184e99ca01", + "http://rdfh.ch/0803/c5058f3a/values/db77ec0302", + "http://rdfh.ch/0803/c5058f3a/values/9ea13f3d02" + ), + values = Vector( + TextValueSimpleV1( + utf8str = "Schramm Bd. XXI, S. 27" + ), + TextValueSimpleV1( + utf8str = "GW 4168" + ), + TextValueSimpleV1( + utf8str = "ISTC ib00512000" + ) + ), + occurrence = Some("0-n"), + attributes = "cols=60;wrap=soft;rows=3", + label = Some("Verweis"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(5), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#citation" ), - value_restype = Vector( - None, - None, - None + PropertyV1( + locations = Nil, + value_rights = Vector(Some(7)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/92faf25701"), + values = Vector( + TextValueSimpleV1( + utf8str = "Universit\u00E4ts- und Stadtbibliothek K\u00F6ln, Sign: AD+S167" + ) + ), + occurrence = Some("0-1"), + attributes = "cols=60;rows=4;wrap=soft", + label = Some("Standort"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(6), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#location" ), - comments = Vector( - None, - None, - None + PropertyV1( + locations = Nil, + value_rights = Vector(Some(7)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/10e00c7acc2704"), + values = Vector( + TextValueSimpleV1( + utf8str = "http://www.ub.uni-koeln.de/cdm/compoundobject/collection/inkunabeln/id/1878/rec/1" + ) + ), + occurrence = Some("0-1"), + attributes = "size=60;maxlength=200", + label = Some("URI"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(7), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#url" ), - value_ids = Vector( - "http://rdfh.ch/0803/c5058f3a/values/184e99ca01", - "http://rdfh.ch/0803/c5058f3a/values/db77ec0302", - "http://rdfh.ch/0803/c5058f3a/values/9ea13f3d02" + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/5524469101"), + values = Vector( + TextValueSimpleV1( + utf8str = "Dimension: 8\u00B0" + ) + ), + occurrence = Some("0-1"), + attributes = "cols=60;wrap=soft;rows=3", + label = Some("Physische Beschreibung"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(9), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#physical_desc" ), - values = Vector( - TextValueSimpleV1( - utf8str = "Schramm Bd. XXI, S. 27" + PropertyV1( + locations = Nil, + value_rights = Vector(Some(2)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/0ca74ce5"), + values = Vector( + TextValueSimpleV1( + utf8str = "Basel" + ) ), - TextValueSimpleV1( - utf8str = "GW 4168" + occurrence = Some("0-1"), + attributes = "size=60;maxlength=100", + label = Some("Ort der Herausgabe"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(4), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#publoc" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/cfd09f1e01"), + values = Vector( + DateValueV1( + calendar = KnoraCalendarV1.JULIAN, + dateval2 = "1492", + dateval1 = "1492", + era1 = "CE", + era2 = "CE" + ) ), - TextValueSimpleV1( - utf8str = "ISTC ib00512000" - ) + occurrence = Some("0-1"), + attributes = "", + label = Some("Datum der Herausgabe"), + is_annotation = "0", + guielement = Some("date"), + guiorder = Some(5), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#DateValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#pubdate" ), - occurrence = Some("0-n"), - attributes = "cols=60;wrap=soft;rows=3", - label = Some("Verweis"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(5), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#citation" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(7)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/92faf25701"), - values = Vector(TextValueSimpleV1( - utf8str = "Universit\u00E4ts- und Stadtbibliothek K\u00F6ln, Sign: AD+S167" - )), - occurrence = Some("0-1"), - attributes = "cols=60;rows=4;wrap=soft", - label = Some("Standort"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(6), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#location" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(7)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/10e00c7acc2704"), - values = Vector(TextValueSimpleV1( - utf8str = "http://www.ub.uni-koeln.de/cdm/compoundobject/collection/inkunabeln/id/1878/rec/1" - )), - occurrence = Some("0-1"), - attributes = "size=60;maxlength=200", - label = Some("URI"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(7), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#url" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/5524469101"), - values = Vector(TextValueSimpleV1( - utf8str = "Dimension: 8\u00B0" - )), - occurrence = Some("0-1"), - attributes = "cols=60;wrap=soft;rows=3", - label = Some("Physische Beschreibung"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(9), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#physical_desc" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(2)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/0ca74ce5"), - values = Vector(TextValueSimpleV1( - utf8str = "Basel" - )), - occurrence = Some("0-1"), - attributes = "size=60;maxlength=100", - label = Some("Ort der Herausgabe"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(4), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#publoc" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/cfd09f1e01"), - values = Vector( - DateValueV1( - calendar = KnoraCalendarV1.JULIAN, - dateval2 = "1492", - dateval1 = "1492", - era1 = "CE", - era2 = "CE" - )), - occurrence = Some("0-1"), - attributes = "", - label = Some("Datum der Herausgabe"), - is_annotation = "0", - guielement = Some("date"), - guiorder = Some(5), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#DateValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#pubdate" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/497df9ab"), - values = Vector(TextValueSimpleV1( - utf8str = "Johann Amerbach" - )), - occurrence = Some("0-n"), - attributes = "maxlength=255;size=60", - label = Some("Verleger"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(3), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#publisher" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-n"), - attributes = "wrap=soft;width=95%;rows=7", - label = Some("Kommentar"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(12), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#book_comment" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-1"), - attributes = "", - label = Some("Beschreibung (Richtext)"), - is_annotation = "0", - guielement = Some("richtext"), - guiorder = Some(2), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#description" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-n"), - attributes = "cols=60;wrap=soft;rows=3", - label = Some("Anmerkung"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(10), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#note" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-n"), - attributes = "restypeid=http://www.knora.org/ontology/knora-base#Resource", - label = Some("hat Standoff Link zu"), - is_annotation = "0", - guielement = None, - guiorder = None, - valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/knora-base#hasStandoffLinkTo" + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/c5058f3a/values/497df9ab"), + values = Vector( + TextValueSimpleV1( + utf8str = "Johann Amerbach" + ) + ), + occurrence = Some("0-n"), + attributes = "maxlength=255;size=60", + label = Some("Verleger"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(3), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#publisher" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-n"), + attributes = "wrap=soft;width=95%;rows=7", + label = Some("Kommentar"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(12), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#book_comment" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-1"), + attributes = "", + label = Some("Beschreibung (Richtext)"), + is_annotation = "0", + guielement = Some("richtext"), + guiorder = Some(2), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#description" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-n"), + attributes = "cols=60;wrap=soft;rows=3", + label = Some("Anmerkung"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(10), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#note" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-n"), + attributes = "restypeid=http://www.knora.org/ontology/knora-base#Resource", + label = Some("hat Standoff Link zu"), + is_annotation = "0", + guielement = None, + guiorder = None, + valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/knora-base#hasStandoffLinkTo" + ) ) - ))), + ) + ), resdata = Some( ResourceDataV1( rights = Some(6), @@ -399,7 +419,8 @@ object ResourcesResponderV1SpecFullData { restype_label = Some("Buch"), restype_name = "http://www.knora.org/ontology/0803/incunabula#book", res_id = "http://rdfh.ch/0803/c5058f3a" - )), + ) + ), resinfo = Some( ResourceInfoV1( firstproperty = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), @@ -418,7 +439,8 @@ object ResourcesResponderV1SpecFullData { restype_id = "http://www.knora.org/ontology/0803/incunabula#book", person_id = "http://rdfh.ch/users/91e19f1e01", project_id = "http://rdfh.ch/projects/0803" - )) + ) + ) ) // The expected response to a "full" resource request for a page. @@ -426,9 +448,370 @@ object ResourcesResponderV1SpecFullData { access = "OK", incoming = Nil, props = Some( - PropsV1(properties = Vector( - PropertyV1( - locations = Vector( + PropsV1(properties = + Vector( + PropertyV1( + locations = Vector( + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/95,128/0/default.jpg", + ny = Some(128), + nx = Some(95), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/82,110/0/default.jpg", + ny = Some(110), + nx = Some(82), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/163,219/0/default.jpg", + ny = Some(219), + nx = Some(163), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/327,438/0/default.jpg", + ny = Some(438), + nx = Some(327), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/653,876/0/default.jpg", + ny = Some(876), + nx = Some(653), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = + s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/1307,1753/0/default.jpg", + ny = Some(1753), + nx = Some(1307), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ), + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = + s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", + ny = Some(3505), + nx = Some(2613), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ) + ), + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Vector(None), + value_ids = Vector("0"), + values = Vector(IntegerValueV1(ival = 0)), + occurrence = None, + attributes = "", + label = None, + is_annotation = "0", + guielement = Some("fileupload"), + guiorder = Some(2147483647), + valuetype_id = Some("-1"), + regular_property = 1, + pid = "__location__" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/61cb927602"), + values = Vector( + TextValueSimpleV1( + utf8str = "a1r, Titelblatt" + ) + ), + occurrence = Some("0-1"), + attributes = "size=8;maxlength=8", + label = Some("Seitenbezeichnung"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(1), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#pagenum" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(6)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/3e3d4dc0e922"), + values = Vector( + TextValueSimpleV1( + utf8str = + "Titel: \"Das andechtig zitglo(e)gglyn | des lebens vnd lide(n)s christi nach | den xxiiij stunden v\u00DFgeteilt.\"\nHolzschnitt: Schlaguhr mit Zifferblatt f\u00FCr 24 Stunden, auf deren oberem Rand zu beiden Seiten einer Glocke die Verk\u00FCndigungsszene mit Maria (links) und dem Engel (rechts) zu sehen ist.\nBord\u00FCre: Ranken mit Fabelwesen, Holzschnitt.\nKolorierung: Rot, Blau, Gr\u00FCn, Gelb, Braun.\nBeschriftung oben Mitte (Graphitstift) \"B 1\"." + ) + ), + occurrence = Some("0-1"), + attributes = "", + label = Some("Beschreibung (Richtext)"), + is_annotation = "0", + guielement = Some("richtext"), + guiorder = Some(2), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#description" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(7)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/e80b2d895f23"), + values = Vector( + TextValueSimpleV1( + utf8str = "Schramm, Bd. 21, Abb. 601." + ) + ), + occurrence = Some("0-n"), + attributes = "wrap=soft;rows=7;width=95%", + label = Some("Kommentar"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(6), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#page_comment" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(2)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/aa488c2203"), + values = Vector( + TextValueSimpleV1( + utf8str = "ad+s167_druck1=0001.tif" + ) + ), + occurrence = Some("1"), + attributes = "maxlength=128;size=54", + label = Some("Urspr\u00FCnglicher Dateiname"), + is_annotation = "0", + guielement = Some("text"), + guiorder = Some(7), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#origname" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(2)), + value_firstprops = Vector(None), + value_iconsrcs = Vector(None), + value_restype = Vector(None), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/e71e39e902"), + values = Vector(IntegerValueV1(ival = 1)), + occurrence = Some("0-1"), + attributes = "max=-1;min=0", + label = Some("Sequenznummer"), + is_annotation = "0", + guielement = Some("spinbox"), + guiorder = Some(3), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#IntValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#seqnum" + ), + PropertyV1( + locations = Nil, + value_rights = Vector(Some(2)), + value_firstprops = Vector(Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi")), + value_iconsrcs = + Vector(Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/book.gif")), + value_restype = Vector(Some("Buch")), + comments = Vector(None), + value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/ac9ddbf4-62a7-4cdc-b530-16cbbaa265bf"), + values = Vector( + LinkV1( + valueResourceClassIcon = + Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/book.gif"), + valueResourceClassLabel = Some("Buch"), + valueResourceClass = Some("http://www.knora.org/ontology/0803/incunabula#book"), + valueLabel = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), + targetResourceIri = "http://rdfh.ch/0803/c5058f3a" + ) + ), + occurrence = Some("1"), + attributes = "restypeid=http://www.knora.org/ontology/0803/incunabula#book", + label = Some("ist ein Teil von"), + is_annotation = "0", + guielement = Some("searchbox"), + guiorder = Some(2), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#partOf" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-1"), + attributes = "numprops=1;restypeid=http://www.knora.org/ontology/0803/incunabula#Sideband", + label = Some("Randleistentyp links"), + is_annotation = "0", + guielement = Some("searchbox"), + guiorder = Some(10), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#hasLeftSideband" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-1"), + attributes = "numprops=1;restypeid=http://www.knora.org/ontology/0803/incunabula#Sideband", + label = Some("Randleistentyp rechts"), + is_annotation = "0", + guielement = Some("searchbox"), + guiorder = Some(11), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#hasRightSideband" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-n"), + attributes = "cols=60;wrap=soft;rows=3", + label = Some("Verweis"), + is_annotation = "0", + guielement = Some("textarea"), + guiorder = Some(5), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#citation" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-n"), + attributes = "restypeid=http://www.knora.org/ontology/knora-base#Resource", + label = Some("hat Standoff Link zu"), + is_annotation = "0", + guielement = None, + guiorder = None, + valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/knora-base#hasStandoffLinkTo" + ), + PropertyV1( + locations = Nil, + value_rights = Nil, + value_firstprops = Nil, + value_iconsrcs = Nil, + value_restype = Nil, + comments = Nil, + value_ids = Nil, + values = Nil, + occurrence = Some("0-n"), + attributes = "", + label = Some("Transkription"), + is_annotation = "0", + guielement = Some("richtext"), + guiorder = Some(12), + valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), + regular_property = 1, + pid = "http://www.knora.org/ontology/0803/incunabula#transcription" + ) + ) + ) + ), + resdata = Some( + ResourceDataV1( + rights = Some(6), + iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/page.gif"), + restype_label = Some("Seite"), + restype_name = "http://www.knora.org/ontology/0803/incunabula#page", + res_id = "http://rdfh.ch/0803/8a0b1e75" + ) + ), + resinfo = Some( + ResourceInfoV1( + firstproperty = Some("a1r, Titelblatt"), + project_shortcode = "0803", + value_of = 0, + lastmod = "0000-00-00 00:00:00", + resclass_has_location = true, + resclass_name = "object", + locdata = Some( + LocationV1( + protocol = "file", + duration = 0, + fps = 0, + path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", + ny = Some(3505), + nx = Some(2613), + origname = Some("ad+s167_druck1=0001.tif"), + format_name = "JPEG2000" + ) + ), + locations = Some( + Vector( LocationV1( protocol = "file", duration = 0, @@ -499,277 +882,9 @@ object ResourcesResponderV1SpecFullData { origname = Some("ad+s167_druck1=0001.tif"), format_name = "JPEG2000" ) - ), - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Vector(None), - value_ids = Vector("0"), - values = Vector(IntegerValueV1(ival = 0)), - occurrence = None, - attributes = "", - label = None, - is_annotation = "0", - guielement = Some("fileupload"), - guiorder = Some(2147483647), - valuetype_id = Some("-1"), - regular_property = 1, - pid = "__location__" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/61cb927602"), - values = Vector(TextValueSimpleV1( - utf8str = "a1r, Titelblatt" - )), - occurrence = Some("0-1"), - attributes = "size=8;maxlength=8", - label = Some("Seitenbezeichnung"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(1), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#pagenum" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(6)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/3e3d4dc0e922"), - values = Vector(TextValueSimpleV1( - utf8str = "Titel: \"Das andechtig zitglo(e)gglyn | des lebens vnd lide(n)s christi nach | den xxiiij stunden v\u00DFgeteilt.\"\nHolzschnitt: Schlaguhr mit Zifferblatt f\u00FCr 24 Stunden, auf deren oberem Rand zu beiden Seiten einer Glocke die Verk\u00FCndigungsszene mit Maria (links) und dem Engel (rechts) zu sehen ist.\nBord\u00FCre: Ranken mit Fabelwesen, Holzschnitt.\nKolorierung: Rot, Blau, Gr\u00FCn, Gelb, Braun.\nBeschriftung oben Mitte (Graphitstift) \"B 1\"." - )), - occurrence = Some("0-1"), - attributes = "", - label = Some("Beschreibung (Richtext)"), - is_annotation = "0", - guielement = Some("richtext"), - guiorder = Some(2), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#description" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(7)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/e80b2d895f23"), - values = Vector(TextValueSimpleV1( - utf8str = "Schramm, Bd. 21, Abb. 601." - )), - occurrence = Some("0-n"), - attributes = "wrap=soft;rows=7;width=95%", - label = Some("Kommentar"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(6), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#page_comment" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(2)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/aa488c2203"), - values = Vector(TextValueSimpleV1( - utf8str = "ad+s167_druck1=0001.tif" - )), - occurrence = Some("1"), - attributes = "maxlength=128;size=54", - label = Some("Urspr\u00FCnglicher Dateiname"), - is_annotation = "0", - guielement = Some("text"), - guiorder = Some(7), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#origname" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(2)), - value_firstprops = Vector(None), - value_iconsrcs = Vector(None), - value_restype = Vector(None), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/e71e39e902"), - values = Vector(IntegerValueV1(ival = 1)), - occurrence = Some("0-1"), - attributes = "max=-1;min=0", - label = Some("Sequenznummer"), - is_annotation = "0", - guielement = Some("spinbox"), - guiorder = Some(3), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#IntValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#seqnum" - ), - PropertyV1( - locations = Nil, - value_rights = Vector(Some(2)), - value_firstprops = Vector(Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi")), - value_iconsrcs = - Vector(Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/book.gif")), - value_restype = Vector(Some("Buch")), - comments = Vector(None), - value_ids = Vector("http://rdfh.ch/0803/8a0b1e75/values/ac9ddbf4-62a7-4cdc-b530-16cbbaa265bf"), - values = Vector(LinkV1( - valueResourceClassIcon = - Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/book.gif"), - valueResourceClassLabel = Some("Buch"), - valueResourceClass = Some("http://www.knora.org/ontology/0803/incunabula#book"), - valueLabel = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi"), - targetResourceIri = "http://rdfh.ch/0803/c5058f3a" - )), - occurrence = Some("1"), - attributes = "restypeid=http://www.knora.org/ontology/0803/incunabula#book", - label = Some("ist ein Teil von"), - is_annotation = "0", - guielement = Some("searchbox"), - guiorder = Some(2), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#partOf" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-1"), - attributes = "numprops=1;restypeid=http://www.knora.org/ontology/0803/incunabula#Sideband", - label = Some("Randleistentyp links"), - is_annotation = "0", - guielement = Some("searchbox"), - guiorder = Some(10), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#hasLeftSideband" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-1"), - attributes = "numprops=1;restypeid=http://www.knora.org/ontology/0803/incunabula#Sideband", - label = Some("Randleistentyp rechts"), - is_annotation = "0", - guielement = Some("searchbox"), - guiorder = Some(11), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#hasRightSideband" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-n"), - attributes = "cols=60;wrap=soft;rows=3", - label = Some("Verweis"), - is_annotation = "0", - guielement = Some("textarea"), - guiorder = Some(5), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#citation" - ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-n"), - attributes = "restypeid=http://www.knora.org/ontology/knora-base#Resource", - label = Some("hat Standoff Link zu"), - is_annotation = "0", - guielement = None, - guiorder = None, - valuetype_id = Some("http://www.knora.org/ontology/knora-base#LinkValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/knora-base#hasStandoffLinkTo" + ) ), - PropertyV1( - locations = Nil, - value_rights = Nil, - value_firstprops = Nil, - value_iconsrcs = Nil, - value_restype = Nil, - comments = Nil, - value_ids = Nil, - values = Nil, - occurrence = Some("0-n"), - attributes = "", - label = Some("Transkription"), - is_annotation = "0", - guielement = Some("richtext"), - guiorder = Some(12), - valuetype_id = Some("http://www.knora.org/ontology/knora-base#TextValue"), - regular_property = 1, - pid = "http://www.knora.org/ontology/0803/incunabula#transcription" - ) - ))), - resdata = Some( - ResourceDataV1( - rights = Some(6), - iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/page.gif"), - restype_label = Some("Seite"), - restype_name = "http://www.knora.org/ontology/0803/incunabula#page", - res_id = "http://rdfh.ch/0803/8a0b1e75" - )), - resinfo = Some( - ResourceInfoV1( - firstproperty = Some("a1r, Titelblatt"), - project_shortcode = "0803", - value_of = 0, - lastmod = "0000-00-00 00:00:00", - resclass_has_location = true, - resclass_name = "object", - locdata = Some(LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", - ny = Some(3505), - nx = Some(2613), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - )), - locations = Some(Vector( + preview = Some( LocationV1( protocol = "file", duration = 0, @@ -779,78 +894,8 @@ object ResourcesResponderV1SpecFullData { nx = Some(95), origname = Some("ad+s167_druck1=0001.tif"), format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/82,110/0/default.jpg", - ny = Some(110), - nx = Some(82), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/163,219/0/default.jpg", - ny = Some(219), - nx = Some(163), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/327,438/0/default.jpg", - ny = Some(438), - nx = Some(327), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/653,876/0/default.jpg", - ny = Some(876), - nx = Some(653), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/1307,1753/0/default.jpg", - ny = Some(1753), - nx = Some(1307), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - ), - LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg", - ny = Some(3505), - nx = Some(2613), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" ) - )), - preview = Some(LocationV1( - protocol = "file", - duration = 0, - fps = 0, - path = s"${settings.externalSipiIIIFGetUrl}/0803/incunabula_0000000002.jp2/full/95,128/0/default.jpg", - ny = Some(128), - nx = Some(95), - origname = Some("ad+s167_druck1=0001.tif"), - format_name = "JPEG2000" - )), + ), restype_iconsrc = Some(settings.salsah1BaseUrl + settings.salsah1ProjectIconsBasePath + "incunabula/page.gif"), restype_description = Some("Eine Seite ist ein Teil eines Buchs"), restype_label = Some("Seite"), @@ -858,7 +903,8 @@ object ResourcesResponderV1SpecFullData { restype_id = "http://www.knora.org/ontology/0803/incunabula#page", person_id = "http://rdfh.ch/users/91e19f1e01", project_id = "http://rdfh.ch/projects/0803" - )) + ) + ) ) val dummyMapping: MappingXMLtoStandoff = MappingXMLtoStandoff( diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/SearchResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/SearchResponderV1Spec.scala index d5a6b00e74..2c8378ab0b 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/SearchResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/SearchResponderV1Spec.scala @@ -29,8 +29,8 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import scala.concurrent.duration._ /** - * Static data for testing [[SearchResponderV1]]. - */ + * Static data for testing [[SearchResponderV1]]. + */ object SearchResponderV1Spec { private val incunabulaUser = SharedTestDataADM.incunabulaMemberUser @@ -64,7 +64,8 @@ object SearchResponderV1Spec { iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), preview_path = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), obj_id = "http://rdfh.ch/0001/a-thing-with-text-values" - )) + ) + ) private val fulltextValueInThingResultsForUser1 = Vector( SearchResultRowV1( @@ -88,7 +89,8 @@ object SearchResponderV1Spec { iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), preview_path = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), obj_id = "http://rdfh.ch/0001/a-thing-with-text-values" - )) + ) + ) private val fulltextThingResultsForUser2 = Vector( SearchResultRowV1( @@ -103,7 +105,8 @@ object SearchResponderV1Spec { iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), preview_path = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), obj_id = "http://rdfh.ch/0001/a-thing-with-text-values" - )) + ) + ) private val hasOtherThingResultsForUser1 = Vector( SearchResultRowV1( @@ -127,7 +130,8 @@ object SearchResponderV1Spec { iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), preview_path = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), obj_id = "http://rdfh.ch/0001/project-thing-1" - )) + ) + ) private val hasStandoffLinkToResultsForUser1 = Vector( SearchResultRowV1( @@ -151,7 +155,8 @@ object SearchResponderV1Spec { iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), preview_path = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), obj_id = "http://rdfh.ch/0001/project-thing-1" - )) + ) + ) private val hasStandoffLinkToResultsForUser2 = Vector( SearchResultRowV1( @@ -175,13 +180,14 @@ object SearchResponderV1Spec { iconsrc = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), preview_path = Some("http://0.0.0.0:3335/project-icons/anything/thing.png"), obj_id = "http://rdfh.ch/0001/project-thing-1" - )) + ) + ) } /** - * Tests [[SearchResponderV1]]. - */ + * Tests [[SearchResponderV1]]. + */ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { import SearchResponderV1Spec._ @@ -206,7 +212,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { show_nrows = 2, start_at = 0, current = true - )), + ) + ), nhits = "2", subjects = Vector( SearchResultRowV1( @@ -402,15 +409,17 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { userProfile = incunabulaUser, searchValue = Vector("Zeitglöcklein", "JULIAN:1490"), compareProps = Vector(SearchComparisonOperatorV1.MATCH, SearchComparisonOperatorV1.EQ), - propertyIri = Vector("http://www.knora.org/ontology/0803/incunabula#title", - "http://www.knora.org/ontology/0803/incunabula#pubdate"), + propertyIri = Vector( + "http://www.knora.org/ontology/0803/incunabula#title", + "http://www.knora.org/ontology/0803/incunabula#pubdate" + ), filterByRestype = Some("http://www.knora.org/ontology/0803/incunabula#book"), startAt = 0, showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(1) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(1) } } @@ -450,8 +459,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(18) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(18) } } @@ -482,8 +491,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(18) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(18) } } @@ -499,8 +508,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(19) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(19) } } @@ -516,8 +525,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(19) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(19) } } @@ -533,8 +542,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(19) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(19) } } @@ -550,8 +559,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 100 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(79) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(79) } } @@ -566,8 +575,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 100 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(79) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(79) } } @@ -583,8 +592,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(2) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(2) } } @@ -600,8 +609,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(7) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(7) } } @@ -617,8 +626,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(15) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(15) } } @@ -634,8 +643,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 500 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(402) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(402) } } @@ -645,15 +654,17 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { userProfile = incunabulaUser, searchValue = Vector("1", ""), compareProps = Vector(SearchComparisonOperatorV1.EQ, SearchComparisonOperatorV1.EXISTS), - propertyIri = Vector("http://www.knora.org/ontology/0803/incunabula#seqnum", - "http://www.knora.org/ontology/0803/incunabula#partOf"), + propertyIri = Vector( + "http://www.knora.org/ontology/0803/incunabula#seqnum", + "http://www.knora.org/ontology/0803/incunabula#partOf" + ), filterByRestype = Some("http://www.knora.org/ontology/0803/incunabula#page"), startAt = 0, showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(19) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(19) } } @@ -662,15 +673,17 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { userProfile = incunabulaUser, searchValue = Vector("1", ""), compareProps = Vector(SearchComparisonOperatorV1.EQ, SearchComparisonOperatorV1.EXISTS), - propertyIri = Vector("http://www.knora.org/ontology/0803/incunabula#seqnum", - "http://www.knora.org/ontology/knora-base#isPartOf"), + propertyIri = Vector( + "http://www.knora.org/ontology/0803/incunabula#seqnum", + "http://www.knora.org/ontology/knora-base#isPartOf" + ), filterByRestype = Some("http://www.knora.org/ontology/knora-base#Representation"), startAt = 0, showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(19) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(19) } } @@ -680,15 +693,17 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { userProfile = incunabulaUser, searchValue = Vector("http://rdfh.ch/0803/c5058f3a", ""), compareProps = Vector(SearchComparisonOperatorV1.EQ, SearchComparisonOperatorV1.EXISTS), - propertyIri = Vector("http://www.knora.org/ontology/0803/incunabula#partOf", - "http://www.knora.org/ontology/0803/incunabula#seqnum"), + propertyIri = Vector( + "http://www.knora.org/ontology/0803/incunabula#partOf", + "http://www.knora.org/ontology/0803/incunabula#seqnum" + ), filterByRestype = Some("http://www.knora.org/ontology/0803/incunabula#page"), startAt = 0, showNRows = 500 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(402) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(402) } } @@ -698,15 +713,17 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { userProfile = incunabulaUser, searchValue = Vector("http://rdfh.ch/0803/c5058f3a", ""), compareProps = Vector(SearchComparisonOperatorV1.EQ, SearchComparisonOperatorV1.EXISTS), - propertyIri = Vector("http://www.knora.org/ontology/knora-base#isPartOf", - "http://www.knora.org/ontology/knora-base#seqnum"), + propertyIri = Vector( + "http://www.knora.org/ontology/knora-base#isPartOf", + "http://www.knora.org/ontology/knora-base#seqnum" + ), filterByRestype = Some("http://www.knora.org/ontology/knora-base#Representation"), startAt = 0, showNRows = 500 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(402) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(402) } } @@ -727,8 +744,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 300 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(199) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(199) } } @@ -745,8 +762,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(10) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(10) } } @@ -763,8 +780,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(1) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(1) } } @@ -781,8 +798,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(4) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(4) } } @@ -799,8 +816,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(fulltextThingResultsForUser1) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(fulltextThingResultsForUser1) } // Another user in the same project, anythingUser2, should get the resource as a search result, but should not see the values. @@ -813,8 +830,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(fulltextThingResultsForUser2) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(fulltextThingResultsForUser2) } // User anythingUser2 should also get the resource as a search result by searching for something that matches the resource's label, but not the values. @@ -827,8 +844,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(fulltextThingResultsForUser2) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(fulltextThingResultsForUser2) } // If user anythingUser1 searches for something that matches one of the values, but doesn't match the resource's label, the result should include the @@ -842,8 +859,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(fulltextValueInThingResultsForUser1) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(fulltextValueInThingResultsForUser1) } // If user anythingUser2 searches for something that matches one of the values, but doesn't match the resource's label, no results should be returned. @@ -856,8 +873,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } // A user in another project shouldn't get any results for any of those queries. @@ -870,8 +887,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } responderManager ! FulltextSearchGetRequestV1( @@ -882,8 +899,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } responderManager ! FulltextSearchGetRequestV1( @@ -894,8 +911,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 25 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } } @@ -910,8 +927,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 10 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } responderManager ! ExtendedSearchGetRequestV1( @@ -924,8 +941,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 10 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } } @@ -942,8 +959,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 10 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(hasOtherThingResultsForUser1) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(hasOtherThingResultsForUser1) } responderManager ! ExtendedSearchGetRequestV1( @@ -956,8 +973,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 10 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(hasStandoffLinkToResultsForUser1) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(hasStandoffLinkToResultsForUser1) } // But another user in the Anything project should see only the hasStandoffLinkTo link. @@ -972,8 +989,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 10 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects.size should ===(0) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects.size should ===(0) } responderManager ! ExtendedSearchGetRequestV1( @@ -986,8 +1003,8 @@ class SearchResponderV1Spec extends CoreSpec() with ImplicitSender { showNRows = 10 ) - expectMsgPF(timeout) { - case response: SearchGetResponseV1 => response.subjects should ===(hasStandoffLinkToResultsForUser2) + expectMsgPF(timeout) { case response: SearchGetResponseV1 => + response.subjects should ===(hasStandoffLinkToResultsForUser2) } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala index 9b9ec886e1..cd29d3e478 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala @@ -18,9 +18,9 @@ */ /** - * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder - * extend ResponderV1 which messes up testing, as we cannot inject the TestActor system. - */ + * To be able to test UsersResponder, we need to be able to start UsersResponder isolated. Now the UsersResponder + * extend ResponderV1 which messes up testing, as we cannot inject the TestActor system. + */ package org.knora.webapi.responders.v1 import akka.actor.Status.Failure @@ -42,8 +42,8 @@ object UsersResponderV1Spec { } /** - * This spec is used to test the messages received by the [[UsersResponderV1]] actor. - */ + * This spec is used to test the messages received by the [[UsersResponderV1]] actor. + */ class UsersResponderV1Spec extends CoreSpec(UsersResponderV1Spec.config) with ImplicitSender { private val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala index 4d2d4b22d5..594df6a879 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala @@ -47,8 +47,8 @@ import org.knora.webapi.util.MutableTestIri import scala.concurrent.duration._ /** - * Static data for testing [[ValuesResponderV1]]. - */ + * Static data for testing [[ValuesResponderV1]]. + */ object ValuesResponderV1Spec { val config: Config = ConfigFactory.parseString(""" akka.loglevel = "DEBUG" @@ -67,8 +67,8 @@ object ValuesResponderV1Spec { } /** - * Tests [[ValuesResponderV1]]. - */ + * Tests [[ValuesResponderV1]]. + */ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with ImplicitSender { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -77,11 +77,14 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with /* we need to run our app with the mocked sipi actor */ override lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with ManagersWithMockedSipi).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + name = APPLICATION_MANAGER_ACTOR_NAME + ) override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/responders.v1.ValuesResponderV1Spec/incunabula-data.ttl", - name = "http://www.knora.org/data/0803/incunabula"), + RdfDataObject( + path = "test_data/responders.v1.ValuesResponderV1Spec/incunabula-data.ttl", + name = "http://www.knora.org/data/0803/incunabula" + ), RdfDataObject(path = "test_data/demo_data/images-demo-data.ttl", name = "http://www.knora.org/data/00FF/images"), RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") ) @@ -127,9 +130,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with defaultXSLTransformation = None ) - private def checkComment1aResponse(response: CreateValueResponseV1, - utf8str: String, - standoff: Seq[StandoffTagV2] = Seq.empty[StandoffTagV2]): Unit = { + private def checkComment1aResponse( + response: CreateValueResponseV1, + utf8str: String, + standoff: Seq[StandoffTagV2] = Seq.empty[StandoffTagV2] + ): Unit = { assert(response.rights == 8, "rights was not 8") assert(response.value.asInstanceOf[TextValueV1].utf8str == utf8str, "comment value did not match") @@ -138,8 +143,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with case textValueWithStandoff: TextValueWithStandoffV1 => assert( textValueWithStandoff.standoff.sortBy(standoffTag => - (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)) == standoff.sortBy(standoffTag => - (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)), + (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition) + ) == standoff.sortBy(standoffTag => (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)), "standoff did not match" ) case _ => throw AssertionException("response should be of type TextValueWithStandoffV1") @@ -186,14 +191,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with assert( response.value.asInstanceOf[TextValueWithStandoffV1].standoff.sortBy(_.standoffTagClassIri) == standoff.sortBy( - _.standoffTagClassIri), + _.standoffTagClassIri + ), "standoff did not match" ) } - private def checkComment1bResponse(response: ChangeValueResponseV1, - utf8str: String, - standoff: Seq[StandoffTagV2] = Seq.empty[StandoffTagV2]): Unit = { + private def checkComment1bResponse( + response: ChangeValueResponseV1, + utf8str: String, + standoff: Seq[StandoffTagV2] = Seq.empty[StandoffTagV2] + ): Unit = { assert(response.rights == 8, "rights was not 8") assert(response.value.asInstanceOf[TextValueV1].utf8str == utf8str, "comment value did not match") @@ -203,8 +211,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with case textValueWithStandoff: TextValueWithStandoffV1 => assert( textValueWithStandoff.standoff.sortBy(standoffTag => - (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)) == standoff.sortBy(standoffTag => - (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)), + (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition) + ) == standoff.sortBy(standoffTag => (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)), "standoff did not match" ) case _ => throw AssertionException("response should be of type TextValueWithStandoffV1") @@ -218,11 +226,13 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with val comments = response.props.get.properties.filter(_.pid == "http://www.knora.org/ontology/0803/incunabula#book_comment").head - assert(comments.values == Vector( - TextValueSimpleV1(utf8str = "Comment 1b"), - TextValueSimpleV1("Comment 2") - ), - "Values of book_comment did not match") + assert( + comments.values == Vector( + TextValueSimpleV1(utf8str = "Comment 1b"), + TextValueSimpleV1("Comment 2") + ), + "Values of book_comment did not match" + ) } private def checkTextValue(expected: TextValueV1, received: TextValueV1): Unit = { @@ -234,19 +244,24 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with case expectedWithStandoff: TextValueWithStandoffV1 => assert( - received.asInstanceOf[TextValueWithStandoffV1].resource_reference == expectedWithStandoff.resource_reference) + received.asInstanceOf[TextValueWithStandoffV1].resource_reference == expectedWithStandoff.resource_reference + ) assert( received .asInstanceOf[TextValueWithStandoffV1] .standoff .map(_.standoffTagClassIri) - .sorted == expectedWithStandoff.standoff.map(_.standoffTagClassIri).sorted) + .sorted == expectedWithStandoff.standoff.map(_.standoffTagClassIri).sorted + ) assert( received .asInstanceOf[TextValueWithStandoffV1] .standoff - .sortBy(standoffTag => (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)) == expectedWithStandoff.standoff - .sortBy(standoffTag => (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition))) + .sortBy(standoffTag => + (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition) + ) == expectedWithStandoff.standoff + .sortBy(standoffTag => (standoffTag.standoffTagClassIri.toString, standoffTag.startPosition)) + ) case _ => } @@ -262,21 +277,22 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(lastModSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - assert(rows.size <= 1, s"Resource $resourceIri has more than one instance of knora-base:lastModificationDate") + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + assert(rows.size <= 1, s"Resource $resourceIri has more than one instance of knora-base:lastModificationDate") - if (rows.size == 1) { - Some(rows.head.rowMap("lastModificationDate")) - } else { - None - } + if (rows.size == 1) { + Some(rows.head.rowMap("lastModificationDate")) + } else { + None + } } } - private def checkImageFileValueChange(received: ChangeFileValueResponseV1, - request: ChangeFileValueRequestV1): Unit = { + private def checkImageFileValueChange( + received: ChangeFileValueResponseV1, + request: ChangeFileValueRequestV1 + ): Unit = { assert(received.locations.size == 1, "Expected one file value to have been changed") received.locations.foreach { location: LocationV1 => @@ -299,8 +315,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => checkComment1aResponse(msg, utf8str) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + checkComment1aResponse(msg, utf8str) } // Check that the resource's last modification date got updated. @@ -321,8 +337,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -334,8 +350,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => checkValueGetResponse(msg) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + checkValueGetResponse(msg) } } @@ -346,9 +362,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - checkValueGetResponseWithStandoff(msg) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + checkValueGetResponseWithStandoff(msg) } } @@ -359,8 +374,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = anythingUser ) - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.rights should ===(2) } } @@ -373,17 +388,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = "http://rdfh.ch/0803/8a0b1e75", - predicateIri = "http://www.knora.org/ontology/0803/incunabula#partOf", - objectIri = zeitglöckleinIri, - referenceCount = 1 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = "http://rdfh.ch/0803/8a0b1e75", + predicateIri = "http://www.knora.org/ontology/0803/incunabula#partOf", + objectIri = zeitglöckleinIri, + referenceCount = 1 + ) + ) + msg.rights should ===(2) } } @@ -402,8 +417,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: ChangeValueResponseV1 => checkComment1bResponse(msg, utf8str) + expectMsgPF(timeout) { case msg: ChangeValueResponseV1 => + checkComment1bResponse(msg, utf8str) } // Check that the resource's last modification date got updated. @@ -414,29 +429,28 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with val sparqlQuery = s""" - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX xsd: - |PREFIX knora-base: - | - |SELECT ?value ?uuid ?permissions WHERE { - | BIND(IRI("$oldIri") AS ?value) - | - | OPTIONAL { - | ?value knora-base:valueHasUUID ?uuid . - | } - | - | OPTIONAL { - | ?value knora-base:hasPermissions ?permissions . - | } - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX xsd: + |PREFIX knora-base: + | + |SELECT ?value ?uuid ?permissions WHERE { + | BIND(IRI("$oldIri") AS ?value) + | + | OPTIONAL { + | ?value knora-base:valueHasUUID ?uuid . + | } + | + | OPTIONAL { + | ?value knora-base:hasPermissions ?permissions . + | } + |} """.stripMargin storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case sparqlSelectResponse: SparqlSelectResult => - assert(sparqlSelectResponse.results.bindings.head.rowMap.keySet == Set("value")) + expectMsgPF(timeout) { case sparqlSelectResponse: SparqlSelectResult => + assert(sparqlSelectResponse.results.bindings.head.rowMap.keySet == Set("value")) } } @@ -451,8 +465,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -468,8 +482,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -484,8 +498,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -499,8 +513,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => () + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + () } responderManager ! ResourceFullGetRequestV1( @@ -509,8 +523,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userADM = incunabulaUser ) - expectMsgPF(timeout) { - case msg: ResourceFullResponseV1 => checkOrderInResource(msg) + expectMsgPF(timeout) { case msg: ResourceFullResponseV1 => + checkOrderInResource(msg) } } @@ -522,8 +536,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: ValueVersionHistoryGetResponseV1 => msg.valueVersions.length should ===(2) + expectMsgPF(timeout) { case msg: ValueVersionHistoryGetResponseV1 => + msg.valueVersions.length should ===(2) } } @@ -537,8 +551,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: DeleteValueResponseV1 => commentIri.set(msg.id) + expectMsgPF(timeout) { case msg: DeleteValueResponseV1 => + commentIri.set(msg.id) } responderManager ! ValueGetRequestV1( @@ -547,8 +561,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } // Check that the resource's last modification date got updated. @@ -566,8 +580,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -581,8 +595,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -595,8 +609,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -610,8 +624,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -625,8 +639,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -639,8 +653,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -653,9 +667,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -670,8 +683,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } // The cardinality of incunabula:seqnum in incunabula:page is 0-1, and page http://rdfh.ch/0803/4f11adaf already has a seqnum. @@ -684,8 +697,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -702,10 +715,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => - currentColorValueIri.set(msg.id) - msg.value should ===(ColorValueV1(color)) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + currentColorValueIri.set(msg.id) + msg.value should ===(ColorValueV1(color)) } } @@ -721,10 +733,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: ChangeValueResponseV1 => - currentColorValueIri.set(msg.id) - msg.value should ===(ColorValueV1(color)) + expectMsgPF(timeout) { case msg: ChangeValueResponseV1 => + currentColorValueIri.set(msg.id) + msg.value should ===(ColorValueV1(color)) } } @@ -742,10 +753,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => - currentGeomValueIri.set(msg.id) - msg.value should ===(GeomValueV1(geom)) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + currentGeomValueIri.set(msg.id) + msg.value should ===(GeomValueV1(geom)) } } @@ -763,10 +773,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: ChangeValueResponseV1 => - currentGeomValueIri.set(msg.id) - msg.value should ===(GeomValueV1(geom)) + expectMsgPF(timeout) { case msg: ChangeValueResponseV1 => + currentGeomValueIri.set(msg.id) + msg.value should ===(GeomValueV1(geom)) } } @@ -777,17 +786,19 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! CreateValueRequestV1( resourceIri = zeitglöckleinIri, propertyIri = "http://www.knora.org/ontology/0803/incunabula#book_comment", - value = TextValueWithStandoffV1(utf8str = utf8str, - standoff = sampleStandoff, - mapping = dummyMapping, - mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping"), + value = TextValueWithStandoffV1( + utf8str = utf8str, + standoff = sampleStandoff, + mapping = dummyMapping, + mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping" + ), featureFactoryConfig = defaultFeatureFactoryConfig, userProfile = incunabulaUser, apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => checkComment1aResponse(msg, utf8str, sampleStandoff) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + checkComment1aResponse(msg, utf8str, sampleStandoff) } } @@ -798,17 +809,19 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! CreateValueRequestV1( resourceIri = zeitglöckleinIri, propertyIri = "http://www.knora.org/ontology/0803/incunabula#book_comment", - value = TextValueWithStandoffV1(utf8str = utf8str, - standoff = sampleStandoff, - mapping = dummyMapping, - mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping"), + value = TextValueWithStandoffV1( + utf8str = utf8str, + standoff = sampleStandoff, + mapping = dummyMapping, + mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping" + ), featureFactoryConfig = defaultFeatureFactoryConfig, userProfile = incunabulaUser, apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -819,17 +832,19 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! ChangeValueRequestV1( valueIri = commentIri.get, - value = TextValueWithStandoffV1(utf8str = utf8str, - standoff = sampleStandoff, - mapping = dummyMapping, - mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping"), + value = TextValueWithStandoffV1( + utf8str = utf8str, + standoff = sampleStandoff, + mapping = dummyMapping, + mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping" + ), featureFactoryConfig = defaultFeatureFactoryConfig, userProfile = incunabulaUser, apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: ChangeValueResponseV1 => checkComment1bResponse(msg, utf8str, sampleStandoff) + expectMsgPF(timeout) { case msg: ChangeValueResponseV1 => + checkComment1bResponse(msg, utf8str, sampleStandoff) } } @@ -839,17 +854,19 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! ChangeValueRequestV1( valueIri = commentIri.get, - value = TextValueWithStandoffV1(utf8str = utf8str, - standoff = sampleStandoff, - mapping = dummyMapping, - mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping"), + value = TextValueWithStandoffV1( + utf8str = utf8str, + standoff = sampleStandoff, + mapping = dummyMapping, + mappingIri = "http://rdfh.ch/standoff/mappings/StandardMapping" + ), featureFactoryConfig = defaultFeatureFactoryConfig, userProfile = incunabulaUser, apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -864,8 +881,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with startPosition = 31, endPosition = 39, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 0 @@ -885,10 +905,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => - firstValueIriWithResourceRef.set(newValueIri) - checkTextValue(received = newValue, expected = textValueWithResourceRef) + expectMsgPF(timeout) { case CreateValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => + firstValueIriWithResourceRef.set(newValueIri) + checkTextValue(received = newValue, expected = textValueWithResourceRef) } responderManager ! LinkValueGetRequestV1( @@ -902,17 +921,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // Since this is the first Standoff resource reference between the source and target resources, we should // now have version 1 of a LinkValue, with a reference count of 1. - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = "http://rdfh.ch/0803/21abac2162", - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = zeitglöckleinIri, - referenceCount = 1 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = "http://rdfh.ch/0803/21abac2162", + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = zeitglöckleinIri, + referenceCount = 1 + ) + ) + msg.rights should ===(2) } val sparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -928,12 +947,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // The new LinkValue should have no previous version, and there should be a direct link between the resources. - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } } @@ -949,8 +967,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with startPosition = 0, endPosition = 4, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 0 @@ -961,8 +982,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with startPosition = 39, endPosition = 47, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 1 @@ -981,10 +1005,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => - firstValueIriWithResourceRef.set(newValueIri) - checkTextValue(received = newValue, expected = textValueWithResourceRef) + expectMsgPF(timeout) { case ChangeValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => + firstValueIriWithResourceRef.set(newValueIri) + checkTextValue(received = newValue, expected = textValueWithResourceRef) } responderManager ! LinkValueGetRequestV1( @@ -998,17 +1021,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // Since the new version still refers to the same resource, the reference count of the LinkValue should not // change. - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = "http://rdfh.ch/0803/21abac2162", - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = zeitglöckleinIri, - referenceCount = 1 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = "http://rdfh.ch/0803/21abac2162", + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = zeitglöckleinIri, + referenceCount = 1 + ) + ) + msg.rights should ===(2) } val sparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1024,12 +1047,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // There should be no new version of the LinkValue, and the direct link should still be there. - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } } @@ -1043,8 +1065,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with startPosition = 30, endPosition = 38, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 0 @@ -1064,10 +1089,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => - secondValueIriWithResourceRef.set(newValueIri) - checkTextValue(received = newValue, expected = textValueWithResourceRef) + expectMsgPF(timeout) { case CreateValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => + secondValueIriWithResourceRef.set(newValueIri) + checkTextValue(received = newValue, expected = textValueWithResourceRef) } responderManager ! LinkValueGetRequestV1( @@ -1081,17 +1105,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // Now that we've added a different TextValue that refers to the same resource, we should have version 2 // of the LinkValue, with a reference count of 2. - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = "http://rdfh.ch/0803/21abac2162", - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = zeitglöckleinIri, - referenceCount = 2 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = "http://rdfh.ch/0803/21abac2162", + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = zeitglöckleinIri, + referenceCount = 2 + ) + ) + msg.rights should ===(2) } val sparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1108,12 +1132,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // It should have a previousValue pointing to the previous version, and the direct link should // still be there. - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } } @@ -1128,10 +1151,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newValue: TextValueSimpleV1, _, newValueIri: IRI, _) => - firstValueIriWithResourceRef.set(newValueIri) - checkTextValue(received = textValue, expected = newValue) + expectMsgPF(timeout) { case ChangeValueResponseV1(newValue: TextValueSimpleV1, _, newValueIri: IRI, _) => + firstValueIriWithResourceRef.set(newValueIri) + checkTextValue(received = textValue, expected = newValue) } responderManager ! LinkValueGetRequestV1( @@ -1144,17 +1166,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // Version 3 of the LinkValue should have a reference count of 1. - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = "http://rdfh.ch/0803/21abac2162", - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = zeitglöckleinIri, - referenceCount = 1 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = "http://rdfh.ch/0803/21abac2162", + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = zeitglöckleinIri, + referenceCount = 1 + ) + ) + msg.rights should ===(2) } val sparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1170,13 +1192,12 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // The LinkValue should point to its previous version, and the direct link should still be there. - expectMsgPF(timeout) { - case response: SparqlSelectResult => - standoffLinkValueIri.set(response.results.bindings.head.rowMap("linkValue")) - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + standoffLinkValueIri.set(response.results.bindings.head.rowMap("linkValue")) + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } // The LinkValue should have 3 versions in its version history. @@ -1188,8 +1209,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: ValueVersionHistoryGetResponseV1 => msg.valueVersions.length should ===(3) + expectMsgPF(timeout) { case msg: ValueVersionHistoryGetResponseV1 => + msg.valueVersions.length should ===(3) } } @@ -1204,10 +1225,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newValue: TextValueSimpleV1, _, newValueIri: IRI, _) => - secondValueIriWithResourceRef.set(newValueIri) - checkTextValue(received = newValue, expected = textValue) + expectMsgPF(timeout) { case ChangeValueResponseV1(newValue: TextValueSimpleV1, _, newValueIri: IRI, _) => + secondValueIriWithResourceRef.set(newValueIri) + checkTextValue(received = newValue, expected = textValue) } // The new version of the LinkValue should be marked as deleted. @@ -1220,8 +1240,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = incunabulaUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } val sparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1238,17 +1258,16 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // The LinkValue should point to its previous version. There should be no direct link. - expectMsgPF(timeout) { - case response: SparqlSelectResult => - standoffLinkValueIri.unset() - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(row => - row - .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean) should ===( - true) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) + expectMsgPF(timeout) { case response: SparqlSelectResult => + standoffLinkValueIri.unset() + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(row => + row + .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean + ) should ===(true) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) } } @@ -1262,8 +1281,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with startPosition = 45, endPosition = 53, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 0 @@ -1282,10 +1304,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => - firstValueIriWithResourceRef.set(newValueIri) - checkTextValue(received = newValue, expected = textValueWithResourceRef) + expectMsgPF(timeout) { case ChangeValueResponseV1(newValue: TextValueWithStandoffV1, _, newValueIri: IRI, _) => + firstValueIriWithResourceRef.set(newValueIri) + checkTextValue(received = newValue, expected = textValueWithResourceRef) } responderManager ! LinkValueGetRequestV1( @@ -1299,17 +1320,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // There should now be a new LinkValue with no previous versions and a reference count of 1, and // there should once again be a direct link. - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = "http://rdfh.ch/0803/21abac2162", - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = zeitglöckleinIri, - referenceCount = 1 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = "http://rdfh.ch/0803/21abac2162", + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = zeitglöckleinIri, + referenceCount = 1 + ) + ) + msg.rights should ===(2) } val sparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1323,12 +1344,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } } @@ -1345,10 +1365,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newValue: IntegerValueV1, _, newValueIri: IRI, _) => - currentSeqnumValueIri.set(newValueIri) - newValue should ===(IntegerValueV1(seqnum)) + expectMsgPF(timeout) { case CreateValueResponseV1(newValue: IntegerValueV1, _, newValueIri: IRI, _) => + currentSeqnumValueIri.set(newValueIri) + newValue should ===(IntegerValueV1(seqnum)) } } @@ -1364,9 +1383,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newValue: IntegerValueV1, _, newValueIri: IRI, _) => - newValue should ===(IntegerValueV1(seqnum)) + expectMsgPF(timeout) { case ChangeValueResponseV1(newValue: IntegerValueV1, _, newValueIri: IRI, _) => + newValue should ===(IntegerValueV1(seqnum)) } } @@ -1383,10 +1401,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newValue: TimeValueV1, _, newValueIri: IRI, _) => - currentTimeValueIri.set(newValueIri) - newValue should ===(TimeValueV1(timeStamp)) + expectMsgPF(timeout) { case CreateValueResponseV1(newValue: TimeValueV1, _, newValueIri: IRI, _) => + currentTimeValueIri.set(newValueIri) + newValue should ===(TimeValueV1(timeStamp)) } } @@ -1402,9 +1419,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newValue: TimeValueV1, _, newValueIri: IRI, _) => - newValue should ===(TimeValueV1(timeStamp)) + expectMsgPF(timeout) { case ChangeValueResponseV1(newValue: TimeValueV1, _, newValueIri: IRI, _) => + newValue should ===(TimeValueV1(timeStamp)) } } @@ -1427,10 +1443,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => - currentPubdateValueIri.set(msg.id) - msg.value should ===(DateValueV1("2000", "2015-01-21", "CE", "CE", KnoraCalendarV1.GREGORIAN)) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + currentPubdateValueIri.set(msg.id) + msg.value should ===(DateValueV1("2000", "2015-01-21", "CE", "CE", KnoraCalendarV1.GREGORIAN)) } } @@ -1450,10 +1465,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: ChangeValueResponseV1 => - currentPubdateValueIri.set(msg.id) - msg.value should ===(DateValueV1("1491-07-28", "1491-07-28", "CE", "CE", KnoraCalendarV1.JULIAN)) + expectMsgPF(timeout) { case msg: ChangeValueResponseV1 => + currentPubdateValueIri.set(msg.id) + msg.value should ===(DateValueV1("1491-07-28", "1491-07-28", "CE", "CE", KnoraCalendarV1.JULIAN)) } } @@ -1475,11 +1489,10 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! createValueRequest - expectMsgPF(timeout) { - case CreateValueResponseV1(linkV1: LinkV1, _, newLinkValueIri: IRI, _) => - linkObjLinkValueIri.set(newLinkValueIri) - linkV1.targetResourceIri should ===(zeitglöckleinIri) - linkV1.valueResourceClass should ===(Some("http://www.knora.org/ontology/0803/incunabula#book")) + expectMsgPF(timeout) { case CreateValueResponseV1(linkV1: LinkV1, _, newLinkValueIri: IRI, _) => + linkObjLinkValueIri.set(newLinkValueIri) + linkV1.targetResourceIri should ===(zeitglöckleinIri) + linkV1.valueResourceClass should ===(Some("http://www.knora.org/ontology/0803/incunabula#book")) } // The new LinkValue should have no previous version, and there should be a direct link between the resources. @@ -1495,12 +1508,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } // Check that the resource's last modification date got updated. @@ -1522,15 +1534,16 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! createValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } "not create a link that points to a resource of the wrong class" in { responderManager ! CreateValueRequestV1( resourceIri = miscResourceIri, - propertyIri = "http://www.knora.org/ontology/0803/incunabula#miscHasBook", // can only point to an incunabula:book + propertyIri = + "http://www.knora.org/ontology/0803/incunabula#miscHasBook", // can only point to an incunabula:book value = LinkUpdateV1( targetResourceIri = "http://rdfh.ch/0803/8a0b1e75" // an incunabula:page, not an incunabula:book ), @@ -1539,8 +1552,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1561,10 +1574,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! changeValueRequest - expectMsgPF(timeout) { - case ChangeValueResponseV1(linkValue: LinkV1, _, newLinkValueIri: IRI, _) => - linkObjLinkValueIri.set(newLinkValueIri) - linkValue.targetResourceIri should ===(linkTargetIri) + expectMsgPF(timeout) { case ChangeValueResponseV1(linkValue: LinkV1, _, newLinkValueIri: IRI, _) => + linkObjLinkValueIri.set(newLinkValueIri) + linkValue.targetResourceIri should ===(linkTargetIri) } // The old LinkValue should be deleted now, and the old direct link should have been removed. @@ -1581,16 +1593,15 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(oldLinkValueSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(row => - row - .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean) should ===( - true) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(row => + row + .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean + ) should ===(true) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) } // The new LinkValue should have no previous version, and there should be a direct link between the resources. @@ -1606,12 +1617,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(newLinkValueSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } // Check that the link source's last modification date got updated. @@ -1635,8 +1645,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: DeleteValueResponseV1 => linkObjLinkValueIri.set(msg.id) + expectMsgPF(timeout) { case msg: DeleteValueResponseV1 => + linkObjLinkValueIri.set(msg.id) } val deletedLinkValueSparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -1651,20 +1661,19 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(deletedLinkValueSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(row => - row - .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean) should ===( - true) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) - rows.exists( - row => - row.rowMap("objPred") == OntologyConstants.KnoraBase.DeleteComment && row - .rowMap("objObj") == comment) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(row => + row + .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean + ) should ===(true) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) + rows.exists(row => + row.rowMap("objPred") == OntologyConstants.KnoraBase.DeleteComment && row + .rowMap("objObj") == comment + ) should ===(true) } // Check that the link source's last modification date got updated. @@ -1693,11 +1702,10 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! changeValueRequest - expectMsgPF(timeout) { - case ChangeValueResponseV1(linkValue: LinkV1, _, newLinkValueIri: IRI, _) => - // save valueIri for next test - partOfLinkValueIri.set(newLinkValueIri) - linkValue.targetResourceIri should ===(linkTargetIri) + expectMsgPF(timeout) { case ChangeValueResponseV1(linkValue: LinkV1, _, newLinkValueIri: IRI, _) => + // save valueIri for next test + partOfLinkValueIri.set(newLinkValueIri) + linkValue.targetResourceIri should ===(linkTargetIri) } } @@ -1721,8 +1729,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! changeValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -1741,10 +1749,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => - msg.value.toString should ===(comment) - msg.comment should ===(Some(metaComment)) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + msg.value.toString should ===(comment) + msg.comment should ===(Some(metaComment)) } } @@ -1763,10 +1770,9 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! changeCommentRequest - expectMsgPF(timeout) { - case msg: ChangeValueResponseV1 => - msg.value should ===(TextValueSimpleV1(utf8str = "Berthold, der Bruder")) - msg.comment should ===(comment) + expectMsgPF(timeout) { case msg: ChangeValueResponseV1 => + msg.value should ===(TextValueSimpleV1(utf8str = "Berthold, der Bruder")) + msg.comment should ===(comment) } // Check that the resource's last modification date got updated. @@ -1796,8 +1802,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with responderManager ! fileChangeRequest - expectMsgPF(timeout) { - case msg: ChangeFileValueResponseV1 => checkImageFileValueChange(msg, fileChangeRequest) + expectMsgPF(timeout) { case msg: ChangeFileValueResponseV1 => + checkImageFileValueChange(msg, fileChangeRequest) } } @@ -1814,9 +1820,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case ChangeValueResponseV1(newListValue: HierarchicalListValueV1, _, _, _) => - newListValue should ===(HierarchicalListValueV1(winter)) + expectMsgPF(timeout) { case ChangeValueResponseV1(newListValue: HierarchicalListValueV1, _, _, _) => + newListValue should ===(HierarchicalListValueV1(winter)) } } @@ -1834,9 +1839,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newListValue: HierarchicalListValueV1, _, _, _) => - newListValue should ===(HierarchicalListValueV1(summer)) + expectMsgPF(timeout) { case CreateValueResponseV1(newListValue: HierarchicalListValueV1, _, _, _) => + newListValue should ===(HierarchicalListValueV1(summer)) } } @@ -1853,15 +1857,16 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newDecimalValue: DecimalValueV1, _, _, _) => - newDecimalValue should ===(decimalValue) + expectMsgPF(timeout) { case CreateValueResponseV1(newDecimalValue: DecimalValueV1, _, _, _) => + newDecimalValue should ===(decimalValue) } } "add an interval value to an anything:Thing" in { - val intervalValue = IntervalValueV1(timeval1 = BigDecimal("1000000000000000.0000000000000001"), - timeval2 = BigDecimal("1000000000000000.0000000000000002")) + val intervalValue = IntervalValueV1( + timeval1 = BigDecimal("1000000000000000.0000000000000001"), + timeval2 = BigDecimal("1000000000000000.0000000000000002") + ) responderManager ! CreateValueRequestV1( value = intervalValue, @@ -1872,9 +1877,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newIntervalValue: IntervalValueV1, _, _, _) => - newIntervalValue should ===(intervalValue) + expectMsgPF(timeout) { case CreateValueResponseV1(newIntervalValue: IntervalValueV1, _, _, _) => + newIntervalValue should ===(intervalValue) } } @@ -1890,9 +1894,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newColorValue: ColorValueV1, _, _, _) => - newColorValue should ===(colorValue) + expectMsgPF(timeout) { case CreateValueResponseV1(newColorValue: ColorValueV1, _, _, _) => + newColorValue should ===(colorValue) } } @@ -1945,9 +1948,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newBooleanValue: BooleanValueV1, _, _, _) => - newBooleanValue should ===(booleanValue) + expectMsgPF(timeout) { case CreateValueResponseV1(newBooleanValue: BooleanValueV1, _, _, _) => + newBooleanValue should ===(booleanValue) } } @@ -1963,9 +1965,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case CreateValueResponseV1(newUriValue: UriValueV1, _, _, _) => - newUriValue should ===(uriValue) + expectMsgPF(timeout) { case CreateValueResponseV1(newUriValue: UriValueV1, _, _, _) => + newUriValue should ===(uriValue) } } @@ -1985,17 +1986,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = anythingUser ) - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = thingWithTextValues, - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = aThingIri, - referenceCount = 2 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = thingWithTextValues, + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = aThingIri, + referenceCount = 2 + ) + ) + msg.rights should ===(2) } val initialLinkValueSparqlQuery = org.knora.webapi.messages.twirl.queries.sparql.v1.txt @@ -2011,12 +2012,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with // It should have no previousValue, and the direct link should exist. - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(false) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } // Now delete the first text value. @@ -2028,8 +2028,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - val deletedFirstTextValue = expectMsgPF(timeout) { - case msg: DeleteValueResponseV1 => msg.id + val deletedFirstTextValue = expectMsgPF(timeout) { case msg: DeleteValueResponseV1 => + msg.id } responderManager ! ValueGetRequestV1( @@ -2038,8 +2038,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = anythingUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } // Check that the resource's last modification date got updated. @@ -2056,17 +2056,17 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = anythingUser ) - expectMsgPF(timeout) { - case msg: ValueGetResponseV1 => - msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) - msg.value should ===( - LinkValueV1( - subjectIri = thingWithTextValues, - predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, - objectIri = aThingIri, - referenceCount = 1 - )) - msg.rights should ===(2) + expectMsgPF(timeout) { case msg: ValueGetResponseV1 => + msg.valuetype should ===(OntologyConstants.KnoraBase.LinkValue) + msg.value should ===( + LinkValueV1( + subjectIri = thingWithTextValues, + predicateIri = OntologyConstants.KnoraBase.HasStandoffLinkTo, + objectIri = aThingIri, + referenceCount = 1 + ) + ) + msg.rights should ===(2) } // It should have a previousValue, and the direct link should still exist. @@ -2082,12 +2082,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(decrementedLinkValueSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(true) } // Now delete the second text value. @@ -2099,8 +2098,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - val deletedSecondTextValue = expectMsgPF(timeout) { - case msg: DeleteValueResponseV1 => msg.id + val deletedSecondTextValue = expectMsgPF(timeout) { case msg: DeleteValueResponseV1 => + msg.id } responderManager ! ValueGetRequestV1( @@ -2109,8 +2108,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = anythingUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } // Check that the resource's last modification date got updated. @@ -2127,8 +2126,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with userProfile = anythingUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } // The LinkValue should point to its previous version. There should be no direct link. @@ -2145,17 +2144,16 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with storeManager ! SparqlSelectRequest(deletedLinkValueSparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - standoffLinkValueIri.unset() - val rows = response.results.bindings - rows.groupBy(_.rowMap("linkValue")).size should ===(1) - rows.exists(row => - row - .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean) should ===( - true) - rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) - rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) + expectMsgPF(timeout) { case response: SparqlSelectResult => + standoffLinkValueIri.unset() + val rows = response.results.bindings + rows.groupBy(_.rowMap("linkValue")).size should ===(1) + rows.exists(row => + row + .rowMap("objPred") == OntologyConstants.KnoraBase.IsDeleted && row.rowMap("objObj").toBoolean + ) should ===(true) + rows.exists(_.rowMap("objPred") == OntologyConstants.KnoraBase.PreviousValue) should ===(true) + rows.head.rowMap.get("directLinkExists").exists(_.toBoolean) should ===(false) } } @@ -2172,8 +2170,11 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with endPosition = 39, startIndex = 0, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = nonexistentIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = nonexistentIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None ) @@ -2192,9 +2193,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -2209,9 +2209,8 @@ class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: CreateValueResponseV1 => - msg.value should ===(TextValueSimpleV1(utf8str = "Hello World!", language = Some("en"))) + expectMsgPF(timeout) { case msg: CreateValueResponseV1 => + msg.value should ===(TextValueSimpleV1(utf8str = "Hello World!", language = Some("en"))) } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2Spec.scala index 3df5fef52b..9c0d4e783e 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2Spec.scala @@ -38,8 +38,8 @@ object ListsResponderV2Spec { } /** - * Tests [[ListsResponderV2]]. - */ + * Tests [[ListsResponderV2]]. + */ class ListsResponderV2Spec extends CoreSpec() with ImplicitSender { import ListsResponderV2Spec._ @@ -66,9 +66,8 @@ class ListsResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = userProfile ) - expectMsgPF(timeout) { - case response: ListGetResponseV2 => - assert(response == listsResponderV2SpecFullData.treeList) + expectMsgPF(timeout) { case response: ListGetResponseV2 => + assert(response == listsResponderV2SpecFullData.treeList) } } @@ -81,9 +80,8 @@ class ListsResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = userProfile ) - expectMsgPF(timeout) { - case response: NodeGetResponseV2 => - assert(response == listsResponderV2SpecFullData.treeNode) + expectMsgPF(timeout) { case response: NodeGetResponseV2 => + assert(response == listsResponderV2SpecFullData.treeNode) } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2SpecFullData.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2SpecFullData.scala index 45707f5215..68b6f97c0e 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2SpecFullData.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ListsResponderV2SpecFullData.scala @@ -27,7 +27,9 @@ class ListsResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { StringLiteralV2( value = "Tree list node 11", language = Some("en") - ))), + ) + ) + ), position = 1, hasRootNode = "http://rdfh.ch/lists/0001/treeList", comments = StringLiteralSequenceV2(Vector.empty[StringLiteralV2]) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/MetadataResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/MetadataResponderV2Spec.scala index d5e51d6c58..0ebf9f14f3 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/MetadataResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/MetadataResponderV2Spec.scala @@ -32,8 +32,8 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import scala.concurrent.duration._ /** - * Tests [[MetadataResponderV2]]. - */ + * Tests [[MetadataResponderV2]]. + */ class MetadataResponderV2Spec extends CoreSpec() with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance private val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(defaultFeatureFactoryConfig) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala index d8b2d1aac9..10ce0551ef 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala @@ -52,12 +52,12 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - private val imagesUser = SharedTestDataADM.imagesUser01 + private val imagesUser = SharedTestDataADM.imagesUser01 private val imagesProjectIri = SharedTestDataADM.IMAGES_PROJECT_IRI.toSmartIri - private val anythingAdminUser = SharedTestDataADM.anythingAdminUser + private val anythingAdminUser = SharedTestDataADM.anythingAdminUser private val anythingNonAdminUser = SharedTestDataADM.anythingUser1 - private val anythingProjectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI.toSmartIri + private val anythingProjectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI.toSmartIri private val exampleSharedOntology = RdfDataObject( path = "test_data/ontologies/example-box.ttl", @@ -75,17 +75,17 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { // The default timeout for receiving reply messages from actors. private val timeout = 10.seconds - private val fooIri = new MutableTestIri + private val fooIri = new MutableTestIri private var fooLastModDate: Instant = Instant.now - private val barIri = new MutableTestIri + private val barIri = new MutableTestIri private var barLastModDate: Instant = Instant.now - private val chairIri = new MutableTestIri + private val chairIri = new MutableTestIri private var chairLastModDate: Instant = Instant.now - private val ExampleSharedOntologyIri = "http://api.knora.org/ontology/shared/example-box/v2".toSmartIri - private val IncunabulaOntologyIri = "http://0.0.0.0:3333/ontology/0803/incunabula/v2".toSmartIri - private val AnythingOntologyIri = "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri + private val ExampleSharedOntologyIri = "http://api.knora.org/ontology/shared/example-box/v2".toSmartIri + private val IncunabulaOntologyIri = "http://0.0.0.0:3333/ontology/0803/incunabula/v2".toSmartIri + private val AnythingOntologyIri = "http://0.0.0.0:3333/ontology/0001/anything/v2".toSmartIri private var anythingLastModDate: Instant = Instant.parse("2017-12-19T15:23:42.166Z") private var freetestLastModData: Instant = Instant.parse("2012-12-12T12:12:12.12Z") @@ -193,7 +193,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } "change both the label and the comment of the 'foo' ontology" in { - val aLabel = "a changed label" + val aLabel = "a changed label" val aComment = "a changed comment" responderManager ! ChangeOntologyMetadataRequestV2( @@ -444,9 +444,9 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { cause.isInstanceOf[BadRequestException] should ===(true) val expectedSubjects = Set( - "", // rdf:type anything:Thing - "", // rdf:type anything:BlueThing, a subclass of anything:Thing - "", // a subclass of anything:Thing in another ontology + "", // rdf:type anything:Thing + "", // rdf:type anything:BlueThing, a subclass of anything:Thing + "", // a subclass of anything:Thing in another ontology "" // a subproperty of anything:hasOtherThing in another ontology ) @@ -776,7 +776,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val externalOntology = msg.toOntologySchema(ApiV2Complex) - val property = externalOntology.properties(propertyIri) + val property = externalOntology.properties(propertyIri) property.entityInfoContent should ===(propertyInfoContent) val metadata = externalOntology.ontologyMetadata val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( @@ -870,7 +870,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val externalOntology = msg.toOntologySchema(ApiV2Complex) - val property = externalOntology.properties(propertyIri) + val property = externalOntology.properties(propertyIri) assert(property.isLinkProp) assert(!property.isLinkValueProp) externalOntology.properties(propertyIri).entityInfoContent should ===(propertyInfoContent) @@ -3284,7 +3284,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { .get(stringFormatter.toSmartIri(OntologyConstants.SalsahGui.GuiElementProp)) match { case Some(predicateInfo) => val guiElementTypeFromMessage = predicateInfo.objects.head.asInstanceOf[SmartIriLiteralV2] - val guiElementTypeInternal = guiElementTypeFromMessage.toOntologySchema(InternalSchema) + val guiElementTypeInternal = guiElementTypeFromMessage.toOntologySchema(InternalSchema) guiElementTypeFromMessage should equal(guiElementTypeInternal) } @@ -3598,7 +3598,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } "create a link property, anything:hasOtherNothing, and add a cardinality for it to the class anything:Nothing" in { - val classIri = AnythingOntologyIri.makeEntityIri("Nothing") + val classIri = AnythingOntologyIri.makeEntityIri("Nothing") val propertyIri = AnythingOntologyIri.makeEntityIri("hasOtherNothing") val propertyInfoContent = PropertyInfoContentV2( @@ -3643,7 +3643,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgPF(timeout) { case msg: ReadOntologyV2 => val externalOntology = msg.toOntologySchema(ApiV2Complex) - val metadata = externalOntology.ontologyMetadata + val metadata = externalOntology.ontologyMetadata val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( throw AssertionException(s"${metadata.ontologyIri} has no last modification date") ) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index 52026e4ba9..ba9f3e3944 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -370,7 +370,8 @@ class GraphTestData { target = "http://rdfh.ch/0001/a-thing", propertyIri = "http://www.knora.org/ontology/knora-base#hasStandoffLinkTo".toSmartIri, source = "http://rdfh.ch/0001/a-thing-with-text-values" - )), + ) + ), nodes = Vector( GraphNodeV2( resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, @@ -393,14 +394,15 @@ class GraphTestData { resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, resourceLabel = "Another thing", resourceIri = "http://rdfh.ch/0001/another-thing" - )), + ) + ), ontologySchema = InternalSchema ) } /** - * Tests [[ResourcesResponderV2]]. - */ + * Tests [[ResourcesResponderV2]]. + */ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { import ResourcesResponderV2Spec._ @@ -415,7 +417,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { /* we need to run our app with the mocked sipi actor */ override lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with ManagersWithMockedSipi).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + name = APPLICATION_MANAGER_ACTOR_NAME + ) override lazy val rdfDataObjects = List( RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), @@ -489,17 +492,19 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => response.toResource(resourceIri).toOntologySchema(ApiV2Complex) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + response.toResource(resourceIri).toOntologySchema(ApiV2Complex) } } - private def checkCreateResource(inputResourceIri: IRI, - inputResource: CreateResourceV2, - outputResource: ReadResourceV2, - defaultResourcePermissions: String, - defaultValuePermissions: String, - requestingUser: UserADM): Unit = { + private def checkCreateResource( + inputResourceIri: IRI, + inputResource: CreateResourceV2, + outputResource: ReadResourceV2, + defaultResourcePermissions: String, + defaultValuePermissions: String, + requestingUser: UserADM + ): Unit = { assert(outputResource.resourceIri == inputResourceIri) assert(outputResource.resourceClassIri == inputResource.resourceClassIri) assert(outputResource.label == inputResource.label) @@ -511,18 +516,17 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { assert(outputResource.values.keySet == inputResource.values.keySet) - inputResource.values.foreach { - case (propertyIri: SmartIri, propertyInputValues: Seq[CreateValueInNewResourceV2]) => - val propertyOutputValues = outputResource.values(propertyIri) + inputResource.values.foreach { case (propertyIri: SmartIri, propertyInputValues: Seq[CreateValueInNewResourceV2]) => + val propertyOutputValues = outputResource.values(propertyIri) - assert(propertyOutputValues.size == propertyInputValues.size) + assert(propertyOutputValues.size == propertyInputValues.size) - propertyInputValues.zip(propertyOutputValues).foreach { - case (inputValue: CreateValueInNewResourceV2, outputValue: ReadValueV2) => - val expectedPermissions = inputValue.permissions.getOrElse(defaultValuePermissions) - assert(outputValue.permissions == expectedPermissions) - assert(inputValue.valueContent.wouldDuplicateCurrentVersion(outputValue.valueContent)) - } + propertyInputValues.zip(propertyOutputValues).foreach { + case (inputValue: CreateValueInNewResourceV2, outputValue: ReadValueV2) => + val expectedPermissions = inputValue.permissions.getOrElse(defaultValuePermissions) + assert(outputValue.permissions == expectedPermissions) + assert(inputValue.valueContent.wouldDuplicateCurrentVersion(outputValue.valueContent)) + } } } @@ -537,11 +541,10 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case sparqlSelectResponse: SparqlSelectResult => - sparqlSelectResponse.results.bindings.map { row => - row.rowMap("standoffTag") - }.toSet + expectMsgPF(timeout) { case sparqlSelectResponse: SparqlSelectResult => + sparqlSelectResponse.results.bindings.map { row => + row.rowMap("standoffTag") + }.toSet } } @@ -555,14 +558,13 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case sparqlSelectResponse: SparqlSelectResult => - val savedDeleteDateStr = sparqlSelectResponse.getFirstRow.rowMap("deleteDate") + expectMsgPF(timeout) { case sparqlSelectResponse: SparqlSelectResult => + val savedDeleteDateStr = sparqlSelectResponse.getFirstRow.rowMap("deleteDate") - stringFormatter.xsdDateTimeStampToInstant( - savedDeleteDateStr, - throw AssertionException(s"Couldn't parse delete date from triplestore: $savedDeleteDateStr") - ) + stringFormatter.xsdDateTimeStampToInstant( + savedDeleteDateStr, + throw AssertionException(s"Couldn't parse delete date from triplestore: $savedDeleteDateStr") + ) } } @@ -576,9 +578,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = KnoraSystemInstances.Users.SystemUser ) - expectMsgPF(timeout) { - case mappingResponse: GetMappingResponseV2 => - standardMapping = Some(mappingResponse.mapping) + expectMsgPF(timeout) { case mappingResponse: GetMappingResponseV2 => + standardMapping = Some(mappingResponse.mapping) } } @@ -593,11 +594,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = incunabulaUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForZeitgloecklein, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForZeitgloecklein, + received = response + ) } } @@ -611,11 +612,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = incunabulaUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedPreviewResourceResponseForZeitgloecklein, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedPreviewResourceResponseForZeitgloecklein, + received = response + ) } } @@ -630,11 +631,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = incunabulaUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForReise, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForReise, + received = response + ) } } @@ -649,11 +650,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = incunabulaUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForZeitgloeckleinAndReise, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForZeitgloeckleinAndReise, + received = response + ) } } @@ -667,11 +668,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = incunabulaUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedPreviewResourceResponseForZeitgloeckleinAndReise, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedPreviewResourceResponseForZeitgloeckleinAndReise, + received = response + ) } } @@ -686,12 +687,12 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = incunabulaUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = - resourcesResponderV2SpecFullData.expectedFullResourceResponseForReiseAndZeitgloeckleinInversedOrder, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = + resourcesResponderV2SpecFullData.expectedFullResourceResponseForReiseAndZeitgloeckleinInversedOrder, + received = response + ) } } @@ -708,11 +709,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { ) // the redundant Iri should be ignored (distinct) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForZeitgloeckleinAndReise, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForZeitgloeckleinAndReise, + received = response + ) } } @@ -729,16 +730,15 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ResourceTEIGetResponseV2 => - val expectedBody = - """

This is a test that contains marked up elements. This is interesting text in italics. This is boring text in italics.

""".stripMargin + expectMsgPF(timeout) { case response: ResourceTEIGetResponseV2 => + val expectedBody = + """

This is a test that contains marked up elements. This is interesting text in italics. This is boring text in italics.

""".stripMargin - // Compare the original XML with the regenerated XML. - val xmlDiff: Diff = - DiffBuilder.compare(Input.fromString(response.body.toXML)).withTest(Input.fromString(expectedBody)).build() + // Compare the original XML with the regenerated XML. + val xmlDiff: Diff = + DiffBuilder.compare(Input.fromString(response.body.toXML)).withTest(Input.fromString(expectedBody)).build() - xmlDiff.hasDifferences should be(false) + xmlDiff.hasDifferences should be(false) } } @@ -755,16 +755,15 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ResourceTEIGetResponseV2 => - val expectedBody = - """

Something with a lot of different markup. And more markup.

""".stripMargin + expectMsgPF(timeout) { case response: ResourceTEIGetResponseV2 => + val expectedBody = + """

Something with a lot of different markup. And more markup.

""".stripMargin - // Compare the original XML with the regenerated XML. - val xmlDiff: Diff = - DiffBuilder.compare(Input.fromString(response.body.toXML)).withTest(Input.fromString(expectedBody)).build() + // Compare the original XML with the regenerated XML. + val xmlDiff: Diff = + DiffBuilder.compare(Input.fromString(response.body.toXML)).withTest(Input.fromString(expectedBody)).build() - xmlDiff.hasDifferences should be(false) + xmlDiff.hasDifferences should be(false) } } @@ -781,11 +780,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForThingWithHistory, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResourceResponseForThingWithHistory, + received = response + ) } } @@ -801,9 +800,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ResourceVersionHistoryResponseV2 => - assert(response == resourcesResponderV2SpecFullData.expectedCompleteVersionHistoryResponse) + expectMsgPF(timeout) { case response: ResourceVersionHistoryResponseV2 => + assert(response == resourcesResponderV2SpecFullData.expectedCompleteVersionHistoryResponse) } } @@ -820,9 +818,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ResourceVersionHistoryResponseV2 => - assert(response == resourcesResponderV2SpecFullData.expectedPartialVersionHistoryResponse) + expectMsgPF(timeout) { case response: ResourceVersionHistoryResponseV2 => + assert(response == resourcesResponderV2SpecFullData.expectedPartialVersionHistoryResponse) } } @@ -835,11 +832,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = resourcesResponderV2SpecFullData.expectedFullResponseResponseForThingWithValueByUuid, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResponseResponseForThingWithValueByUuid, + received = response + ) } } @@ -853,12 +850,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = anythingUserProfile ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = - resourcesResponderV2SpecFullData.expectedFullResponseResponseForThingWithValueByUuidAndVersionDate, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = resourcesResponderV2SpecFullData.expectedFullResponseResponseForThingWithValueByUuidAndVersionDate, + received = response + ) } } @@ -908,8 +904,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: GraphDataGetResponseV2 => response should ===(graphTestData.graphWithStandoffLink) + expectMsgPF(timeout) { case response: GraphDataGetResponseV2 => + response should ===(graphTestData.graphWithStandoffLink) } } @@ -923,8 +919,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: GraphDataGetResponseV2 => response should ===(graphTestData.graphWithOneNode) + expectMsgPF(timeout) { case response: GraphDataGetResponseV2 => + response should ===(graphTestData.graphWithOneNode) } } @@ -950,18 +946,17 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { // Check that the response contains the correct metadata. - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - val outputResource: ReadResourceV2 = response.toResource(resourceIri).toOntologySchema(ApiV2Complex) - - checkCreateResource( - inputResourceIri = resourceIri, - inputResource = inputResource, - outputResource = outputResource, - defaultResourcePermissions = defaultAnythingResourcePermissions, - defaultValuePermissions = defaultAnythingValuePermissions, - requestingUser = anythingUserProfile - ) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + val outputResource: ReadResourceV2 = response.toResource(resourceIri).toOntologySchema(ApiV2Complex) + + checkCreateResource( + inputResourceIri = resourceIri, + inputResource = inputResource, + outputResource = outputResource, + defaultResourcePermissions = defaultAnythingResourcePermissions, + defaultValuePermissions = defaultAnythingValuePermissions, + requestingUser = anythingUserProfile + ) } // Get the resource from the triplestore and check it again. @@ -1247,8 +1242,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1295,8 +1290,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1337,8 +1332,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1383,8 +1378,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[DuplicateValueException] should ===(true) } } @@ -1417,8 +1412,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -1451,8 +1446,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -1477,8 +1472,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { originalXMLID = None, startIndex = 1, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = "http://rdfh.ch/0001/nonexistent-thing")), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = "http://rdfh.ch/0001/nonexistent-thing" + ) + ), startParentIndex = Some(0) ), StandoffTagV2( @@ -1521,8 +1519,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -1555,8 +1553,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -1589,8 +1587,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1623,8 +1621,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[OntologyConstraintException] should ===(true) } } @@ -1647,8 +1645,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1683,8 +1681,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1706,8 +1704,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1723,8 +1721,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -1740,8 +1738,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1770,7 +1768,9 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { assert(outputResource.label == newLabel) assert( PermissionUtilADM.parsePermissions(outputResource.permissions) == PermissionUtilADM.parsePermissions( - newPermissions)) + newPermissions + ) + ) aThingLastModificationDate = outputResource.lastModificationDate.get assert(aThingLastModificationDate.isAfter(dateTimeStampBeforeUpdate)) } @@ -1787,8 +1787,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[EditConflictException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[EditConflictException] should ===(true) } } @@ -1805,8 +1805,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[EditConflictException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[EditConflictException] should ===(true) } } @@ -1849,8 +1849,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1895,8 +1895,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! deleteRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[BadRequestException] should ===(true) } } @@ -1924,8 +1924,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } @@ -1957,8 +1957,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } val savedDeleteDate: Instant = getDeleteDate(resourceIri) @@ -1984,9 +1984,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2065,9 +2064,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2207,9 +2205,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - secondValueIriToErase.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + secondValueIriToErase.set(updateValueResponse.valueIri) } val updatedResource = getResource(resourceIri = resourceIri, requestingUser = anythingUserProfile) @@ -2246,10 +2243,9 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! eraseRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - // println(msg.cause) - msg.cause.isInstanceOf[ForbiddenException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + // println(msg.cause) + msg.cause.isInstanceOf[ForbiddenException] should ===(true) } } @@ -2305,10 +2301,9 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! eraseRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - // println(msg.cause) - msg.cause.isInstanceOf[BadRequestException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + // println(msg.cause) + msg.cause.isInstanceOf[BadRequestException] should ===(true) } // Delete the link. @@ -2361,9 +2356,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { storeManager ! SparqlAskRequest(sparqlQuery) - expectMsgPF(timeout) { - case entityExistsResponse: SparqlAskResponse => - entityExistsResponse.result should be(false) + expectMsgPF(timeout) { case entityExistsResponse: SparqlAskResponse => + entityExistsResponse.result should be(false) } } @@ -2379,9 +2373,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { storeManager ! SparqlSelectRequest(isEntityUsedSparql) - expectMsgPF(timeout) { - case entityUsedResponse: SparqlSelectResult => - assert(entityUsedResponse.results.bindings.isEmpty, s"Link value was not erased") + expectMsgPF(timeout) { case entityUsedResponse: SparqlSelectResult => + assert(entityUsedResponse.results.bindings.isEmpty, s"Link value was not erased") } } } @@ -2409,18 +2402,17 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { // Check that the response contains the correct metadata. - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - val outputResource: ReadResourceV2 = response.toResource(resourceIri).toOntologySchema(ApiV2Complex) - - checkCreateResource( - inputResourceIri = resourceIri, - inputResource = inputResource, - outputResource = outputResource, - defaultResourcePermissions = defaultAnythingResourcePermissions, - defaultValuePermissions = defaultAnythingValuePermissions, - requestingUser = anythingUserProfile - ) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + val outputResource: ReadResourceV2 = response.toResource(resourceIri).toOntologySchema(ApiV2Complex) + + checkCreateResource( + inputResourceIri = resourceIri, + inputResource = inputResource, + outputResource = outputResource, + defaultResourcePermissions = defaultAnythingResourcePermissions, + defaultValuePermissions = defaultAnythingValuePermissions, + requestingUser = anythingUserProfile + ) } // Get the resource from the triplestore and check it again. @@ -2597,11 +2589,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(11) val createValueEvent: Option[ResourceAndValueHistoryEvent] = - events.find( - event => - event.eventType == ResourceAndValueEventsUtil.CREATE_VALUE_EVENT && event.eventBody - .asInstanceOf[ValueEventBody] - .valueIri == newValueIri) + events.find(event => + event.eventType == ResourceAndValueEventsUtil.CREATE_VALUE_EVENT && event.eventBody + .asInstanceOf[ValueEventBody] + .valueIri == newValueIri + ) assert(createValueEvent.isDefined) val createValuePayloadContent = createValueEvent.get.eventBody .asInstanceOf[ValueEventBody] @@ -2641,11 +2633,11 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(12) val deleteValueEvent: Option[ResourceAndValueHistoryEvent] = - events.find( - event => - event.eventType == ResourceAndValueEventsUtil.DELETE_VALUE_EVENT && event.eventBody - .asInstanceOf[ValueEventBody] - .valueIri == valueToDelete) + events.find(event => + event.eventType == ResourceAndValueEventsUtil.DELETE_VALUE_EVENT && event.eventBody + .asInstanceOf[ValueEventBody] + .valueIri == valueToDelete + ) assert(deleteValueEvent.isDefined) } @@ -2703,8 +2695,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.anythingAdminUser ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + msg.cause.isInstanceOf[NotFoundException] should ===(true) } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2SpecFullData.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2SpecFullData.scala index 668a5ba4b3..c4f2c1f440 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2SpecFullData.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2SpecFullData.scala @@ -44,7 +44,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#citation".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -119,7 +120,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#hasAuthor".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -137,7 +139,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#url".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -157,7 +160,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#location".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -176,7 +180,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#publoc".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -195,7 +200,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#pubdate".toSmartIri -> Vector( ReadOtherValueV2( valueContent = DateValueContentV2( @@ -216,7 +222,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -235,7 +242,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )) + ) + ) ), lastModificationDate = None, deletionInfo = None @@ -287,7 +295,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#citation".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -470,7 +479,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#hasAuthor".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -488,7 +498,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#book_comment".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -507,7 +518,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#url".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -527,7 +539,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#note".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -607,7 +620,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter valueHasLanguage = None, comment = None, maybeValueHasString = Some( - "Zusammengebunden mit: Die zehen Gebote ; Was und wie man beten soll und Auslegung des hlg. Pater nosters / Hans von Warmont. Strassburg, 1516") + "Zusammengebunden mit: Die zehen Gebote ; Was und wie man beten soll und Auslegung des hlg. Pater nosters / Hans von Warmont. Strassburg, 1516" + ) ), valueHasMaxStandoffStartIndex = None, valueIri = "http://rdfh.ch/0803/2a6221216701/values/b82048bf9305", @@ -639,7 +653,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#publoc".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -658,7 +673,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#pubdate".toSmartIri -> Vector( ReadOtherValueV2( valueContent = DateValueContentV2( @@ -679,7 +695,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -758,15 +775,15 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter ) val expectedFullResourceResponseForZeitgloecklein: ReadResourcesSequenceV2 = ReadResourcesSequenceV2( - resources = Vector(expectedReadResourceV2ForZeitgloecklein), + resources = Vector(expectedReadResourceV2ForZeitgloecklein) ) val expectedPreviewResourceResponseForZeitgloecklein: ReadResourcesSequenceV2 = ReadResourcesSequenceV2( - resources = Vector(expectedReadResourceV2ForZeitgloeckleinPreview), + resources = Vector(expectedReadResourceV2ForZeitgloeckleinPreview) ) val expectedFullResourceResponseForReise: ReadResourcesSequenceV2 = ReadResourcesSequenceV2( - resources = Vector(expectedReadResourceV2ForReiseInsHeiligeland), + resources = Vector(expectedReadResourceV2ForReiseInsHeiligeland) ) val expectedFullResourceResponseForThingWithHistory: ReadResourcesSequenceV2 = ReadResourcesSequenceV2( @@ -798,7 +815,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", previousValueIri = Some("http://rdfh.ch/0001/thing-with-history/values/2a"), deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0001/anything#hasOtherThingValue".toSmartIri -> Vector( ReadLinkValueV2( valueContent = LinkValueContentV2( @@ -816,7 +834,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( ReadOtherValueV2( valueContent = IntegerValueContentV2( @@ -832,7 +851,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", previousValueIri = None, deletionInfo = None - )) + ) + ) ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = Some(Instant.parse("2019-02-13T09:05:10Z")), @@ -879,7 +899,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter versionDate = Instant.parse("2019-02-08T15:05:10Z"), author = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" ) - )) + ) + ) val expectedPartialVersionHistoryResponse: ResourceVersionHistoryResponseV2 = ResourceVersionHistoryResponseV2( history = Vector( @@ -911,7 +932,8 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter versionDate = Instant.parse("2019-02-10T10:05:10Z"), author = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ" ) - )) + ) + ) val expectedFullResourceResponseForZeitgloeckleinAndReise: ReadResourcesSequenceV2 = ReadResourcesSequenceV2( resources = Vector(expectedReadResourceV2ForZeitgloecklein, expectedReadResourceV2ForReiseInsHeiligeland) @@ -939,26 +961,29 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter creationDate = Instant.parse("2019-02-08T15:05:10Z"), userPermission = ModifyPermission, values = Map( - "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector(ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 3, - comment = None - ), - valueIri = "http://rdfh.ch/0001/thing-with-history/values/1c", - permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser", - valueCreationDate = Instant.parse("2019-02-13T09:05:10Z"), - attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", - valueHasUUID = stringFormatter.decodeUuid("pLlW4ODASumZfZFbJdpw1g"), - userPermission = ChangeRightsPermission, - previousValueIri = Some("http://rdfh.ch/0001/thing-with-history/values/1b"), - deletionInfo = None - )) + "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 3, + comment = None + ), + valueIri = "http://rdfh.ch/0001/thing-with-history/values/1c", + permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser", + valueCreationDate = Instant.parse("2019-02-13T09:05:10Z"), + attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", + valueHasUUID = stringFormatter.decodeUuid("pLlW4ODASumZfZFbJdpw1g"), + userPermission = ChangeRightsPermission, + previousValueIri = Some("http://rdfh.ch/0001/thing-with-history/values/1b"), + deletionInfo = None + ) + ) ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = Some(Instant.parse("2019-02-13T09:05:10Z")), deletionInfo = None - )) + ) + ) ) val expectedFullResponseResponseForThingWithValueByUuidAndVersionDate: ReadResourcesSequenceV2 = @@ -974,25 +999,28 @@ class ResourcesResponderV2SpecFullData(implicit stringFormatter: StringFormatter creationDate = Instant.parse("2019-02-08T15:05:10Z"), userPermission = ModifyPermission, values = Map( - "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector(ReadOtherValueV2( - valueContent = IntegerValueContentV2( - ontologySchema = InternalSchema, - valueHasInteger = 2, - comment = None - ), - valueIri = "http://rdfh.ch/0001/thing-with-history/values/1b", - permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser", - valueCreationDate = Instant.parse("2019-02-12T09:05:10Z"), - attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", - valueHasUUID = stringFormatter.decodeUuid("pLlW4ODASumZfZFbJdpw1g"), - userPermission = ChangeRightsPermission, - previousValueIri = Some("http://rdfh.ch/0001/thing-with-history/values/1a"), - deletionInfo = None - )) + "http://www.knora.org/ontology/0001/anything#hasInteger".toSmartIri -> Vector( + ReadOtherValueV2( + valueContent = IntegerValueContentV2( + ontologySchema = InternalSchema, + valueHasInteger = 2, + comment = None + ), + valueIri = "http://rdfh.ch/0001/thing-with-history/values/1b", + permissions = "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser", + valueCreationDate = Instant.parse("2019-02-12T09:05:10Z"), + attachedToUser = "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", + valueHasUUID = stringFormatter.decodeUuid("pLlW4ODASumZfZFbJdpw1g"), + userPermission = ChangeRightsPermission, + previousValueIri = Some("http://rdfh.ch/0001/thing-with-history/values/1a"), + deletionInfo = None + ) + ) ), projectADM = SharedTestDataADM.anythingProject, lastModificationDate = Some(Instant.parse("2019-02-13T09:05:10Z")), deletionInfo = None - )) + ) + ) ) } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2.scala index 621fe463c8..54e3d67728 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2.scala @@ -26,13 +26,15 @@ import org.knora.webapi.messages.v2.responder.valuemessages._ object ResourcesResponseCheckerV2 { /** - * Compares the response to a full resource request with the expected response. - * - * @param received the response returned by the resource responder. - * @param expected the expected response. - */ - def compareReadResourcesSequenceV2Response(expected: ReadResourcesSequenceV2, - received: ReadResourcesSequenceV2): Unit = { + * Compares the response to a full resource request with the expected response. + * + * @param received the response returned by the resource responder. + * @param expected the expected response. + */ + def compareReadResourcesSequenceV2Response( + expected: ReadResourcesSequenceV2, + received: ReadResourcesSequenceV2 + ): Unit = { assert(expected.resources.size == received.resources.size, "number of resources is not equal") assert(expected.resources.size == received.resources.size, "number of resources are not equal") @@ -55,8 +57,10 @@ object ResourcesResponseCheckerV2 { // compare the properties // convert Map to a sequence of tuples and sort by property Iri) expectedResource.values.toSeq.sortBy(_._1).zip(receivedResource.values.toSeq.sortBy(_._1)).foreach { - case ((expectedPropIri: SmartIri, expectedPropValues: Seq[ReadValueV2]), - (receivedPropIri: SmartIri, receivedPropValues: Seq[ReadValueV2])) => + case ( + (expectedPropIri: SmartIri, expectedPropValues: Seq[ReadValueV2]), + (receivedPropIri: SmartIri, receivedPropValues: Seq[ReadValueV2]) + ) => assert(expectedPropIri == receivedPropIri) // this check is necessary because zip returns a sequence of the length of the smaller of the two lists to be combined. diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2SpecFullData.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2SpecFullData.scala index eb0d00dce6..666fa08fb1 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2SpecFullData.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponseCheckerV2SpecFullData.scala @@ -44,7 +44,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#citation".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -227,7 +228,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#hasAuthor".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -245,7 +247,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#book_comment".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -264,7 +267,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#url".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -284,7 +288,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#note".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -364,7 +369,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor valueHasLanguage = None, comment = None, maybeValueHasString = Some( - "Zusammengebunden mit: Die zehen Gebote ; Was und wie man beten soll und Auslegung des hlg. Pater nosters / Hans von Warmont. Strassburg, 1516") + "Zusammengebunden mit: Die zehen Gebote ; Was und wie man beten soll und Auslegung des hlg. Pater nosters / Hans von Warmont. Strassburg, 1516" + ) ), valueHasMaxStandoffStartIndex = None, valueIri = "http://rdfh.ch/2a6221216701/values/b82048bf9305", @@ -396,7 +402,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#publoc".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( @@ -415,7 +422,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#pubdate".toSmartIri -> Vector( ReadOtherValueV2( valueContent = DateValueContentV2( @@ -436,7 +444,8 @@ class ResourcesResponseCheckerV2SpecFullData(implicit stringFormatter: StringFor attachedToUser = "http://rdfh.ch/users/91e19f1e01", previousValueIri = None, deletionInfo = None - )), + ) + ), "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( ReadTextValueV2( valueContent = TextValueContentV2( diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2Spec.scala index 2d45dc90f4..78249cf813 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2Spec.scala @@ -33,8 +33,8 @@ import org.knora.webapi.{ApiV2Complex, CoreSpec, SchemaOptions} import scala.concurrent.duration._ /** - * Tests [[SearchResponderV2]]. - */ + * Tests [[SearchResponderV2]]. + */ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -66,12 +66,11 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anonymousUser ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response( - expected = searchResponderV2SpecFullData.fulltextSearchForNarr, - received = response - ) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = searchResponderV2SpecFullData.fulltextSearchForNarr, + received = response + ) } } @@ -90,10 +89,11 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response(expected = searchResponderV2SpecFullData.fulltextSearchForDinge, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = searchResponderV2SpecFullData.fulltextSearchForDinge, + received = response + ) } } @@ -113,17 +113,16 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anythingUser1 ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - val hasImageFileValues: Boolean = - response.resources.flatMap(_.values.values.flatten).exists { readValueV2: ReadValueV2 => - readValueV2.valueContent match { - case _: StillImageFileValueContentV2 => true - case _ => false - } + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + val hasImageFileValues: Boolean = + response.resources.flatMap(_.values.values.flatten).exists { readValueV2: ReadValueV2 => + readValueV2.valueContent match { + case _: StillImageFileValueContentV2 => true + case _ => false } + } - assert(hasImageFileValues) + assert(hasImageFileValues) } } @@ -139,11 +138,11 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { ) // extended search sort by resource Iri by default if no order criterion is indicated - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - compareReadResourcesSequenceV2Response(expected = - searchResponderV2SpecFullData.booksWithTitleZeitgloeckleinResponse, - received = response) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + compareReadResourcesSequenceV2Response( + expected = searchResponderV2SpecFullData.booksWithTitleZeitgloeckleinResponse, + received = response + ) } } @@ -159,10 +158,9 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { ) // extended search sort by resource Iri by default if no order criterion is indicated - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - // TODO: do better testing once JSON-LD can be converted back into case classes - assert(response.resources.size == 18, s"18 books were expected, but ${response.resources.size} given.") + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + // TODO: do better testing once JSON-LD can be converted back into case classes + assert(response.resources.size == 18, s"18 books were expected, but ${response.resources.size} given.") } } @@ -179,9 +177,8 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anonymousUser ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - assert(response.resources.size == 3, s"3 results were expected, but ${response.resources.size} given") + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + assert(response.resources.size == 3, s"3 results were expected, but ${response.resources.size} given") } } @@ -198,9 +195,8 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anonymousUser ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => - assert(response.resources.size == 3, s"3 results were expected, but ${response.resources.size} given") + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + assert(response.resources.size == 3, s"3 results were expected, but ${response.resources.size} given") } } @@ -215,9 +211,8 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anonymousUser ) - expectMsgPF(timeout) { - case response: ResourceCountV2 => - assert(response.numberOfResources == 3, s"3 results were expected, but ${response.numberOfResources} given") + expectMsgPF(timeout) { case response: ResourceCountV2 => + assert(response.numberOfResources == 3, s"3 results were expected, but ${response.numberOfResources} given") } } @@ -232,9 +227,8 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.anonymousUser ) - expectMsgPF(timeout) { - case response: ResourceCountV2 => - assert(response.numberOfResources == 3, s"3 results were expected, but ${response.numberOfResources} given") + expectMsgPF(timeout) { case response: ResourceCountV2 => + assert(response.numberOfResources == 3, s"3 results were expected, but ${response.numberOfResources} given") } } @@ -251,8 +245,8 @@ class SearchResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = SharedTestDataADM.incunabulaProjectAdminUser ) - expectMsgPF(timeout) { - case response: ReadResourcesSequenceV2 => response.resources.size should ===(19) + expectMsgPF(timeout) { case response: ReadResourcesSequenceV2 => + response.resources.size should ===(19) } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2SpecFullData.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2SpecFullData.scala index 267702fcbc..a41643e42d 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2SpecFullData.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/SearchResponderV2SpecFullData.scala @@ -31,25 +31,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:46Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 105.\nHolzschnitt identisch mit Kap. 95: In einer Landschaft fasst ein Narr, der ein Zepter in der Linken h\u00E4lt, einem Mann an die Schulter und redet auf ihn ein, er m\u00F6ge die Feiertage missachten, 11.7 x 8.6 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/00505cf0a803/values/549527258a26", - valueHasUUID = stringFormatter.decodeUuid("549527258a26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:46Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 105.\nHolzschnitt identisch mit Kap. 95: In einer Landschaft fasst ein Narr, der ein Zepter in der Linken h\u00E4lt, einem Mann an die Schulter und redet auf ihn ein, er m\u00F6ge die Feiertage missachten, 11.7 x 8.6 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/00505cf0a803/values/549527258a26", + valueHasUUID = stringFormatter.decodeUuid("549527258a26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:46Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -65,25 +69,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:40Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 21.\nHolzschnitt zu Kap. 21: Andere tadeln und selbst unrecht handeln.\nEin Narr, der mit seinen Beinen im Sumpf steckt, zeigt auf einen nahen Weg, an dem ein Bildstock die Richtung weist.\n11.7 x 8.5 cm.\nUnkoloriert.\n") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/00c650d23303/values/af68552c3626", - valueHasUUID = stringFormatter.decodeUuid("af68552c3626"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 21.\nHolzschnitt zu Kap. 21: Andere tadeln und selbst unrecht handeln.\nEin Narr, der mit seinen Beinen im Sumpf steckt, zeigt auf einen nahen Weg, an dem ein Bildstock die Richtung weist.\n11.7 x 8.5 cm.\nUnkoloriert.\n" + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/00c650d23303/values/af68552c3626", + valueHasUUID = stringFormatter.decodeUuid("af68552c3626"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -99,25 +107,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:49Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 99.\nHolzschnitt zu Kap. 99: Von der Einbusse des christlichen Reiches\nAuf einem Hof kniet ein Narr vor den Vertretern der kirchlichen und weltlichen Obrigkeit, die vor ein Portal getreten sind, und bittet darum, sie m\u00F6gen die Narrenkappe verschm\u00E4hen. Im Hintergrund kommentieren zwei weitere Narren \u00FCber die Hofmauer hinweg das Geschehen mit ungl\u00E4ubigen Gesten, 11.7 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/02abe871e903/values/1852a8aa8526", - valueHasUUID = stringFormatter.decodeUuid("1852a8aa8526"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:49Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 99.\nHolzschnitt zu Kap. 99: Von der Einbusse des christlichen Reiches\nAuf einem Hof kniet ein Narr vor den Vertretern der kirchlichen und weltlichen Obrigkeit, die vor ein Portal getreten sind, und bittet darum, sie m\u00F6gen die Narrenkappe verschm\u00E4hen. Im Hintergrund kommentieren zwei weitere Narren \u00FCber die Hofmauer hinweg das Geschehen mit ungl\u00E4ubigen Gesten, 11.7 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/02abe871e903/values/1852a8aa8526", + valueHasUUID = stringFormatter.decodeUuid("1852a8aa8526"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:49Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -133,25 +145,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:50Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 109.\nHolzschnitt zu Kap. 109: Von Verachtung des Ungl\u00FCcks\nEin Narr hat sich in einem Boot zu weit vom Ufer entfernt. Nun birst der Schiffsrumpf, das Segel flattert haltlos umher. Der Narr h\u00E4lt sich an einem Seil der Takelage fest, 11.6 x 8.4 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/04416f64ef03/values/6ce3c0ef8b26", - valueHasUUID = stringFormatter.decodeUuid("6ce3c0ef8b26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:50Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 109.\nHolzschnitt zu Kap. 109: Von Verachtung des Ungl\u00FCcks\nEin Narr hat sich in einem Boot zu weit vom Ufer entfernt. Nun birst der Schiffsrumpf, das Segel flattert haltlos umher. Der Narr h\u00E4lt sich an einem Seil der Takelage fest, 11.6 x 8.4 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/04416f64ef03/values/6ce3c0ef8b26", + valueHasUUID = stringFormatter.decodeUuid("6ce3c0ef8b26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:50Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -167,25 +183,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:40Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 44.\nHolzschnitt zu Kap. 44: Vom L\u00E4rmen in der Kirche\nEin junger Narr in edler Kleidung, der einen Jagdfalken auf dem Arm h\u00E4lt, von Hunden begleitet wird, und klappernde Schuhsohlen tr\u00E4gt, geht auf ein Portal zu, in dem eine Frau steht und ihm sch\u00F6ne Augen macht.\n11.7 x 8.5 cm.\nUnkoloriert.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/04f25db73f03/values/aa8971af4d26", - valueHasUUID = stringFormatter.decodeUuid("aa8971af4d26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 44.\nHolzschnitt zu Kap. 44: Vom L\u00E4rmen in der Kirche\nEin junger Narr in edler Kleidung, der einen Jagdfalken auf dem Arm h\u00E4lt, von Hunden begleitet wird, und klappernde Schuhsohlen tr\u00E4gt, geht auf ein Portal zu, in dem eine Frau steht und ihm sch\u00F6ne Augen macht.\n11.7 x 8.5 cm.\nUnkoloriert." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/04f25db73f03/values/aa8971af4d26", + valueHasUUID = stringFormatter.decodeUuid("aa8971af4d26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -201,25 +221,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:47Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Titelblatt (Hartl 2001: Stultitia Navis I).\nHolzschnitt: \nErsatzholzschnitt f\u00FCr Titelblatt, recto:\nEin Schiff voller Narren f\u00E4hrt nach links. Hinten auf der Br\u00FCcke trinkt ein Narr aus einer Flasche, vorne pr\u00FCgeln sich zwei weitere narren so sehr, dass einer von ihnen \u00FCber Bord zu gehen droht. Oben die Inschrift \"Nauis stultoru(m).\"; auf dem Schiffsrumpf die Datierung \"1.4.9.7.\".\n6.5 x 11.5 cm.\noben rechts die bibliographische Angabe (Graphitstift) \"Hain 3750\"; unten rechts Bibliotheksstempel (queroval, schwarz): \"BIBL. PUBL.| BASILEENSIS\".") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/05c7acceb703/values/5f23f3171d26", - valueHasUUID = stringFormatter.decodeUuid("5f23f3171d26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Titelblatt (Hartl 2001: Stultitia Navis I).\nHolzschnitt: \nErsatzholzschnitt f\u00FCr Titelblatt, recto:\nEin Schiff voller Narren f\u00E4hrt nach links. Hinten auf der Br\u00FCcke trinkt ein Narr aus einer Flasche, vorne pr\u00FCgeln sich zwei weitere narren so sehr, dass einer von ihnen \u00FCber Bord zu gehen droht. Oben die Inschrift \"Nauis stultoru(m).\"; auf dem Schiffsrumpf die Datierung \"1.4.9.7.\".\n6.5 x 11.5 cm.\noben rechts die bibliographische Angabe (Graphitstift) \"Hain 3750\"; unten rechts Bibliotheksstempel (queroval, schwarz): \"BIBL. PUBL.| BASILEENSIS\"." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/05c7acceb703/values/5f23f3171d26", + valueHasUUID = stringFormatter.decodeUuid("5f23f3171d26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -235,25 +259,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:47Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 4.\nHolzschnitt zu Kap. 4: Von neumodischen Sitten.\nEin alter Narr mit Becher h\u00E4lt einem jungen Mann in modischer Tracht einen Spiegel vor. Zwischen Narr und J\u00FCngling steht der Name \u201E.VLI.\u201C; \u00FCber den beiden schwebt eine Banderole mit der Aufschrift \u201Evly . von . stouffen . . frisch . vnd vngschaffen\u201C; zwischen den F\u00FCssen des J\u00FCnglings ist die Jahreszahl \u201E.1.4.9.4.\u201C zu lesen.\n11.6 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/075d33c1bd03/values/77718ce21e26", - valueHasUUID = stringFormatter.decodeUuid("77718ce21e26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 4.\nHolzschnitt zu Kap. 4: Von neumodischen Sitten.\nEin alter Narr mit Becher h\u00E4lt einem jungen Mann in modischer Tracht einen Spiegel vor. Zwischen Narr und J\u00FCngling steht der Name \u201E.VLI.\u201C; \u00FCber den beiden schwebt eine Banderole mit der Aufschrift \u201Evly . von . stouffen . . frisch . vnd vngschaffen\u201C; zwischen den F\u00FCssen des J\u00FCnglings ist die Jahreszahl \u201E.1.4.9.4.\u201C zu lesen.\n11.6 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/075d33c1bd03/values/77718ce21e26", + valueHasUUID = stringFormatter.decodeUuid("77718ce21e26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -269,25 +297,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:48Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 29.\nHolzschnitt zu Kap. 29: Von Verkennung der Mitmenschen.\nEin Narr verspottet einen Sterbenden, neben dessen Bett eine Frau betet, w\u00E4hrend sich unter dem Narren die H\u00F6lle in Gestalt eines gefr\u00E4ssigen Drachenkopfs auftut, 11.7 x 8.5 cm.\n") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/0b8940a6c903/values/f752218c3b26", - valueHasUUID = stringFormatter.decodeUuid("f752218c3b26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 29.\nHolzschnitt zu Kap. 29: Von Verkennung der Mitmenschen.\nEin Narr verspottet einen Sterbenden, neben dessen Bett eine Frau betet, w\u00E4hrend sich unter dem Narren die H\u00F6lle in Gestalt eines gefr\u00E4ssigen Drachenkopfs auftut, 11.7 x 8.5 cm.\n" + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/0b8940a6c903/values/f752218c3b26", + valueHasUUID = stringFormatter.decodeUuid("f752218c3b26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -303,25 +335,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:48Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 43.\nHolzschnitt zu Kap. 43: Missachten der ewigen Seligkeit\nEin Narr steht mit einer grossen Waage in einer Landschaft und wiegt das Himmelsfirmament (links) gegen eine Burg (rechts) auf. Die Zunge der Waage schl\u00E4gt zugunsten der Burg aus, 11.5 x 8.4 cm.\n") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/0d1fc798cf03/values/e75f1e764d26", - valueHasUUID = stringFormatter.decodeUuid("e75f1e764d26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 43.\nHolzschnitt zu Kap. 43: Missachten der ewigen Seligkeit\nEin Narr steht mit einer grossen Waage in einer Landschaft und wiegt das Himmelsfirmament (links) gegen eine Burg (rechts) auf. Die Zunge der Waage schl\u00E4gt zugunsten der Burg aus, 11.5 x 8.4 cm.\n" + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/0d1fc798cf03/values/e75f1e764d26", + valueHasUUID = stringFormatter.decodeUuid("e75f1e764d26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -337,25 +373,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:45Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 66.\nHolzschnitt zu Kap. 66: Von der Erforschung der Welt.\nEin Narr hat ein Schema des Universums auf den Boden Gezeichnet und vermisst es mit einem Zirkel. Von hinten blickt ein zweiter Narr \u00FCber eine Mauer und wendet sich dem ersten mit sp\u00F6ttischen Gesten zu.\n11.6 x 8.4 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/0d5ac1099503/values/4dcdbebc7126", - valueHasUUID = stringFormatter.decodeUuid("4dcdbebc7126"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 66.\nHolzschnitt zu Kap. 66: Von der Erforschung der Welt.\nEin Narr hat ein Schema des Universums auf den Boden Gezeichnet und vermisst es mit einem Zirkel. Von hinten blickt ein zweiter Narr \u00FCber eine Mauer und wendet sich dem ersten mit sp\u00F6ttischen Gesten zu.\n11.6 x 8.4 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/0d5ac1099503/values/4dcdbebc7126", + valueHasUUID = stringFormatter.decodeUuid("4dcdbebc7126"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -371,25 +411,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:48Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 58.\nHolzschnitt zu Kap. 58: Sich um die Angelegenheiten anderer k\u00FCmmern.\nEin Narr versucht mit einem Wassereimer den Brand im Haus des Nachbarn zu l\u00F6schen und wird dabei von einem anderen Narren, der an seinem Mantel zerrt, unterbrochen, den hinter ihm steht auch sein eigenes Haus in Flammen.\n11.6 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/0fb54d8bd503/values/9a966e995f26", - valueHasUUID = stringFormatter.decodeUuid("9a966e995f26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 58.\nHolzschnitt zu Kap. 58: Sich um die Angelegenheiten anderer k\u00FCmmern.\nEin Narr versucht mit einem Wassereimer den Brand im Haus des Nachbarn zu l\u00F6schen und wird dabei von einem anderen Narren, der an seinem Mantel zerrt, unterbrochen, den hinter ihm steht auch sein eigenes Haus in Flammen.\n11.6 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/0fb54d8bd503/values/9a966e995f26", + valueHasUUID = stringFormatter.decodeUuid("9a966e995f26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -405,25 +449,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:45Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 81.\nHolzschnitt zu Kap. 81: Aus K\u00FCche und Keller.\nEin Narr f\u00FChrt von einem Boot aus vier Knechte am Strick, die sich in einer K\u00FCche \u00FCber Spreis und Trank hermachen, w\u00E4hrend eine Frau, die am Herdfeuer sitzt, das Essen zubereitet, 11.7 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/0ff047fc9a03/values/b9ac70cc7926", - valueHasUUID = stringFormatter.decodeUuid("b9ac70cc7926"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 81.\nHolzschnitt zu Kap. 81: Aus K\u00FCche und Keller.\nEin Narr f\u00FChrt von einem Boot aus vier Knechte am Strick, die sich in einer K\u00FCche \u00FCber Spreis und Trank hermachen, w\u00E4hrend eine Frau, die am Herdfeuer sitzt, das Essen zubereitet, 11.7 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/0ff047fc9a03/values/b9ac70cc7926", + valueHasUUID = stringFormatter.decodeUuid("b9ac70cc7926"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -439,25 +487,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:49Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 69.\nHolzschnitt Lemmer 1979, S. 117: Variante zu Kap. 69.\nEin Narr, der vor einer Stadtkulisse steht, hat mit seiner Rechten einen Ball in die Luft geworfen und schl\u00E4gt mit seiner Linken einen Mann, der sogleich nach seinem Dolch greift. Ein junger Mann beobachtet das Geschehen.\nDer Bildinhalt stimmt weitgehend mit dem urspr\u00FCnglichen Holzschnitt \u00FCberein.\n11.7 x 8.4 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/114bd47ddb03/values/c99f73e26726", - valueHasUUID = stringFormatter.decodeUuid("c99f73e26726"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:49Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 69.\nHolzschnitt Lemmer 1979, S. 117: Variante zu Kap. 69.\nEin Narr, der vor einer Stadtkulisse steht, hat mit seiner Rechten einen Ball in die Luft geworfen und schl\u00E4gt mit seiner Linken einen Mann, der sogleich nach seinem Dolch greift. Ein junger Mann beobachtet das Geschehen.\nDer Bildinhalt stimmt weitgehend mit dem urspr\u00FCnglichen Holzschnitt \u00FCberein.\n11.7 x 8.4 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/114bd47ddb03/values/c99f73e26726", + valueHasUUID = stringFormatter.decodeUuid("c99f73e26726"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:49Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -473,25 +525,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:40Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 23.\nHolzschnitt zu Kap. 23: Vom blinden Vertrauen auf das Gl\u00FCck.\nEin Narr schaut oben aus dem Fenster seines Hauses, das unten lichterloh brennt. Am Himmel erscheint die r\u00E4chende Gotteshand, die mit einen Hammer auf Haus und Narr einschl\u00E4gt. Auf der Fahne \u00FCber dem Erker des Hauses ist der Baselstab zu erkennen.\n11.5 x 8.2 cm.\nUnkoloriert.\n") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/14dd8cbc3403/values/7e39f54a3726", - valueHasUUID = stringFormatter.decodeUuid("7e39f54a3726"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 23.\nHolzschnitt zu Kap. 23: Vom blinden Vertrauen auf das Gl\u00FCck.\nEin Narr schaut oben aus dem Fenster seines Hauses, das unten lichterloh brennt. Am Himmel erscheint die r\u00E4chende Gotteshand, die mit einen Hammer auf Haus und Narr einschl\u00E4gt. Auf der Fahne \u00FCber dem Erker des Hauses ist der Baselstab zu erkennen.\n11.5 x 8.2 cm.\nUnkoloriert.\n" + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/14dd8cbc3403/values/7e39f54a3726", + valueHasUUID = stringFormatter.decodeUuid("7e39f54a3726"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -507,25 +563,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:40Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 34.\nHolzschnitt zu Kap. 34: Ein Narr sein und es bleiben.\nEin Narr wird von drei G\u00E4nsen umgeben, deren eine von ihm wegfliegt.\n11.7 x 8.4 cm.\nUnkoloriert.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/167313af3a03/values/1ab5d9ef4226", - valueHasUUID = stringFormatter.decodeUuid("1ab5d9ef4226"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 34.\nHolzschnitt zu Kap. 34: Ein Narr sein und es bleiben.\nEin Narr wird von drei G\u00E4nsen umgeben, deren eine von ihm wegfliegt.\n11.7 x 8.4 cm.\nUnkoloriert." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/167313af3a03/values/1ab5d9ef4226", + valueHasUUID = stringFormatter.decodeUuid("1ab5d9ef4226"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:40Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -541,25 +601,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:47Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 6.\nHolzschnitt zu Kap. 6: Von mangelhafter Erziehung der Kinder.\nZwei Jungen geraten am Spieltisch \u00FCber Karten und W\u00FCrfen in Streit. W\u00E4hrend der eine einen Dolch z\u00FCckt und der andere nach seinem Schwert greift, sitzt ein \u00E4lterer Narr mit verbundenen Augen ahnungslos neben dem Geschehen.\n11.7 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/1b746fabbe03/values/8318d9c71f26", - valueHasUUID = stringFormatter.decodeUuid("8318d9c71f26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 6.\nHolzschnitt zu Kap. 6: Von mangelhafter Erziehung der Kinder.\nZwei Jungen geraten am Spieltisch \u00FCber Karten und W\u00FCrfen in Streit. W\u00E4hrend der eine einen Dolch z\u00FCckt und der andere nach seinem Schwert greift, sitzt ein \u00E4lterer Narr mit verbundenen Augen ahnungslos neben dem Geschehen.\n11.7 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/1b746fabbe03/values/8318d9c71f26", + valueHasUUID = stringFormatter.decodeUuid("8318d9c71f26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -575,25 +639,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:44Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 28.\nHolzschnitt zu Kap. 28: Vom N\u00F6rgeln an Gottes Werken.\nEin Narr, der auf einem Berg ein Feuer entfacht hat, h\u00E4lt seine Hand sch\u00FCtzend \u00FCber die Augen, w\u00E4hrend er seinen Blick auf die hell am Himmel strahlende Sonne richtet. 11.7 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/1baf691c8403/values/2882816d3a26", - valueHasUUID = stringFormatter.decodeUuid("2882816d3a26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:44Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 28.\nHolzschnitt zu Kap. 28: Vom N\u00F6rgeln an Gottes Werken.\nEin Narr, der auf einem Berg ein Feuer entfacht hat, h\u00E4lt seine Hand sch\u00FCtzend \u00FCber die Augen, w\u00E4hrend er seinen Blick auf die hell am Himmel strahlende Sonne richtet. 11.7 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/1baf691c8403/values/2882816d3a26", + valueHasUUID = stringFormatter.decodeUuid("2882816d3a26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:44Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -609,25 +677,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:47Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 18.\nHolzschnitt zu Kap. 18: Vom Dienst an zwei Herren.\nEin mit Spiess bewaffneter Narr bl\u00E4st in ein Horn. Sein Hund versucht derweil im Hintergrund zwei Hasen gleichzeitig zu erjagen, 11.6 x 8.4 cm.\n") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/1d0af69dc403/values/4e9dc2b53326", - valueHasUUID = stringFormatter.decodeUuid("4e9dc2b53326"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 18.\nHolzschnitt zu Kap. 18: Vom Dienst an zwei Herren.\nEin mit Spiess bewaffneter Narr bl\u00E4st in ein Horn. Sein Hund versucht derweil im Hintergrund zwei Hasen gleichzeitig zu erjagen, 11.6 x 8.4 cm.\n" + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/1d0af69dc403/values/4e9dc2b53326", + valueHasUUID = stringFormatter.decodeUuid("4e9dc2b53326"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:47Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -643,25 +715,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:48Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 31.\nHolzschnitt zu Kap. 31: Vom Hinausschieben auf morgen.\nEin Narr steht mit ausgebreiteten Armen auf einer Strasse. Auf seinen H\u00E4nden sitzen zwei Raben, die beide \u201ECras\u201C \u2013 das lateinische Wort f\u00FCr \u201Emorgen\u201C \u2013 rufen. Auf dem Kopf des Narren sitzt ein Papagei und ahmt den Ruf der Kr\u00E4hen nach, 11.6 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/1fa07c90ca03/values/c623c1aa3c26", - valueHasUUID = stringFormatter.decodeUuid("c623c1aa3c26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 31.\nHolzschnitt zu Kap. 31: Vom Hinausschieben auf morgen.\nEin Narr steht mit ausgebreiteten Armen auf einer Strasse. Auf seinen H\u00E4nden sitzen zwei Raben, die beide \u201ECras\u201C \u2013 das lateinische Wort f\u00FCr \u201Emorgen\u201C \u2013 rufen. Auf dem Kopf des Narren sitzt ein Papagei und ahmt den Ruf der Kr\u00E4hen nach, 11.6 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/1fa07c90ca03/values/c623c1aa3c26", + valueHasUUID = stringFormatter.decodeUuid("c623c1aa3c26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -677,25 +753,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:45Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 57.\nHolzschnitt zu Kap. 57: Von der Gnadenwahl Gottes.\nEin Narr, der auf einem Krebs reitet, st\u00FCtzt sich auf ein brechendes Schildrohr, das ihm die Hand durchbohrt. Ein Vogel fliegt auf den offenen Mund des Narren zu.\n11.6 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/1fdb76019003/values/118a3f426d26", - valueHasUUID = stringFormatter.decodeUuid("118a3f426d26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 57.\nHolzschnitt zu Kap. 57: Von der Gnadenwahl Gottes.\nEin Narr, der auf einem Krebs reitet, st\u00FCtzt sich auf ein brechendes Schildrohr, das ihm die Hand durchbohrt. Ein Vogel fliegt auf den offenen Mund des Narren zu.\n11.6 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/1fdb76019003/values/118a3f426d26", + valueHasUUID = stringFormatter.decodeUuid("118a3f426d26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -711,25 +791,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:48Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 45.\nHolzschnitt zu Kap. 45: Von selbstverschuldetem Ungl\u00FCck.\nIn Gestalt eines Narren springt Empedokles in den lodernden Krater des \u00C4tna. Im Vordergrund l\u00E4sst sich ein anderer Narr in einen Brunnen fallen. Beide werden von drei M\u00E4nnern beobachtet, die das Verhalten mit \u201EJn geschicht recht\u201C kommentieren, 11.7 x 8.3 cm.\n") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/21360383d003/values/b630be944e26", - valueHasUUID = stringFormatter.decodeUuid("b630be944e26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 45.\nHolzschnitt zu Kap. 45: Von selbstverschuldetem Ungl\u00FCck.\nIn Gestalt eines Narren springt Empedokles in den lodernden Krater des \u00C4tna. Im Vordergrund l\u00E4sst sich ein anderer Narr in einen Brunnen fallen. Beide werden von drei M\u00E4nnern beobachtet, die das Verhalten mit \u201EJn geschicht recht\u201C kommentieren, 11.7 x 8.3 cm.\n" + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/21360383d003/values/b630be944e26", + valueHasUUID = stringFormatter.decodeUuid("b630be944e26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -745,25 +829,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:45Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 68.\nHolzschnitt zu Kap. 68: Keinen Scherz verstehen.\nEin Kind, das auf einem Steckenpferd reitet und mit einem Stock als Gerte umher fuchtelt, wird von einem Narren am rechten Rand ausgeschimpft. Ein anderer Narr, der neben dem Kind steht, ist dabei, sein Schwert aus der Scheide zu ziehen.\n11.7 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/2171fdf39503/values/59740ba27226", - valueHasUUID = stringFormatter.decodeUuid("59740ba27226"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 68.\nHolzschnitt zu Kap. 68: Keinen Scherz verstehen.\nEin Kind, das auf einem Steckenpferd reitet und mit einem Stock als Gerte umher fuchtelt, wird von einem Narren am rechten Rand ausgeschimpft. Ein anderer Narr, der neben dem Kind steht, ist dabei, sein Schwert aus der Scheide zu ziehen.\n11.7 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/2171fdf39503/values/59740ba27226", + valueHasUUID = stringFormatter.decodeUuid("59740ba27226"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -779,25 +867,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:45Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 83.\nneuer Holzschitt (nicht in Lemmer 1979): Vor einer H\u00E4userkulisse kniet ein Narr mit einem Beutel in der Linken und zwei Keulen in der Rechten vor einem Mann mit Hut und einem j\u00FCngeren Begleiter, 11.6 x 8.6 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/230784e69b03/values/4ba763247b26", - valueHasUUID = stringFormatter.decodeUuid("4ba763247b26"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 83.\nneuer Holzschitt (nicht in Lemmer 1979): Vor einer H\u00E4userkulisse kniet ein Narr mit einem Beutel in der Linken und zwei Keulen in der Rechten vor einem Mann mit Hut und einem j\u00FCngeren Begleiter, 11.6 x 8.6 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/230784e69b03/values/4ba763247b26", + valueHasUUID = stringFormatter.decodeUuid("4ba763247b26"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:45Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -813,25 +905,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:42Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 96.\nHolzschnitt zu Kap. 96: Schenken und hinterdrein bereuen.\nEin Narr, der vor einem Haus steht, \u00FCberreicht einem b\u00E4rtigen Alten ein Geschenk, kratzt sich dabei aber unschl\u00FCssig am Kopf.\n11.6 x 8.3 cm.\nUnkoloriert.\nOben rechts Blattnummerierung (Graphitstift): \"128\".") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/23427e576103/values/c32d62198426", - valueHasUUID = stringFormatter.decodeUuid("c32d62198426"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:42Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 96.\nHolzschnitt zu Kap. 96: Schenken und hinterdrein bereuen.\nEin Narr, der vor einem Haus steht, \u00FCberreicht einem b\u00E4rtigen Alten ein Geschenk, kratzt sich dabei aber unschl\u00FCssig am Kopf.\n11.6 x 8.3 cm.\nUnkoloriert.\nOben rechts Blattnummerierung (Graphitstift): \"128\"." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/23427e576103/values/c32d62198426", + valueHasUUID = stringFormatter.decodeUuid("c32d62198426"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:42Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -847,25 +943,29 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:48Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some( - "Beginn Kapitel 60.\nHolzschnitt zu Kap. 60: Von Selbstgef\u00E4lligkeit.\nEin alter Narr steht am Ofen und r\u00FChrt in einem Topf. Gleichzeitig schaut er sich dabei in einem Handspiegel an.\n11.7 x 8.5 cm.") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/23cc8975d603/values/a63dbb7e6026", - valueHasUUID = stringFormatter.decodeUuid("a63dbb7e6026"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), - attachedToUser = "http://rdfh.ch/users/b83acc5f05", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#description".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some( + "Beginn Kapitel 60.\nHolzschnitt zu Kap. 60: Von Selbstgef\u00E4lligkeit.\nEin alter Narr steht am Ofen und r\u00FChrt in einem Topf. Gleichzeitig schaut er sich dabei in einem Handspiegel an.\n11.7 x 8.5 cm." + ) + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/23cc8975d603/values/a63dbb7e6026", + valueHasUUID = stringFormatter.decodeUuid("a63dbb7e6026"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser,knora-admin:KnownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:48Z"), + attachedToUser = "http://rdfh.ch/users/b83acc5f05", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -884,686 +984,829 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing".toSmartIri, projectADM = SharedTestDataADM.anythingProject, creationDate = Instant.parse("2016-03-02T15:05:10Z"), - values = Map("http://www.knora.org/ontology/0001/anything#hasText".toSmartIri -> Vector( - ReadTextValueV2( - valueContent = TextValueContentV2( - standoff = Vector( - StandoffTagV2( - endParentIndex = None, - originalXMLID = None, - uuid = UUID.fromString("2e136103-2a4b-4e59-ac8f-79a53f54b496"), - endPosition = 45, - startParentIndex = None, - attributes = Nil, - startIndex = 0, - endIndex = None, - dataType = None, - startPosition = 0, - standoffTagClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag".toSmartIri - ), - StandoffTagV2( - endParentIndex = None, - originalXMLID = None, - uuid = UUID.fromString("80133696-26a1-4941-967b-6bf210d7d5e1"), - endPosition = 19, - startParentIndex = Some(0), - attributes = Vector(StandoffTagIriAttributeV2( - standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasLink".toSmartIri, - value = "http://rdfh.ch/0001/a-thing" - )), - startIndex = 1, - endIndex = None, - dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), - startPosition = 14, - standoffTagClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag".toSmartIri - ) - ), - mappingIri = Some("http://rdfh.ch/standoff/mappings/StandardMapping"), - mapping = Some(MappingXMLtoStandoff( - namespace = Map("noNamespace" -> Map( - "tbody" -> Map("noClass" -> XMLTag( - name = "tbody", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableBodyTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "pre" -> Map("noClass" -> XMLTag( - name = "pre", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffPreTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "ol" -> Map("noClass" -> XMLTag( - name = "ol", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffOrderedListTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "hr" -> Map("noClass" -> XMLTag( - name = "hr", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffLineTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h4" -> Map("noClass" -> XMLTag( - name = "h4", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader4Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h3" -> Map("noClass" -> XMLTag( - name = "h3", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader3Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "li" -> Map("noClass" -> XMLTag( - name = "li", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffListElementTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "br" -> Map("noClass" -> XMLTag( - name = "br", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBrTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "u" -> Map("noClass" -> XMLTag( - name = "u", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnderlineTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "strike" -> Map("noClass" -> XMLTag( - name = "strike", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffStrikethroughTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "ul" -> Map("noClass" -> XMLTag( - name = "ul", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnorderedListTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "a" -> Map( - "salsah-link" -> XMLTag( - name = "a", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag", - attributesToProps = Map(), - dataType = Some(XMLStandoffDataTypeClass( - standoffDataTypeClass = StandoffDataTypeClasses.StandoffLinkTag, - dataTypeXMLAttribute = "href" - )) - ), - separatorRequired = false - ), - "internal-link" -> XMLTag( - name = "a", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffInternalReferenceTag", - attributesToProps = Map(), - dataType = Some(XMLStandoffDataTypeClass( - standoffDataTypeClass = StandoffDataTypeClasses.StandoffInternalReferenceTag, - dataTypeXMLAttribute = "href" - )) - ), - separatorRequired = false - ), - "noClass" -> XMLTag( - name = "a", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffUriTag", - attributesToProps = Map(), - dataType = Some(XMLStandoffDataTypeClass( - standoffDataTypeClass = StandoffDataTypeClasses.StandoffUriTag, - dataTypeXMLAttribute = "href" - )) - ), - separatorRequired = false - ) + values = Map( + "http://www.knora.org/ontology/0001/anything#hasText".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + standoff = Vector( + StandoffTagV2( + endParentIndex = None, + originalXMLID = None, + uuid = UUID.fromString("2e136103-2a4b-4e59-ac8f-79a53f54b496"), + endPosition = 45, + startParentIndex = None, + attributes = Nil, + startIndex = 0, + endIndex = None, + dataType = None, + startPosition = 0, + standoffTagClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag".toSmartIri ), - "text" -> Map("noClass" -> XMLTag( - name = "text", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag", - attributesToProps = Map("noNamespace" -> Map( - "documentType" -> "http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType")), - dataType = None + StandoffTagV2( + endParentIndex = None, + originalXMLID = None, + uuid = UUID.fromString("80133696-26a1-4941-967b-6bf210d7d5e1"), + endPosition = 19, + startParentIndex = Some(0), + attributes = Vector( + StandoffTagIriAttributeV2( + standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasLink".toSmartIri, + value = "http://rdfh.ch/0001/a-thing" + ) ), - separatorRequired = false - )), - "strong" -> Map("noClass" -> XMLTag( - name = "strong", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBoldTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "code" -> Map("noClass" -> XMLTag( - name = "code", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCodeTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h2" -> Map("noClass" -> XMLTag( - name = "h2", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader2Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "td" -> Map("noClass" -> XMLTag( - name = "td", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableCellTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "em" -> Map("noClass" -> XMLTag( - name = "em", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffItalicTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "tr" -> Map("noClass" -> XMLTag( - name = "tr", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableRowTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "cite" -> Map("noClass" -> XMLTag( - name = "cite", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCiteTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "blockquote" -> Map("noClass" -> XMLTag( - name = "blockquote", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBlockquoteTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "p" -> Map("noClass" -> XMLTag( - name = "p", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffParagraphTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h6" -> Map("noClass" -> XMLTag( - name = "h6", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader6Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h1" -> Map("noClass" -> XMLTag( - name = "h1", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader1Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "sub" -> Map("noClass" -> XMLTag( - name = "sub", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSubscriptTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "sup" -> Map("noClass" -> XMLTag( - name = "sup", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSuperscriptTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "h5" -> Map("noClass" -> XMLTag( - name = "h5", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader5Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "table" -> Map("noClass" -> XMLTag( - name = "table", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableTag", - attributesToProps = Map(), - dataType = None + startIndex = 1, + endIndex = None, + dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), + startPosition = 14, + standoffTagClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag".toSmartIri + ) + ), + mappingIri = Some("http://rdfh.ch/standoff/mappings/StandardMapping"), + mapping = Some( + MappingXMLtoStandoff( + namespace = Map( + "noNamespace" -> Map( + "tbody" -> Map( + "noClass" -> XMLTag( + name = "tbody", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableBodyTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "pre" -> Map( + "noClass" -> XMLTag( + name = "pre", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffPreTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "ol" -> Map( + "noClass" -> XMLTag( + name = "ol", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffOrderedListTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "hr" -> Map( + "noClass" -> XMLTag( + name = "hr", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffLineTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h4" -> Map( + "noClass" -> XMLTag( + name = "h4", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader4Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h3" -> Map( + "noClass" -> XMLTag( + name = "h3", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader3Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "li" -> Map( + "noClass" -> XMLTag( + name = "li", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffListElementTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "br" -> Map( + "noClass" -> XMLTag( + name = "br", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBrTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "u" -> Map( + "noClass" -> XMLTag( + name = "u", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnderlineTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "strike" -> Map( + "noClass" -> XMLTag( + name = "strike", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffStrikethroughTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "ul" -> Map( + "noClass" -> XMLTag( + name = "ul", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnorderedListTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "a" -> Map( + "salsah-link" -> XMLTag( + name = "a", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag", + attributesToProps = Map(), + dataType = Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = StandoffDataTypeClasses.StandoffLinkTag, + dataTypeXMLAttribute = "href" + ) + ) + ), + separatorRequired = false + ), + "internal-link" -> XMLTag( + name = "a", + mapping = XMLTagToStandoffClass( + standoffClassIri = + "http://www.knora.org/ontology/knora-base#StandoffInternalReferenceTag", + attributesToProps = Map(), + dataType = Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = StandoffDataTypeClasses.StandoffInternalReferenceTag, + dataTypeXMLAttribute = "href" + ) + ) + ), + separatorRequired = false + ), + "noClass" -> XMLTag( + name = "a", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffUriTag", + attributesToProps = Map(), + dataType = Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = StandoffDataTypeClasses.StandoffUriTag, + dataTypeXMLAttribute = "href" + ) + ) + ), + separatorRequired = false + ) + ), + "text" -> Map( + "noClass" -> XMLTag( + name = "text", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag", + attributesToProps = Map( + "noNamespace" -> Map( + "documentType" -> "http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType" + ) + ), + dataType = None + ), + separatorRequired = false + ) + ), + "strong" -> Map( + "noClass" -> XMLTag( + name = "strong", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBoldTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "code" -> Map( + "noClass" -> XMLTag( + name = "code", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCodeTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h2" -> Map( + "noClass" -> XMLTag( + name = "h2", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader2Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "td" -> Map( + "noClass" -> XMLTag( + name = "td", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableCellTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "em" -> Map( + "noClass" -> XMLTag( + name = "em", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffItalicTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "tr" -> Map( + "noClass" -> XMLTag( + name = "tr", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableRowTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "cite" -> Map( + "noClass" -> XMLTag( + name = "cite", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCiteTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "blockquote" -> Map( + "noClass" -> XMLTag( + name = "blockquote", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBlockquoteTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "p" -> Map( + "noClass" -> XMLTag( + name = "p", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffParagraphTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h6" -> Map( + "noClass" -> XMLTag( + name = "h6", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader6Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h1" -> Map( + "noClass" -> XMLTag( + name = "h1", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader1Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "sub" -> Map( + "noClass" -> XMLTag( + name = "sub", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSubscriptTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "sup" -> Map( + "noClass" -> XMLTag( + name = "sup", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSuperscriptTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "h5" -> Map( + "noClass" -> XMLTag( + name = "h5", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader5Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "table" -> Map( + "noClass" -> XMLTag( + name = "table", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ) + ) ), - separatorRequired = true - )) - )), - defaultXSLTransformation = None - )), - xslt = None, - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some("Ich liebe die Dinge, sie sind alles f\u00FCr mich.") - ), - valueHasMaxStandoffStartIndex = Some(1), - valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/1", - valueHasUUID = stringFormatter.decodeUuid("1"), - permissions = "CR knora-admin:Creator", - userPermission = ChangeRightsPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:54Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - deletionInfo = None - ), - ReadTextValueV2( - valueContent = TextValueContentV2( - standoff = Vector( - StandoffTagV2( - endParentIndex = None, - originalXMLID = None, - uuid = UUID.fromString("fd583868-2a3c-4941-a330-990f5a972f71"), - endPosition = 25, - startParentIndex = None, - attributes = Nil, - startIndex = 0, - endIndex = None, - dataType = None, - startPosition = 0, - standoffTagClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag".toSmartIri + defaultXSLTransformation = None + ) ), - StandoffTagV2( - endParentIndex = None, - originalXMLID = None, - uuid = UUID.fromString("59a36237-95a9-4acc-8361-7c8fac311063"), - endPosition = 16, - startParentIndex = Some(0), - attributes = Vector(StandoffTagIriAttributeV2( - standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasLink".toSmartIri, - value = "http://rdfh.ch/0001/a-thing" - )), - startIndex = 1, - endIndex = None, - dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), - startPosition = 11, - standoffTagClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag".toSmartIri - ) + xslt = None, + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some("Ich liebe die Dinge, sie sind alles f\u00FCr mich.") ), - mappingIri = Some("http://rdfh.ch/standoff/mappings/StandardMapping"), - mapping = Some(MappingXMLtoStandoff( - namespace = Map("noNamespace" -> Map( - "tbody" -> Map("noClass" -> XMLTag( - name = "tbody", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableBodyTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "pre" -> Map("noClass" -> XMLTag( - name = "pre", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffPreTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "ol" -> Map("noClass" -> XMLTag( - name = "ol", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffOrderedListTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "hr" -> Map("noClass" -> XMLTag( - name = "hr", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffLineTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h4" -> Map("noClass" -> XMLTag( - name = "h4", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader4Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h3" -> Map("noClass" -> XMLTag( - name = "h3", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader3Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "li" -> Map("noClass" -> XMLTag( - name = "li", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffListElementTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "br" -> Map("noClass" -> XMLTag( - name = "br", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBrTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "u" -> Map("noClass" -> XMLTag( - name = "u", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnderlineTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "strike" -> Map("noClass" -> XMLTag( - name = "strike", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffStrikethroughTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "ul" -> Map("noClass" -> XMLTag( - name = "ul", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnorderedListTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "a" -> Map( - "salsah-link" -> XMLTag( - name = "a", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag", - attributesToProps = Map(), - dataType = Some(XMLStandoffDataTypeClass( - standoffDataTypeClass = StandoffDataTypeClasses.StandoffLinkTag, - dataTypeXMLAttribute = "href" - )) - ), - separatorRequired = false - ), - "internal-link" -> XMLTag( - name = "a", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffInternalReferenceTag", - attributesToProps = Map(), - dataType = Some(XMLStandoffDataTypeClass( - standoffDataTypeClass = StandoffDataTypeClasses.StandoffInternalReferenceTag, - dataTypeXMLAttribute = "href" - )) - ), - separatorRequired = false - ), - "noClass" -> XMLTag( - name = "a", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffUriTag", - attributesToProps = Map(), - dataType = Some(XMLStandoffDataTypeClass( - standoffDataTypeClass = StandoffDataTypeClasses.StandoffUriTag, - dataTypeXMLAttribute = "href" - )) - ), - separatorRequired = false - ) + valueHasMaxStandoffStartIndex = Some(1), + valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/1", + valueHasUUID = stringFormatter.decodeUuid("1"), + permissions = "CR knora-admin:Creator", + userPermission = ChangeRightsPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:54Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + deletionInfo = None + ), + ReadTextValueV2( + valueContent = TextValueContentV2( + standoff = Vector( + StandoffTagV2( + endParentIndex = None, + originalXMLID = None, + uuid = UUID.fromString("fd583868-2a3c-4941-a330-990f5a972f71"), + endPosition = 25, + startParentIndex = None, + attributes = Nil, + startIndex = 0, + endIndex = None, + dataType = None, + startPosition = 0, + standoffTagClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag".toSmartIri ), - "text" -> Map("noClass" -> XMLTag( - name = "text", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag", - attributesToProps = Map("noNamespace" -> Map( - "documentType" -> "http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType")), - dataType = None - ), - separatorRequired = false - )), - "strong" -> Map("noClass" -> XMLTag( - name = "strong", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBoldTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "code" -> Map("noClass" -> XMLTag( - name = "code", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCodeTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h2" -> Map("noClass" -> XMLTag( - name = "h2", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader2Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "td" -> Map("noClass" -> XMLTag( - name = "td", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableCellTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "em" -> Map("noClass" -> XMLTag( - name = "em", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffItalicTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "tr" -> Map("noClass" -> XMLTag( - name = "tr", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableRowTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "cite" -> Map("noClass" -> XMLTag( - name = "cite", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCiteTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "blockquote" -> Map("noClass" -> XMLTag( - name = "blockquote", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBlockquoteTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "p" -> Map("noClass" -> XMLTag( - name = "p", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffParagraphTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h6" -> Map("noClass" -> XMLTag( - name = "h6", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader6Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "h1" -> Map("noClass" -> XMLTag( - name = "h1", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader1Tag", - attributesToProps = Map(), - dataType = None + StandoffTagV2( + endParentIndex = None, + originalXMLID = None, + uuid = UUID.fromString("59a36237-95a9-4acc-8361-7c8fac311063"), + endPosition = 16, + startParentIndex = Some(0), + attributes = Vector( + StandoffTagIriAttributeV2( + standoffPropertyIri = "http://www.knora.org/ontology/knora-base#standoffTagHasLink".toSmartIri, + value = "http://rdfh.ch/0001/a-thing" + ) ), - separatorRequired = true - )), - "sub" -> Map("noClass" -> XMLTag( - name = "sub", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSubscriptTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "sup" -> Map("noClass" -> XMLTag( - name = "sup", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSuperscriptTag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = false - )), - "h5" -> Map("noClass" -> XMLTag( - name = "h5", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader5Tag", - attributesToProps = Map(), - dataType = None - ), - separatorRequired = true - )), - "table" -> Map("noClass" -> XMLTag( - name = "table", - mapping = XMLTagToStandoffClass( - standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableTag", - attributesToProps = Map(), - dataType = None + startIndex = 1, + endIndex = None, + dataType = Some(StandoffDataTypeClasses.StandoffLinkTag), + startPosition = 11, + standoffTagClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag".toSmartIri + ) + ), + mappingIri = Some("http://rdfh.ch/standoff/mappings/StandardMapping"), + mapping = Some( + MappingXMLtoStandoff( + namespace = Map( + "noNamespace" -> Map( + "tbody" -> Map( + "noClass" -> XMLTag( + name = "tbody", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableBodyTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "pre" -> Map( + "noClass" -> XMLTag( + name = "pre", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffPreTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "ol" -> Map( + "noClass" -> XMLTag( + name = "ol", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffOrderedListTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "hr" -> Map( + "noClass" -> XMLTag( + name = "hr", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffLineTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h4" -> Map( + "noClass" -> XMLTag( + name = "h4", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader4Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h3" -> Map( + "noClass" -> XMLTag( + name = "h3", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader3Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "li" -> Map( + "noClass" -> XMLTag( + name = "li", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffListElementTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "br" -> Map( + "noClass" -> XMLTag( + name = "br", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBrTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "u" -> Map( + "noClass" -> XMLTag( + name = "u", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnderlineTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "strike" -> Map( + "noClass" -> XMLTag( + name = "strike", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffStrikethroughTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "ul" -> Map( + "noClass" -> XMLTag( + name = "ul", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffUnorderedListTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "a" -> Map( + "salsah-link" -> XMLTag( + name = "a", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffLinkTag", + attributesToProps = Map(), + dataType = Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = StandoffDataTypeClasses.StandoffLinkTag, + dataTypeXMLAttribute = "href" + ) + ) + ), + separatorRequired = false + ), + "internal-link" -> XMLTag( + name = "a", + mapping = XMLTagToStandoffClass( + standoffClassIri = + "http://www.knora.org/ontology/knora-base#StandoffInternalReferenceTag", + attributesToProps = Map(), + dataType = Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = StandoffDataTypeClasses.StandoffInternalReferenceTag, + dataTypeXMLAttribute = "href" + ) + ) + ), + separatorRequired = false + ), + "noClass" -> XMLTag( + name = "a", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/knora-base#StandoffUriTag", + attributesToProps = Map(), + dataType = Some( + XMLStandoffDataTypeClass( + standoffDataTypeClass = StandoffDataTypeClasses.StandoffUriTag, + dataTypeXMLAttribute = "href" + ) + ) + ), + separatorRequired = false + ) + ), + "text" -> Map( + "noClass" -> XMLTag( + name = "text", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffRootTag", + attributesToProps = Map( + "noNamespace" -> Map( + "documentType" -> "http://www.knora.org/ontology/standoff#standoffRootTagHasDocumentType" + ) + ), + dataType = None + ), + separatorRequired = false + ) + ), + "strong" -> Map( + "noClass" -> XMLTag( + name = "strong", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBoldTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "code" -> Map( + "noClass" -> XMLTag( + name = "code", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCodeTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h2" -> Map( + "noClass" -> XMLTag( + name = "h2", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader2Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "td" -> Map( + "noClass" -> XMLTag( + name = "td", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableCellTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "em" -> Map( + "noClass" -> XMLTag( + name = "em", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffItalicTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "tr" -> Map( + "noClass" -> XMLTag( + name = "tr", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableRowTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "cite" -> Map( + "noClass" -> XMLTag( + name = "cite", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffCiteTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "blockquote" -> Map( + "noClass" -> XMLTag( + name = "blockquote", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffBlockquoteTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "p" -> Map( + "noClass" -> XMLTag( + name = "p", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffParagraphTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h6" -> Map( + "noClass" -> XMLTag( + name = "h6", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader6Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "h1" -> Map( + "noClass" -> XMLTag( + name = "h1", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader1Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "sub" -> Map( + "noClass" -> XMLTag( + name = "sub", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSubscriptTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "sup" -> Map( + "noClass" -> XMLTag( + name = "sup", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffSuperscriptTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = false + ) + ), + "h5" -> Map( + "noClass" -> XMLTag( + name = "h5", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffHeader5Tag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ), + "table" -> Map( + "noClass" -> XMLTag( + name = "table", + mapping = XMLTagToStandoffClass( + standoffClassIri = "http://www.knora.org/ontology/standoff#StandoffTableTag", + attributesToProps = Map(), + dataType = None + ), + separatorRequired = true + ) + ) + ) ), - separatorRequired = true - )) - )), - defaultXSLTransformation = None - )), - xslt = None, - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some("Na ja, die Dinge sind OK.") - ), - valueHasMaxStandoffStartIndex = Some(1), - valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/2", - valueHasUUID = stringFormatter.decodeUuid("2"), - permissions = "CR knora-admin:Creator", - userPermission = ChangeRightsPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:54Z"), - attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", - deletionInfo = None + defaultXSLTransformation = None + ) + ), + xslt = None, + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some("Na ja, die Dinge sind OK.") + ), + valueHasMaxStandoffStartIndex = Some(1), + valueIri = "http://rdfh.ch/0001/a-thing-with-text-values/values/2", + valueHasUUID = stringFormatter.decodeUuid("2"), + permissions = "CR knora-admin:Creator", + userPermission = ChangeRightsPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:54Z"), + attachedToUser = "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q", + deletionInfo = None + ) ) - )), + ), lastModificationDate = None, versionDate = None, deletionInfo = None - )) + ) + ) ) val constructQueryForBooksWithTitleZeitgloecklein: ConstructQuery = ConstructQuery( @@ -1575,10 +1818,12 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { XsdLiteral("true", "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri), None ), - StatementPattern(QueryVariable("book"), - IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), - QueryVariable("title"), - None) + StatementPattern( + QueryVariable("book"), + IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), + QueryVariable("title"), + None + ) ), querySchema = Some(ApiV2Simple) ), @@ -1596,10 +1841,12 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { IriRef("http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, None), None ), - StatementPattern(QueryVariable("book"), - IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), - QueryVariable("title"), - None), + StatementPattern( + QueryVariable("book"), + IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), + QueryVariable("title"), + None + ), StatementPattern( IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), IriRef("http://api.knora.org/ontology/knora-api/simple/v2#objectType".toSmartIri, None), @@ -1616,9 +1863,12 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { CompareExpression( QueryVariable("title"), CompareExpressionOperator.EQUALS, - XsdLiteral("Zeitglöcklein des Lebens und Leidens Christi", - "http://www.w3.org/2001/XMLSchema#string".toSmartIri) - )) + XsdLiteral( + "Zeitglöcklein des Lebens und Leidens Christi", + "http://www.w3.org/2001/XMLSchema#string".toSmartIri + ) + ) + ) ), positiveEntities = Set( IriRef("http://api.knora.org/ontology/knora-api/simple/v2#isMainResource".toSmartIri, None), @@ -1649,24 +1899,27 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:10Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/c5058f3a/values/c3295339", - valueHasUUID = stringFormatter.decodeUuid("c3295339"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi") + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/c5058f3a/values/c3295339", + valueHasUUID = stringFormatter.decodeUuid("c3295339"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:10Z"), + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -1682,24 +1935,27 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { projectADM = SharedTestDataADM.incunabulaProject, creationDate = Instant.parse("2016-03-02T15:05:23Z"), values = Map( - "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector(ReadTextValueV2( - valueContent = TextValueContentV2( - ontologySchema = InternalSchema, - valueHasLanguage = None, - comment = None, - maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi") - ), - valueHasMaxStandoffStartIndex = None, - valueIri = "http://rdfh.ch/0803/ff17e5ef9601/values/d9a522845006", - valueHasUUID = stringFormatter.decodeUuid("d9a522845006"), - permissions = - "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", - userPermission = ViewPermission, - previousValueIri = None, - valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), - attachedToUser = "http://rdfh.ch/users/91e19f1e01", - deletionInfo = None - ))), + "http://www.knora.org/ontology/0803/incunabula#title".toSmartIri -> Vector( + ReadTextValueV2( + valueContent = TextValueContentV2( + ontologySchema = InternalSchema, + valueHasLanguage = None, + comment = None, + maybeValueHasString = Some("Zeitgl\u00F6cklein des Lebens und Leidens Christi") + ), + valueHasMaxStandoffStartIndex = None, + valueIri = "http://rdfh.ch/0803/ff17e5ef9601/values/d9a522845006", + valueHasUUID = stringFormatter.decodeUuid("d9a522845006"), + permissions = + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser", + userPermission = ViewPermission, + previousValueIri = None, + valueCreationDate = Instant.parse("2016-03-02T15:05:23Z"), + attachedToUser = "http://rdfh.ch/users/91e19f1e01", + deletionInfo = None + ) + ) + ), lastModificationDate = None, versionDate = None, deletionInfo = None @@ -1716,10 +1972,12 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { XsdLiteral("true", "http://www.w3.org/2001/XMLSchema#boolean".toSmartIri), None ), - StatementPattern(QueryVariable("book"), - IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), - QueryVariable("title"), - None) + StatementPattern( + QueryVariable("book"), + IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), + QueryVariable("title"), + None + ) ), querySchema = Some(ApiV2Simple) ), @@ -1737,10 +1995,12 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { IriRef("http://api.knora.org/ontology/knora-api/simple/v2#Resource".toSmartIri, None), None ), - StatementPattern(QueryVariable("book"), - IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), - QueryVariable("title"), - None), + StatementPattern( + QueryVariable("book"), + IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), + QueryVariable("title"), + None + ), StatementPattern( IriRef("http://0.0.0.0:3333/ontology/0803/incunabula/simple/v2#title".toSmartIri, None), IriRef("http://api.knora.org/ontology/knora-api/simple/v2#objectType".toSmartIri, None), @@ -1757,9 +2017,12 @@ class SearchResponderV2SpecFullData(implicit stringFormatter: StringFormatter) { CompareExpression( QueryVariable("title"), CompareExpressionOperator.NOT_EQUALS, - XsdLiteral("Zeitglöcklein des Lebens und Leidens Christi", - "http://www.w3.org/2001/XMLSchema#string".toSmartIri) - )) + XsdLiteral( + "Zeitglöcklein des Lebens und Leidens Christi", + "http://www.w3.org/2001/XMLSchema#string".toSmartIri + ) + ) + ) ), positiveEntities = Set( IriRef("http://api.knora.org/ontology/knora-api/simple/v2#isMainResource".toSmartIri, None), diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala index b2f4802b97..2d876111f3 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala @@ -52,8 +52,8 @@ import org.knora.webapi.util.MutableTestIri import scala.concurrent.duration._ /** - * Tests [[ValuesResponderV2]]. - */ + * Tests [[ValuesResponderV2]]. + */ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -71,11 +71,14 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { /* we need to run our app with the mocked sipi actor */ override lazy val appActor: ActorRef = system.actorOf( Props(new ApplicationActor with ManagersWithMockedSipi).withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME) + name = APPLICATION_MANAGER_ACTOR_NAME + ) override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/responders.v2.ValuesResponderV2Spec/incunabula-data.ttl", - name = "http://www.knora.org/data/0803/incunabula"), + RdfDataObject( + path = "test_data/responders.v2.ValuesResponderV2Spec/incunabula-data.ttl", + name = "http://www.knora.org/data/0803/incunabula" + ), RdfDataObject(path = "test_data/demo_data/images-demo-data.ttl", name = "http://www.knora.org/data/00FF/images"), RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") ) @@ -158,8 +161,11 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { originalXMLID = None, startIndex = 0, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = aThingIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = aThingIri + ) + ) ), StandoffTagV2( standoffTagClassIri = OntologyConstants.Standoff.StandoffParagraphTag.toSmartIri, @@ -173,9 +179,11 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { private var standardMapping: Option[MappingXMLtoStandoff] = None - private def getResourceWithValues(resourceIri: IRI, - propertyIrisForGravsearch: Seq[SmartIri], - requestingUser: UserADM): ReadResourceV2 = { + private def getResourceWithValues( + resourceIri: IRI, + propertyIrisForGravsearch: Seq[SmartIri], + requestingUser: UserADM + ): ReadResourceV2 = { // Make a Gravsearch query from a template. val gravsearchQuery: String = org.knora.webapi.messages.twirl.queries.gravsearch.txt .getResourceWithSpecifiedProperties( @@ -196,36 +204,42 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = requestingUser ) - expectMsgPF(timeout) { - case searchResponse: ReadResourcesSequenceV2 => - searchResponse.toResource(resourceIri).toOntologySchema(ApiV2Complex) + expectMsgPF(timeout) { case searchResponse: ReadResourcesSequenceV2 => + searchResponse.toResource(resourceIri).toOntologySchema(ApiV2Complex) } } - private def getValuesFromResource(resource: ReadResourceV2, propertyIriInResult: SmartIri): Seq[ReadValueV2] = { + private def getValuesFromResource(resource: ReadResourceV2, propertyIriInResult: SmartIri): Seq[ReadValueV2] = resource.values.getOrElse( propertyIriInResult, - throw AssertionException(s"Resource <${resource.resourceIri}> does not have property <$propertyIriInResult>")) - } + throw AssertionException(s"Resource <${resource.resourceIri}> does not have property <$propertyIriInResult>") + ) - private def getValueFromResource(resource: ReadResourceV2, - propertyIriInResult: SmartIri, - expectedValueIri: IRI): ReadValueV2 = { + private def getValueFromResource( + resource: ReadResourceV2, + propertyIriInResult: SmartIri, + expectedValueIri: IRI + ): ReadValueV2 = { val propertyValues: Seq[ReadValueV2] = getValuesFromResource(resource = resource, propertyIriInResult = propertyIriInResult) propertyValues .find(_.valueIri == expectedValueIri) - .getOrElse(throw AssertionException( - s"Property <$propertyIriInResult> of resource <${resource.resourceIri}> does not have value <$expectedValueIri>")) + .getOrElse( + throw AssertionException( + s"Property <$propertyIriInResult> of resource <${resource.resourceIri}> does not have value <$expectedValueIri>" + ) + ) } - private def checkValueIsDeleted(resourceIri: IRI, - maybePreviousLastModDate: Option[Instant], - propertyIriForGravsearch: SmartIri, - propertyIriInResult: SmartIri, - valueIri: IRI, - customDeleteDate: Option[Instant] = None, - requestingUser: UserADM): Unit = { + private def checkValueIsDeleted( + resourceIri: IRI, + maybePreviousLastModDate: Option[Instant], + propertyIriForGravsearch: SmartIri, + propertyIriInResult: SmartIri, + valueIri: IRI, + customDeleteDate: Option[Instant] = None, + requestingUser: UserADM + ): Unit = { val resource = getResourceWithValues( resourceIri = resourceIri, propertyIrisForGravsearch = Seq(propertyIriForGravsearch), @@ -258,25 +272,26 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case sparqlSelectResponse: SparqlSelectResult => - val savedDeleteDateStr = sparqlSelectResponse.getFirstRow.rowMap("deleteDate") + expectMsgPF(timeout) { case sparqlSelectResponse: SparqlSelectResult => + val savedDeleteDateStr = sparqlSelectResponse.getFirstRow.rowMap("deleteDate") - val savedDeleteDate: Instant = stringFormatter.xsdDateTimeStampToInstant( - savedDeleteDateStr, - throw AssertionException(s"Couldn't parse delete date from triplestore: $savedDeleteDateStr") - ) + val savedDeleteDate: Instant = stringFormatter.xsdDateTimeStampToInstant( + savedDeleteDateStr, + throw AssertionException(s"Couldn't parse delete date from triplestore: $savedDeleteDateStr") + ) - assert(savedDeleteDate == deleteDate) + assert(savedDeleteDate == deleteDate) } case None => () } } - private def checkLastModDate(resourceIri: IRI, - maybePreviousLastModDate: Option[Instant], - maybeUpdatedLastModDate: Option[Instant]): Unit = { + private def checkLastModDate( + resourceIri: IRI, + maybePreviousLastModDate: Option[Instant], + maybeUpdatedLastModDate: Option[Instant] + ): Unit = maybeUpdatedLastModDate match { case Some(updatedLastModDate) => maybePreviousLastModDate match { @@ -286,15 +301,16 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { case None => throw AssertionException(s"Resource $resourceIri has no knora-base:lastModificationDate") } - } - private def getValue(resourceIri: IRI, - maybePreviousLastModDate: Option[Instant], - propertyIriForGravsearch: SmartIri, - propertyIriInResult: SmartIri, - expectedValueIri: IRI, - requestingUser: UserADM, - checkLastModDateChanged: Boolean = true): ReadValueV2 = { + private def getValue( + resourceIri: IRI, + maybePreviousLastModDate: Option[Instant], + propertyIriForGravsearch: SmartIri, + propertyIriInResult: SmartIri, + expectedValueIri: IRI, + requestingUser: UserADM, + checkLastModDateChanged: Boolean = true + ): ReadValueV2 = { val resource = getResourceWithValues( resourceIri = resourceIri, propertyIrisForGravsearch = Seq(propertyIriForGravsearch), @@ -324,62 +340,59 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = requestingUser ) - expectMsgPF(timeout) { - case previewResponse: ReadResourcesSequenceV2 => - val resourcePreview: ReadResourceV2 = previewResponse.toResource(resourceIri) - resourcePreview.lastModificationDate + expectMsgPF(timeout) { case previewResponse: ReadResourcesSequenceV2 => + val resourcePreview: ReadResourceV2 = previewResponse.toResource(resourceIri) + resourcePreview.lastModificationDate } } private def getValueUUID(valueIri: IRI): Option[UUID] = { val sparqlQuery = s""" - |PREFIX knora-base: - | - |SELECT ?valueUUID WHERE { - | <$valueIri> knora-base:valueHasUUID ?valueUUID . - |} + |PREFIX knora-base: + | + |SELECT ?valueUUID WHERE { + | <$valueIri> knora-base:valueHasUUID ?valueUUID . + |} """.stripMargin storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings - if (rows.isEmpty) { - None - } else if (rows.size > 1) { - throw AssertionException(s"Expected one knora-base:valueHasUUID, got ${rows.size}") - } else { - Some(stringFormatter.base64DecodeUuid(rows.head.rowMap("valueUUID"))) - } + if (rows.isEmpty) { + None + } else if (rows.size > 1) { + throw AssertionException(s"Expected one knora-base:valueHasUUID, got ${rows.size}") + } else { + Some(stringFormatter.base64DecodeUuid(rows.head.rowMap("valueUUID"))) + } } } private def getValuePermissions(valueIri: IRI): Option[UUID] = { val sparqlQuery = s""" - |PREFIX knora-base: - | - |SELECT ?valuePermissions WHERE { - | <$valueIri> knora-base:hasPermissions ?valuePermissions . - |} + |PREFIX knora-base: + | + |SELECT ?valuePermissions WHERE { + | <$valueIri> knora-base:hasPermissions ?valuePermissions . + |} """.stripMargin storeManager ! SparqlSelectRequest(sparqlQuery) - expectMsgPF(timeout) { - case response: SparqlSelectResult => - val rows = response.results.bindings + expectMsgPF(timeout) { case response: SparqlSelectResult => + val rows = response.results.bindings - if (rows.isEmpty) { - None - } else if (rows.size > 1) { - throw AssertionException(s"Expected one knora-base:hasPermissions, got ${rows.size}") - } else { - Some(stringFormatter.base64DecodeUuid(rows.head.rowMap("valuePermissions"))) - } + if (rows.isEmpty) { + None + } else if (rows.size > 1) { + throw AssertionException(s"Expected one knora-base:hasPermissions, got ${rows.size}") + } else { + Some(stringFormatter.base64DecodeUuid(rows.head.rowMap("valuePermissions"))) + } } } @@ -390,9 +403,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { requestingUser = KnoraSystemInstances.Users.SystemUser ) - expectMsgPF(timeout) { - case mappingResponse: GetMappingResponseV2 => - standardMapping = Some(mappingResponse.mapping) + expectMsgPF(timeout) { case mappingResponse: GetMappingResponseV2 => + standardMapping = Some(mappingResponse.mapping) } } @@ -420,11 +432,10 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - intValueIri.set(createValueResponse.valueIri) - firstIntValueVersionIri.set(createValueResponse.valueIri) - integerValueUUID = createValueResponse.valueUUID + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + intValueIri.set(createValueResponse.valueIri) + firstIntValueVersionIri.set(createValueResponse.valueIri) + integerValueUUID = createValueResponse.valueUUID } // Read the value back to check that it was added correctly. @@ -464,8 +475,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -505,10 +516,9 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - intValueIri.set(updateValueResponse.valueIri) - assert(updateValueResponse.valueUUID == integerValueUUID) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueIri.set(updateValueResponse.valueIri) + assert(updateValueResponse.valueUUID == integerValueUUID) } // Read the value back to check that it was added correctly. @@ -557,8 +567,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -600,8 +610,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -653,8 +663,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -696,8 +706,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -751,9 +761,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - intValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + intValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -801,9 +810,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - intValueIriWithCustomPermissions.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + intValueIriWithCustomPermissions.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -821,7 +829,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { case savedValue: IntegerValueContentV2 => savedValue.valueHasInteger should ===(intValue) PermissionUtilADM.parsePermissions(valueFromTriplestore.permissions) should ===( - PermissionUtilADM.parsePermissions(permissions)) + PermissionUtilADM.parsePermissions(permissions) + ) case _ => throw AssertionException(s"Expected integer value, got $valueFromTriplestore") } @@ -849,8 +858,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -877,8 +886,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -909,8 +918,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => intValueForRsyncIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + intValueForRsyncIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -957,8 +966,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -989,9 +998,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - intValueForRsyncIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueForRsyncIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1041,9 +1049,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - intValueForRsyncIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueForRsyncIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1086,8 +1093,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -1111,9 +1118,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - zeitglöckleinCommentWithoutStandoffIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + zeitglöckleinCommentWithoutStandoffIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1152,8 +1158,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1179,9 +1185,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - zeitglöckleinCommentWithCommentIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + zeitglöckleinCommentWithCommentIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1228,9 +1233,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - zeitglöckleinCommentWithStandoffIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + zeitglöckleinCommentWithStandoffIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1278,8 +1282,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1306,8 +1310,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => decimalValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + decimalValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1347,8 +1351,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1375,8 +1379,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => timeValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + timeValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1424,8 +1428,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => dateValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + dateValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1476,8 +1480,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1504,8 +1508,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => booleanValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + booleanValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1549,8 +1553,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => geometryValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + geometryValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1591,8 +1595,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1621,8 +1625,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => intervalValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + intervalValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1667,8 +1671,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1695,8 +1699,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => listValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + listValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1738,8 +1742,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1763,8 +1767,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -1791,8 +1795,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => colorValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + colorValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1834,8 +1838,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1862,8 +1866,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => uriValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + uriValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1905,8 +1909,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -1933,8 +1937,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => geonameValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + geonameValueIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -1976,8 +1980,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -2004,10 +2008,9 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! createValueRequest - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - linkValueIri.set(createValueResponse.valueIri) - linkValueUUID = createValueResponse.valueUUID + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + linkValueIri.set(createValueResponse.valueIri) + linkValueUUID = createValueResponse.valueUUID } val valueFromTriplestore = getValue( @@ -2049,8 +2052,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! createValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -2075,8 +2078,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! createValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -2096,8 +2099,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -2121,8 +2124,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -2146,8 +2149,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -2171,8 +2174,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -2195,8 +2198,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[OntologyConstraintException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[OntologyConstraintException]) } } @@ -2220,8 +2223,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[OntologyConstraintException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[OntologyConstraintException]) } // The cardinality of incunabula:seqnum in incunabula:page is 0-1, and page http://rdfh.ch/0803/4f11adaf already has a seqnum. @@ -2241,8 +2244,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[OntologyConstraintException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[OntologyConstraintException]) } } @@ -2259,12 +2262,16 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { startPosition = 31, endPosition = 39, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 0 - )) + ) + ) responderManager ! CreateValueRequestV2( CreateValueV2( @@ -2284,8 +2291,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => lobComment1Iri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + lobComment1Iri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2352,12 +2359,16 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { startPosition = 30, endPosition = 38, attributes = Vector( - StandoffTagIriAttributeV2(standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, - value = zeitglöckleinIri)), + StandoffTagIriAttributeV2( + standoffPropertyIri = OntologyConstants.KnoraBase.StandoffTagHasLink.toSmartIri, + value = zeitglöckleinIri + ) + ), uuid = UUID.randomUUID(), originalXMLID = None, startIndex = 0 - )) + ) + ) responderManager ! CreateValueRequestV2( CreateValueV2( @@ -2377,8 +2388,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => lobComment2Iri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + lobComment2Iri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2454,8 +2465,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -2480,8 +2491,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -2509,8 +2520,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2556,8 +2567,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -2584,8 +2595,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -2612,8 +2623,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -2647,8 +2658,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2685,8 +2696,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -2709,8 +2720,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -2733,8 +2744,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -2759,8 +2770,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -2785,9 +2796,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - zeitglöckleinCommentWithoutStandoffIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + zeitglöckleinCommentWithoutStandoffIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2832,9 +2842,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - zeitglöckleinCommentWithStandoffIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + zeitglöckleinCommentWithStandoffIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2903,8 +2912,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -2932,9 +2941,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => - zeitglöckleinSecondCommentWithStandoffIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + zeitglöckleinSecondCommentWithStandoffIri.set(createValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -2983,8 +2991,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3013,9 +3021,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - zeitglöckleinSecondCommentWithStandoffIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + zeitglöckleinSecondCommentWithStandoffIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3064,8 +3071,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3089,8 +3096,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3116,8 +3123,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => decimalValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + decimalValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3158,8 +3165,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3185,8 +3192,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => timeValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + timeValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3227,8 +3234,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3259,8 +3266,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => dateValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + dateValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3312,8 +3319,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3339,8 +3346,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => booleanValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + booleanValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3381,8 +3388,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3409,8 +3416,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => geometryValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + geometryValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3452,8 +3459,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3481,8 +3488,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => intervalValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + intervalValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3528,8 +3535,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3555,8 +3562,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => listValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + listValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3599,8 +3606,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3625,8 +3632,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[NotFoundException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[NotFoundException]) } } @@ -3652,8 +3659,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => colorValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + colorValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3696,8 +3703,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3723,8 +3730,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => uriValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + uriValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3767,8 +3774,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3794,8 +3801,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => geonameValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + geonameValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -3838,8 +3845,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3867,13 +3874,12 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - linkValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + linkValueIri.set(updateValueResponse.valueIri) - // When you change a link value's target, it gets a new UUID. - assert(updateValueResponse.valueUUID != linkValueUUID) - linkValueUUID = updateValueResponse.valueUUID + // When you change a link value's target, it gets a new UUID. + assert(updateValueResponse.valueUUID != linkValueUUID) + linkValueUUID = updateValueResponse.valueUUID } val valueFromTriplestore = getValue( @@ -3916,8 +3922,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -3947,12 +3953,11 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - linkValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + linkValueIri.set(updateValueResponse.valueIri) - // Since we only changed metadata, the link should have the same UUID. - assert(updateValueResponse.valueUUID == linkValueUUID) + // Since we only changed metadata, the link should have the same UUID. + assert(updateValueResponse.valueUUID == linkValueUUID) } val valueFromTriplestore = getValue( @@ -3998,8 +4003,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -4029,12 +4034,11 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => - linkValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + linkValueIri.set(updateValueResponse.valueIri) - // Since we only changed metadata, the link should have the same UUID. - assert(updateValueResponse.valueUUID == linkValueUUID) + // Since we only changed metadata, the link should have the same UUID. + assert(updateValueResponse.valueUUID == linkValueUUID) } val valueFromTriplestore = getValue( @@ -4081,8 +4085,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! createValueRequest - expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => linkValueIri.set(createValueResponse.valueIri) + expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + linkValueIri.set(createValueResponse.valueIri) } val valueFromTriplestore = getValue( @@ -4121,8 +4125,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -4157,8 +4161,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[DuplicateValueException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[DuplicateValueException]) } } @@ -4205,8 +4209,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => stillImageFileValueIri.set(updateValueResponse.valueIri) + expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + stillImageFileValueIri.set(updateValueResponse.valueIri) } // Read the value back to check that it was added correctly. @@ -4260,9 +4264,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -4296,8 +4299,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[SipiException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[SipiException]) } } @@ -4317,8 +4320,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -4395,8 +4398,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[BadRequestException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[BadRequestException]) } } @@ -4481,8 +4484,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[OntologyConstraintException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[OntologyConstraintException]) } } @@ -4526,8 +4529,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => assert(msg.cause.isInstanceOf[ForbiddenException]) + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[ForbiddenException]) } } @@ -4641,8 +4644,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - val textValue1Iri: IRI = expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => createValueResponse.valueIri + val textValue1Iri: IRI = expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + createValueResponse.valueIri } val resourceVersion1: ReadResourceV2 = getResourceWithValues( @@ -4685,8 +4688,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - val textValue2Version1Iri: IRI = expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => createValueResponse.valueIri + val textValue2Version1Iri: IRI = expectMsgPF(timeout) { case createValueResponse: CreateValueResponseV2 => + createValueResponse.valueIri } val resourceVersion2: ReadResourceV2 = getResourceWithValues( @@ -4739,8 +4742,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) - val textValue2Version2Iri = expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => updateValueResponse.valueIri + val textValue2Version2Iri = expectMsgPF(timeout) { case updateValueResponse: UpdateValueResponseV2 => + updateValueResponse.valueIri } val resourceVersion3: ReadResourceV2 = getResourceWithValues( @@ -4768,7 +4771,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { resourceVersion3 .values(OntologyConstants.KnoraApiV2Complex.HasStandoffLinkToValue.toSmartIri) .head - .valueIri == standoffLinkValueVersion2.valueIri) + .valueIri == standoffLinkValueVersion2.valueIri + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/CacheSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/CacheSpec.scala index f34dde40ad..f99f12b3c5 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/CacheSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/CacheSpec.scala @@ -86,12 +86,12 @@ class CacheSpec extends IntegrationSpec(TestContainerFuseki.PortConfig) { "successfully load all ontologies" in { val ontologiesFromCacheFuture: Future[Map[SmartIri, ReadOntologyV2]] = for { _ <- Cache.loadOntologies( - settings, - fusekiActor, - defaultFeatureFactoryConfig, - KnoraSystemInstances.Users.SystemUser - ) - cacheData: Cache.OntologyCacheData <- Cache.getCacheData + settings, + fusekiActor, + defaultFeatureFactoryConfig, + KnoraSystemInstances.Users.SystemUser + ) + cacheData: Cache.OntologyCacheData <- Cache.getCacheData ontologies: Map[SmartIri, ReadOntologyV2] = cacheData.ontologies } yield ontologies @@ -107,11 +107,11 @@ class CacheSpec extends IntegrationSpec(TestContainerFuseki.PortConfig) { "removing a property from an ontology," should { "remove the property from the cache." in { - val iri: SmartIri = stringFormatter.toSmartIri(additionalTestData.head.name) + val iri: SmartIri = stringFormatter.toSmartIri(additionalTestData.head.name) val hasTitlePropertyIri = stringFormatter.toSmartIri(s"${additionalTestData.head.name}#hasTitle") val previousCacheDataFuture = Cache.getCacheData - val previousCacheData = Await.result(previousCacheDataFuture, 2 seconds) + val previousCacheData = Await.result(previousCacheDataFuture, 2 seconds) val previousBooksMaybe = previousCacheData.ontologies.get(iri) previousBooksMaybe match { @@ -158,11 +158,11 @@ class CacheSpec extends IntegrationSpec(TestContainerFuseki.PortConfig) { "add a value property to the cache." in { - val iri: SmartIri = stringFormatter.toSmartIri(additionalTestData.head.name) + val iri: SmartIri = stringFormatter.toSmartIri(additionalTestData.head.name) val hasDescriptionPropertyIri = stringFormatter.toSmartIri(s"${additionalTestData.head.name}#hasDescription") val previousCacheDataFuture = Cache.getCacheData - val previousCacheData = Await.result(previousCacheDataFuture, 2 seconds) + val previousCacheData = Await.result(previousCacheDataFuture, 2 seconds) val previousBooksMaybe = previousCacheData.ontologies.get(iri) previousBooksMaybe match { @@ -249,9 +249,9 @@ class CacheSpec extends IntegrationSpec(TestContainerFuseki.PortConfig) { "add a link property and a link value property to the cache." in { - val ontologyIri = stringFormatter.toSmartIri("http://www.knora.org/ontology/0001/books") + val ontologyIri = stringFormatter.toSmartIri("http://www.knora.org/ontology/0001/books") val hasPagePropertyIri = stringFormatter.toSmartIri("http://www.knora.org/ontology/0001/books#hasPage") - val pagePropertyIri = stringFormatter.toSmartIri("http://www.knora.org/ontology/0001/books#Page") + val pagePropertyIri = stringFormatter.toSmartIri("http://www.knora.org/ontology/0001/books#Page") val hasPageValuePropertyIri = stringFormatter.toSmartIri("http://www.knora.org/ontology/0001/books#hasPageValue") val bookIri = stringFormatter.toSmartIri("http://rdfh.ch/0001/book-instance-01") @@ -303,7 +303,7 @@ class CacheSpec extends IntegrationSpec(TestContainerFuseki.PortConfig) { isLinkValueProp = true ) val newProps = previousBooks.properties + - (hasPagePropertyIri -> hasPageProperties) + + (hasPagePropertyIri -> hasPageProperties) + (hasPageValuePropertyIri -> hasPageValueProperties) val newBooks = previousBooks.copy( ontologyMetadata = previousBooks.ontologyMetadata.copy( diff --git a/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala b/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala index b03430cd0c..d059d637b4 100644 --- a/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala @@ -57,7 +57,8 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic system, responderManager, timeout, - executionContext) + executionContext + ) resF map { res => assert(res == AuthenticatorSpec.rootUser) } @@ -70,7 +71,8 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic system, responderManager, timeout, - executionContext) + executionContext + ) resF map { res => assertThrows(BadCredentialsException) } @@ -78,12 +80,14 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic "fail when not providing anything " in { an[BadRequestException] should be thrownBy { - Authenticator invokePrivate getUserByIdentifier(UserIdentifierADM(), - defaultFeatureFactoryConfig, - system, - responderManager, - timeout, - executionContext) + Authenticator invokePrivate getUserByIdentifier( + UserIdentifierADM(), + defaultFeatureFactoryConfig, + system, + responderManager, + timeout, + executionContext + ) } } } @@ -91,13 +95,17 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic "called, the 'authenticateCredentialsV2' method" should { "succeed with correct email/password" in { val correctPasswordCreds = - KnoraPasswordCredentialsV2(UserIdentifierADM(maybeEmail = Some(AuthenticatorSpec.rootUserEmail)), - AuthenticatorSpec.rootUserPassword) - val resF = Authenticator invokePrivate authenticateCredentialsV2(Some(correctPasswordCreds), - defaultFeatureFactoryConfig, - system, - responderManager, - executionContext) + KnoraPasswordCredentialsV2( + UserIdentifierADM(maybeEmail = Some(AuthenticatorSpec.rootUserEmail)), + AuthenticatorSpec.rootUserPassword + ) + val resF = Authenticator invokePrivate authenticateCredentialsV2( + Some(correctPasswordCreds), + defaultFeatureFactoryConfig, + system, + responderManager, + executionContext + ) resF map { res => assert(res) } @@ -105,24 +113,30 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic "fail with unknown email" in { val wrongPasswordCreds = KnoraPasswordCredentialsV2(UserIdentifierADM(maybeEmail = Some("wrongemail@example.com")), "wrongpassword") - val resF = Authenticator invokePrivate authenticateCredentialsV2(Some(wrongPasswordCreds), - defaultFeatureFactoryConfig, - system, - responderManager, - executionContext) + val resF = Authenticator invokePrivate authenticateCredentialsV2( + Some(wrongPasswordCreds), + defaultFeatureFactoryConfig, + system, + responderManager, + executionContext + ) resF map { res => assertThrows(BadCredentialsException) } } "fail with wrong password" in { val wrongPasswordCreds = - KnoraPasswordCredentialsV2(UserIdentifierADM(maybeEmail = Some(AuthenticatorSpec.rootUserEmail)), - "wrongpassword") - val resF = Authenticator invokePrivate authenticateCredentialsV2(Some(wrongPasswordCreds), - defaultFeatureFactoryConfig, - system, - responderManager, - executionContext) + KnoraPasswordCredentialsV2( + UserIdentifierADM(maybeEmail = Some(AuthenticatorSpec.rootUserEmail)), + "wrongpassword" + ) + val resF = Authenticator invokePrivate authenticateCredentialsV2( + Some(wrongPasswordCreds), + defaultFeatureFactoryConfig, + system, + responderManager, + executionContext + ) resF map { res => assertThrows(BadCredentialsException) } @@ -130,11 +144,13 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic "succeed with correct token" in { val token = JWTHelper.createToken("myuseriri", settings.jwtSecretKey, settings.jwtLongevity) val tokenCreds = KnoraTokenCredentialsV2(token) - val resF = Authenticator invokePrivate authenticateCredentialsV2(Some(tokenCreds), - defaultFeatureFactoryConfig, - system, - responderManager, - executionContext) + val resF = Authenticator invokePrivate authenticateCredentialsV2( + Some(tokenCreds), + defaultFeatureFactoryConfig, + system, + responderManager, + executionContext + ) resF map { res => assert(res) } @@ -143,22 +159,26 @@ class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with Implic val token = JWTHelper.createToken("myuseriri", settings.jwtSecretKey, settings.jwtLongevity) val tokenCreds = KnoraTokenCredentialsV2(token) CacheUtil.put(AUTHENTICATION_INVALIDATION_CACHE_NAME, tokenCreds.token, tokenCreds.token) - val resF = Authenticator invokePrivate authenticateCredentialsV2(Some(tokenCreds), - defaultFeatureFactoryConfig, - system, - responderManager, - executionContext) + val resF = Authenticator invokePrivate authenticateCredentialsV2( + Some(tokenCreds), + defaultFeatureFactoryConfig, + system, + responderManager, + executionContext + ) resF map { res => assertThrows(BadCredentialsException) } } "fail with wrong token" in { val tokenCreds = KnoraTokenCredentialsV2("123456") - val resF = Authenticator invokePrivate authenticateCredentialsV2(Some(tokenCreds), - defaultFeatureFactoryConfig, - system, - responderManager, - executionContext) + val resF = Authenticator invokePrivate authenticateCredentialsV2( + Some(tokenCreds), + defaultFeatureFactoryConfig, + system, + responderManager, + executionContext + ) resF map { res => assertThrows(BadCredentialsException) } diff --git a/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala b/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala index 64b6843254..60467f4681 100644 --- a/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala @@ -27,12 +27,12 @@ import spray.json.JsString object JWTHelperSpec { val config: Config = ConfigFactory.parseString(""" - |app { - | akka.loglevel = "DEBUG" - | - | jwt-secret-key = "UP 4888, nice 4-8-4 steam engine" - | jwt-longevity = 36500 days - |} + |app { + | akka.loglevel = "DEBUG" + | + | jwt-secret-key = "UP 4888, nice 4-8-4 steam engine" + | jwt-longevity = 36500 days + |} """.stripMargin) } diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedListsTestDataADM.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedListsTestDataADM.scala index 51fc0a4ded..93284800ca 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedListsTestDataADM.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedListsTestDataADM.scala @@ -15,7 +15,7 @@ object SharedListsTestDataADM { projectIri = "http://rdfh.ch/projects/0001", name = None, labels = StringLiteralSequenceV2(Vector(StringLiteralV2("Tree list root", Some("en")))), - comments = StringLiteralSequenceV2(Vector.empty[StringLiteralV2]), + comments = StringLiteralSequenceV2(Vector.empty[StringLiteralV2]) ) val summerNodeInfo: ListChildNodeInfoADM = ListChildNodeInfoADM( @@ -74,8 +74,11 @@ object SharedListsTestDataADM { projectIri = "http://rdfh.ch/projects/0001", name = Some("treelistroot"), labels = StringLiteralSequenceV2( - Vector(StringLiteralV2(value = "Listenwurzel", language = Some("de")), - StringLiteralV2(value = "Tree list root", language = Some("en")))), + Vector( + StringLiteralV2(value = "Listenwurzel", language = Some("de")), + StringLiteralV2(value = "Tree list root", language = Some("en")) + ) + ), comments = StringLiteralSequenceV2(Vector(StringLiteralV2(value = "Anything Tree List", language = Some("en")))) ) @@ -101,8 +104,11 @@ object SharedListsTestDataADM { id = "http://rdfh.ch/lists/0001/treeList02", name = Some("Tree list node 02"), labels = StringLiteralSequenceV2( - Vector(StringLiteralV2(value = "Baumlistenknoten 02", language = Some("de")), - StringLiteralV2(value = "Tree list node 02", language = Some("en")))), + Vector( + StringLiteralV2(value = "Baumlistenknoten 02", language = Some("de")), + StringLiteralV2(value = "Tree list node 02", language = Some("en")) + ) + ), comments = StringLiteralSequenceV2(Vector.empty[StringLiteralV2]), children = Seq.empty[ListChildNodeADM], position = 1, diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala index b2db5b1922..b06455782f 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala @@ -26,60 +26,60 @@ object SharedOntologyTestDataADM { val LocalHost_Ontology = "http://0.0.0.0:3333/ontology" // anything - val ANYTHING_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/anything" - val ANYTHING_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/anything/v2" - val ANYTHING_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0001/anything" + val ANYTHING_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/anything" + val ANYTHING_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/anything/v2" + val ANYTHING_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0001/anything" val ANYTHING_THING_RESOURCE_CLASS_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "Thing" val ANYTHING_HasListItem_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasListItem" - val ANYTHING_HasDate_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasDate" + val ANYTHING_HasDate_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasDate" // freetest - val FREETEST_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/freetest" + val FREETEST_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/freetest" val FREETEST_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/freetest/v2" // minimal - val MINIMAL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/minimal" + val MINIMAL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/minimal" val MINIMAL_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/minimal/v2" // images - val IMAGES_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/00FF/images" - val IMAGES_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/00FF/images/v2" - val IMAGES_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/00FF/images" - val IMAGES_TITEL_PROPERTY: IRI = IMAGES_ONTOLOGY_IRI + "#" + "titel" - val IMAGES_TITEL_PROPERTY_LocalHost: IRI = IMAGES_ONTOLOGY_IRI_LocalHost + "#" + "titel" - val IMAGES_BILD_RESOURCE_CLASS: IRI = IMAGES_ONTOLOGY_IRI + "#" + "bild" + val IMAGES_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/00FF/images" + val IMAGES_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/00FF/images/v2" + val IMAGES_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/00FF/images" + val IMAGES_TITEL_PROPERTY: IRI = IMAGES_ONTOLOGY_IRI + "#" + "titel" + val IMAGES_TITEL_PROPERTY_LocalHost: IRI = IMAGES_ONTOLOGY_IRI_LocalHost + "#" + "titel" + val IMAGES_BILD_RESOURCE_CLASS: IRI = IMAGES_ONTOLOGY_IRI + "#" + "bild" val IMAGES_BILD_RESOURCE_CLASS_LocalHost: IRI = IMAGES_ONTOLOGY_IRI_LocalHost + "#" + "bild" // beol - val BEOL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/beol" + val BEOL_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/beol" val BEOL_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0801/beol/v2" - val BEOL_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/beol" + val BEOL_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/beol" // biblio - val BIBLIO_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/biblio" + val BIBLIO_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0801/biblio" val BIBLIO_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0801/biblio/v2" - val BIBLIO_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/biblio" + val BIBLIO_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0801/biblio" // incunabula - val INCUNABULA_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0803/incunabula" - val INCUNABULA_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0803/incunabula/v2" - val INCUNABULA_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0803/incunabula" - val INCUNABULA_BOOK_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "book" + val INCUNABULA_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0803/incunabula" + val INCUNABULA_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0803/incunabula/v2" + val INCUNABULA_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0803/incunabula" + val INCUNABULA_BOOK_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "book" val INCUNABULA_BOOK_RESOURCE_CLASS_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "book" - val INCUNABULA_PAGE_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "page" + val INCUNABULA_PAGE_RESOURCE_CLASS: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "page" val INCUNABULA_PAGE_RESOURCE_CLASS_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "page" - val INCUNABULA_PartOf_Property: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "partOfValue" - val INCUNABULA_PartOf_Property_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "partOfValue" + val INCUNABULA_PartOf_Property: IRI = INCUNABULA_ONTOLOGY_IRI + "#" + "partOfValue" + val INCUNABULA_PartOf_Property_LocalHost: IRI = INCUNABULA_ONTOLOGY_IRI_LocalHost + "#" + "partOfValue" // dokubib - val DOKUBIB_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0804/dokubib" + val DOKUBIB_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0804/dokubib" val DOKUBIB_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0804/dokubib/v2" - val DOKUBIB_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0804/dokubib" + val DOKUBIB_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0804/dokubib" // webern - val WEBERN_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0806/webern" + val WEBERN_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0806/webern" val WEBERN_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0806/webern/v2" - val WEBERN_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0806/webern" + val WEBERN_DATA_IRI: IRI = OntologyConstants.NamedGraphs.DataNamedGraphStart + "/0806/webern" //foo ontology val FOO_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/00FF/foo/v2" diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedPermissionsTestData.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedPermissionsTestData.scala index 350d319956..1fdb1456b6 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedPermissionsTestData.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedPermissionsTestData.scala @@ -35,13 +35,17 @@ case class oap(iri: String, p: ObjectAccessPermissionADM) case class doap(iri: String, p: DefaultObjectAccessPermissionADM) /** - * This object holds data representations for the data in 'test_data/all_data/permissions-data.ttl'. - */ + * This object holds data representations for the data in 'test_data/all_data/permissions-data.ttl'. + */ object SharedPermissionsTestData { - /*************************************/ - /** Knora System Permissions **/ - /*************************************/ + /** + * ********************************** + */ + /** Knora System Permissions * */ + /** + * ********************************** + */ val perm001_d1: doap = doap( iri = "http://rdfh.ch/permissions/0000/001-d1", @@ -88,9 +92,13 @@ object SharedPermissionsTestData { ) ) - /*************************************/ - /** Images Demo Project Permissions **/ - /*************************************/ + /** + * ********************************** + */ + /** Images Demo Project Permissions * */ + /** + * ********************************** + */ val perm002_a1: ap = ap( iri = "http://rdfh.ch/permissions/00FF/a1", @@ -172,9 +180,13 @@ object SharedPermissionsTestData { ) ) - /*************************************/ - /** Incunabula Project Permissions **/ - /*************************************/ + /** + * ********************************** + */ + /** Incunabula Project Permissions * */ + /** + * ********************************** + */ val perm003_a1: ap = ap( iri = "http://rdfh.ch/permissions/0803/003-a1", @@ -303,9 +315,13 @@ object SharedPermissionsTestData { ) ) - /************************************/ - /** Anything Project Permissions **/ - /************************************/ + /** + * ********************************* + */ + /** Anything Project Permissions * */ + /** + * ********************************* + */ val perm005_a1: ap = ap( iri = "http://rdfh.ch/permissions/0001/005-a1", diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala index b459434422..a58f620c6e 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala @@ -31,14 +31,18 @@ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.messages.util.KnoraSystemInstances /** - * This object holds the same user which are loaded with 'test_data/all_data/admin-data.ttl'. Using this object - * in tests, allows easier updating of details as they change over time. - */ + * This object holds the same user which are loaded with 'test_data/all_data/admin-data.ttl'. Using this object + * in tests, allows easier updating of details as they change over time. + */ object SharedTestDataADM { - /** ***********************************/ - /** System Admin Data **/ - /** ***********************************/ + /** + * ********************************** + */ + /** System Admin Data * */ + /** + * ********************************** + */ val SYSTEM_PROJECT_IRI: IRI = OntologyConstants.KnoraAdmin.SystemProject // built-in project val testPass: String = java.net.URLEncoder.encode("test", "utf-8") @@ -144,11 +148,15 @@ object SharedTestDataADM { sessionId = None, permissions = PermissionsDataADM( groupsPerProject = Map( - INCUNABULA_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin), - IMAGES_PROJECT_IRI -> List("http://rdfh.ch/groups/00FF/images-reviewer", - OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + INCUNABULA_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ), + IMAGES_PROJECT_IRI -> List( + "http://rdfh.ch/groups/00FF/images-reviewer", + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) ), administrativePermissionsPerProject = Map( INCUNABULA_PROJECT_IRI -> Set( @@ -182,8 +190,8 @@ object SharedTestDataADM { selfjoin = false ) - val DefaultSharedOntologiesProjectIri - : IRI = OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject // built-in project + val DefaultSharedOntologiesProjectIri: IRI = + OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject // built-in project /* represents the full project info of the default shared ontologies project */ def defaultSharedOntologiesProject: ProjectADM = ProjectADM( @@ -199,9 +207,13 @@ object SharedTestDataADM { selfjoin = false ) - /** ***********************************/ - /** Images Demo Project Admin Data **/ - /** ***********************************/ + /** + * ********************************** + */ + /** Images Demo Project Admin Data * */ + /** + * ********************************** + */ val IMAGES_PROJECT_IRI = "http://rdfh.ch/projects/00FF" /* represents 'user01' as found in admin-data.ttl */ @@ -221,8 +233,10 @@ object SharedTestDataADM { sessionId = None, permissions = PermissionsDataADM( groupsPerProject = Map( - IMAGES_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + IMAGES_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) ), administrativePermissionsPerProject = Map( IMAGES_PROJECT_IRI -> Set( @@ -277,15 +291,19 @@ object SharedTestDataADM { sessionId = None, permissions = PermissionsDataADM( groupsPerProject = Map( - IMAGES_PROJECT_IRI -> List("http://rdfh.ch/groups/00FF/images-reviewer", - OntologyConstants.KnoraAdmin.ProjectMember) + IMAGES_PROJECT_IRI -> List( + "http://rdfh.ch/groups/00FF/images-reviewer", + OntologyConstants.KnoraAdmin.ProjectMember + ) ), administrativePermissionsPerProject = Map( IMAGES_PROJECT_IRI -> Set( PermissionADM.projectResourceCreateRestrictedPermission( - s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bild"), + s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bild" + ), PermissionADM.projectResourceCreateRestrictedPermission( - s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bildformat") + s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bildformat" + ) ) ) ) @@ -335,9 +353,13 @@ object SharedTestDataADM { selfjoin = false ) - /** ***********************************/ - /** Incunabula Project Admin Data **/ - /** ***********************************/ + /** + * ********************************** + */ + /** Incunabula Project Admin Data * */ + /** + * ********************************** + */ val INCUNABULA_PROJECT_IRI = "http://rdfh.ch/projects/0803" /* represents 'testuser' (Incunabula ProjectAdmin) as found in admin-data.ttl */ @@ -357,8 +379,10 @@ object SharedTestDataADM { sessionId = None, permissions = PermissionsDataADM( groupsPerProject = Map( - INCUNABULA_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + INCUNABULA_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) ), administrativePermissionsPerProject = Map( INCUNABULA_PROJECT_IRI -> Set( @@ -434,7 +458,8 @@ object SharedTestDataADM { value = "

Das interdisziplinäre Forschungsprojekt \"Die Bilderfolgen der Basler Frühdrucke: Spätmittelalterliche Didaxe als Bild-Text-Lektüre\" verbindet eine umfassende kunstwissenschaftliche Analyse der Bezüge zwischen den Bildern und Texten in den illustrierten Basler Inkunabeln mit der Digitalisierung der Bestände der Universitätsbibliothek und der Entwicklung einer elektronischen Edition in der Form einer neuartigen Web-0.2-Applikation.\n

\n

Das Projekt wird durchgeführt vom Kunsthistorischen Seminar der Universität Basel (Prof. B. Schellewald) und dem Digital Humanities Lab der Universität Basel (PD Dr. L. Rosenthaler).\n

\n

\nDas Kernstück der digitalen Edition besteht aus rund zwanzig reich bebilderten Frühdrucken aus vier verschiedenen Basler Offizinen. Viele davon sind bereits vor 1500 in mehreren Ausgaben erschienen, einige fast gleichzeitig auf Deutsch und Lateinisch. Es handelt sich um eine ausserordentlich vielfältige Produktion; neben dem Heilsspiegel finden sich ein Roman, die Melusine, die Reisebeschreibungen des Jean de Mandeville, einige Gebets- und Erbauungsbüchlein, theologische Schriften, Fastenpredigten, die Leben der Heiligen Fridolin und Meinrad, das berühmte Narrenschiff sowie die Exempelsammlung des Ritters vom Thurn.\n

\nDie Internetpublikation macht das digitalisierte Korpus dieser Frühdrucke durch die Möglichkeiten nichtlinearer Verknüpfung und Kommentierung der Bilder und Texte, für die wissenschaftliche Edition sowie für die Erforschung der Bilder und Texte nutzbar machen. Auch können bereits bestehende und entstehende Online-Editionen damit verknüpft werden , wodurch die Nutzung von Datenbanken anderer Institutionen im Hinblick auf unser Corpus optimiert wird.\n

", language = None - )), + ) + ), keywords = Seq( "Basler Frühdrucke", "Inkunabel", @@ -457,9 +482,13 @@ object SharedTestDataADM { selfjoin = false ) - /** **********************************/ - /** Anything Admin Data **/ - /** **********************************/ + /** + * ********************************* + */ + /** Anything Admin Data * */ + /** + * ********************************* + */ val ANYTHING_PROJECT_IRI = "http://rdfh.ch/projects/0001" val customResourceIRI: IRI = "http://rdfh.ch/0001/aUrDPcJRmFNzBHW_AlR1hw" @@ -489,8 +518,10 @@ object SharedTestDataADM { sessionId = None, permissions = PermissionsDataADM( groupsPerProject = Map( - ANYTHING_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + ANYTHING_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) ), administrativePermissionsPerProject = Map( ANYTHING_PROJECT_IRI -> Set( @@ -517,8 +548,10 @@ object SharedTestDataADM { sessionId = None, permissions = PermissionsDataADM( groupsPerProject = Map( - ANYTHING_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - "http://rdfh.ch/groups/0001/thing-searcher") + ANYTHING_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + "http://rdfh.ch/groups/0001/thing-searcher" + ) ), administrativePermissionsPerProject = Map( ANYTHING_PROJECT_IRI -> Set( @@ -577,9 +610,13 @@ object SharedTestDataADM { selfjoin = true ) - /** **********************************/ - /** BEOL **/ - /** **********************************/ + /** + * ********************************* + */ + /** BEOL * */ + /** + * ********************************* + */ val BEOL_PROJECT_IRI = "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF" def beolProject: ProjectADM = ProjectADM( @@ -626,9 +663,13 @@ object SharedTestDataADM { ) ) - /** **********************************/ - /** DOKUBIB **/ - /** **********************************/ + /** + * ********************************* + */ + /** DOKUBIB * */ + /** + * ********************************* + */ val DOKUBIB_PROJECT_IRI = "http://rdfh.ch/projects/0804" def dokubibProject: ProjectADM = ProjectADM( diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataV1.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataV1.scala index be78632e59..e69cdeabdd 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataV1.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataV1.scala @@ -8,14 +8,18 @@ import org.knora.webapi.messages.v1.responder.usermessages.{UserDataV1, UserProf import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI /** - * This object holds the same user which are loaded with 'test_data/all_data/admin-data.ttl'. Using this object - * in tests, allows easier updating of details as they change over time. - */ + * This object holds the same user which are loaded with 'test_data/all_data/admin-data.ttl'. Using this object + * in tests, allows easier updating of details as they change over time. + */ object SharedTestDataV1 { - /** ***********************************/ - /** System Admin Data **/ - /** ***********************************/ + /** + * ********************************** + */ + /** System Admin Data * */ + /** + * ********************************** + */ val SYSTEM_PROJECT_IRI: IRI = OntologyConstants.KnoraAdmin.SystemProject // built-in project /* represents the user profile of 'root' as found in admin-data.ttl */ @@ -50,11 +54,15 @@ object SharedTestDataV1 { sessionId = None, permissionData = PermissionsDataADM( groupsPerProject = Map( - INCUNABULA_PROJECT_IRI -> List(s"${OntologyConstants.KnoraAdmin.ProjectMember}", - s"${OntologyConstants.KnoraAdmin.ProjectAdmin}"), - IMAGES_PROJECT_IRI -> List("http://rdfh.ch/groups/00FF/images-reviewer", - s"${OntologyConstants.KnoraAdmin.ProjectMember}", - s"${OntologyConstants.KnoraAdmin.ProjectAdmin}") + INCUNABULA_PROJECT_IRI -> List( + s"${OntologyConstants.KnoraAdmin.ProjectMember}", + s"${OntologyConstants.KnoraAdmin.ProjectAdmin}" + ), + IMAGES_PROJECT_IRI -> List( + "http://rdfh.ch/groups/00FF/images-reviewer", + s"${OntologyConstants.KnoraAdmin.ProjectMember}", + s"${OntologyConstants.KnoraAdmin.ProjectAdmin}" + ) ), administrativePermissionsPerProject = Map( INCUNABULA_PROJECT_IRI -> Set( @@ -74,9 +82,13 @@ object SharedTestDataV1 { SharedTestDataADM.systemProject.asProjectInfoV1 .copy(ontologies = Seq(OntologyConstants.KnoraBase.KnoraBaseOntologyIri)) - /** ***********************************/ - /** Images Demo Project Admin Data **/ - /** ***********************************/ + /** + * ********************************** + */ + /** Images Demo Project Admin Data * */ + /** + * ********************************** + */ val IMAGES_PROJECT_IRI = "http://rdfh.ch/projects/00FF" /* represents 'user01' as found in admin-data.ttl */ @@ -96,8 +108,10 @@ object SharedTestDataV1 { sessionId = None, permissionData = PermissionsDataADM( groupsPerProject = Map( - IMAGES_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + IMAGES_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) ), administrativePermissionsPerProject = Map( IMAGES_PROJECT_IRI -> Set( @@ -152,8 +166,10 @@ object SharedTestDataV1 { sessionId = None, permissionData = PermissionsDataADM( groupsPerProject = Map( - IMAGES_PROJECT_IRI -> List("http://rdfh.ch/groups/00FF/images-reviewer", - s"${OntologyConstants.KnoraAdmin.ProjectMember}") + IMAGES_PROJECT_IRI -> List( + "http://rdfh.ch/groups/00FF/images-reviewer", + s"${OntologyConstants.KnoraAdmin.ProjectMember}" + ) ), administrativePermissionsPerProject = Map( IMAGES_PROJECT_IRI -> Set( @@ -179,9 +195,13 @@ object SharedTestDataV1 { selfjoin = false ) - /** ***********************************/ - /** Incunabula Project Admin Data **/ - /** ***********************************/ + /** + * ********************************** + */ + /** Incunabula Project Admin Data * */ + /** + * ********************************** + */ val INCUNABULA_PROJECT_IRI = "http://rdfh.ch/projects/0803" /* represents 'testuser' (Incunabula ProjectAdmin) as found in admin-data.ttl */ @@ -201,8 +221,10 @@ object SharedTestDataV1 { sessionId = None, permissionData = PermissionsDataADM( groupsPerProject = Map( - INCUNABULA_PROJECT_IRI -> List(s"${OntologyConstants.KnoraAdmin.ProjectMember}", - s"${OntologyConstants.KnoraAdmin.ProjectAdmin}") + INCUNABULA_PROJECT_IRI -> List( + s"${OntologyConstants.KnoraAdmin.ProjectMember}", + s"${OntologyConstants.KnoraAdmin.ProjectAdmin}" + ) ), administrativePermissionsPerProject = Map( INCUNABULA_PROJECT_IRI -> Set( @@ -274,9 +296,11 @@ object SharedTestDataV1 { shortcode = "0803", longname = Some("Bilderfolgen Basler Frühdrucke"), description = Some( - "

Das interdisziplinäre Forschungsprojekt \"Die Bilderfolgen der Basler Frühdrucke: Spätmittelalterliche Didaxe als Bild-Text-Lektüre\" verbindet eine umfassende kunstwissenschaftliche Analyse der Bezüge zwischen den Bildern und Texten in den illustrierten Basler Inkunabeln mit der Digitalisierung der Bestände der Universitätsbibliothek und der Entwicklung einer elektronischen Edition in der Form einer neuartigen Web-0.2-Applikation.\n

\n

Das Projekt wird durchgeführt vom Kunsthistorischen Seminar der Universität Basel (Prof. B. Schellewald) und dem Digital Humanities Lab der Universität Basel (PD Dr. L. Rosenthaler).\n

\n

\nDas Kernstück der digitalen Edition besteht aus rund zwanzig reich bebilderten Frühdrucken aus vier verschiedenen Basler Offizinen. Viele davon sind bereits vor 1500 in mehreren Ausgaben erschienen, einige fast gleichzeitig auf Deutsch und Lateinisch. Es handelt sich um eine ausserordentlich vielfältige Produktion; neben dem Heilsspiegel finden sich ein Roman, die Melusine, die Reisebeschreibungen des Jean de Mandeville, einige Gebets- und Erbauungsbüchlein, theologische Schriften, Fastenpredigten, die Leben der Heiligen Fridolin und Meinrad, das berühmte Narrenschiff sowie die Exempelsammlung des Ritters vom Thurn.\n

\nDie Internetpublikation macht das digitalisierte Korpus dieser Frühdrucke durch die Möglichkeiten nichtlinearer Verknüpfung und Kommentierung der Bilder und Texte, für die wissenschaftliche Edition sowie für die Erforschung der Bilder und Texte nutzbar machen. Auch können bereits bestehende und entstehende Online-Editionen damit verknüpft werden , wodurch die Nutzung von Datenbanken anderer Institutionen im Hinblick auf unser Corpus optimiert wird.\n

"), + "

Das interdisziplinäre Forschungsprojekt \"Die Bilderfolgen der Basler Frühdrucke: Spätmittelalterliche Didaxe als Bild-Text-Lektüre\" verbindet eine umfassende kunstwissenschaftliche Analyse der Bezüge zwischen den Bildern und Texten in den illustrierten Basler Inkunabeln mit der Digitalisierung der Bestände der Universitätsbibliothek und der Entwicklung einer elektronischen Edition in der Form einer neuartigen Web-0.2-Applikation.\n

\n

Das Projekt wird durchgeführt vom Kunsthistorischen Seminar der Universität Basel (Prof. B. Schellewald) und dem Digital Humanities Lab der Universität Basel (PD Dr. L. Rosenthaler).\n

\n

\nDas Kernstück der digitalen Edition besteht aus rund zwanzig reich bebilderten Frühdrucken aus vier verschiedenen Basler Offizinen. Viele davon sind bereits vor 1500 in mehreren Ausgaben erschienen, einige fast gleichzeitig auf Deutsch und Lateinisch. Es handelt sich um eine ausserordentlich vielfältige Produktion; neben dem Heilsspiegel finden sich ein Roman, die Melusine, die Reisebeschreibungen des Jean de Mandeville, einige Gebets- und Erbauungsbüchlein, theologische Schriften, Fastenpredigten, die Leben der Heiligen Fridolin und Meinrad, das berühmte Narrenschiff sowie die Exempelsammlung des Ritters vom Thurn.\n

\nDie Internetpublikation macht das digitalisierte Korpus dieser Frühdrucke durch die Möglichkeiten nichtlinearer Verknüpfung und Kommentierung der Bilder und Texte, für die wissenschaftliche Edition sowie für die Erforschung der Bilder und Texte nutzbar machen. Auch können bereits bestehende und entstehende Online-Editionen damit verknüpft werden , wodurch die Nutzung von Datenbanken anderer Institutionen im Hinblick auf unser Corpus optimiert wird.\n

" + ), keywords = Some( - "Basel, Basler Frühdrucke, Bilderfolgen, Contectualisation of images, Inkunabel, Kunsthistorisches Seminar Universität Basel, Late Middle Ages, Letterpress Printing, Narrenschiff, Sebastian Brant, Wiegendrucke, early print, incunabula, ship of fools"), + "Basel, Basler Frühdrucke, Bilderfolgen, Contectualisation of images, Inkunabel, Kunsthistorisches Seminar Universität Basel, Late Middle Ages, Letterpress Printing, Narrenschiff, Sebastian Brant, Wiegendrucke, early print, incunabula, ship of fools" + ), logo = Some("incunabula_logo.png"), institution = None, ontologies = Seq(SharedOntologyTestDataADM.INCUNABULA_ONTOLOGY_IRI), @@ -284,9 +308,13 @@ object SharedTestDataV1 { selfjoin = false ) - /** **********************************/ - /** Anything Admin Data **/ - /** **********************************/ + /** + * ********************************* + */ + /** Anything Admin Data * */ + /** + * ********************************* + */ val ANYTHING_PROJECT_IRI = "http://rdfh.ch/projects/0001" def anythingAdminUser = UserProfileV1( @@ -305,8 +333,10 @@ object SharedTestDataV1 { sessionId = None, permissionData = PermissionsDataADM( groupsPerProject = Map( - ANYTHING_PROJECT_IRI -> List(OntologyConstants.KnoraAdmin.ProjectMember, - OntologyConstants.KnoraAdmin.ProjectAdmin) + ANYTHING_PROJECT_IRI -> List( + OntologyConstants.KnoraAdmin.ProjectMember, + OntologyConstants.KnoraAdmin.ProjectAdmin + ) ), administrativePermissionsPerProject = Map( ANYTHING_PROJECT_IRI -> Set( @@ -383,9 +413,13 @@ object SharedTestDataV1 { selfjoin = false ) - /** **********************************/ - /** BEOL **/ - /** **********************************/ + /** + * ********************************* + */ + /** BEOL * */ + /** + * ********************************* + */ val BEOL_PROJECT_IRI = "http://rdfh.ch/projects/0801" def beolProjectInfo = ProjectInfoV1( @@ -419,9 +453,13 @@ object SharedTestDataV1 { permissionData = PermissionsDataADM() ) - /** **********************************/ - /** DOKUBIB **/ - /** **********************************/ + /** + * ********************************* + */ + /** DOKUBIB * */ + /** + * ********************************* + */ val DOKUBIB_PROJECT_IRI = "http://rdfh.ch/projects/00FE" def dokubibProjectInfo = ProjectInfoV1( @@ -438,9 +476,13 @@ object SharedTestDataV1 { selfjoin = false ) - /** **********************************/ - /** WEBERN **/ - /** **********************************/ + /** + * ********************************* + */ + /** WEBERN * */ + /** + * ********************************* + */ val WEBERN_PROJECT_IRI = "http://rdfh.ch/projects/0806" def webernProjectInfo = ProjectInfoV1( diff --git a/webapi/src/test/scala/org/knora/webapi/store/MockableStoreManager.scala b/webapi/src/test/scala/org/knora/webapi/store/MockableStoreManager.scala index 7a64febed2..52e5be9634 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/MockableStoreManager.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/MockableStoreManager.scala @@ -30,11 +30,12 @@ class MockableStoreManager(mockStoreConnectors: Map[String, ActorRef], appActor: with LiveActorMaker { /** - * Starts the MockableIIIFManager - */ + * Starts the MockableIIIFManager + */ override lazy val iiifManager: ActorRef = makeActor( Props(new MockableIIIFManager(mockStoreConnectors) with LiveActorMaker) .withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - IIIFManagerActorName) + IIIFManagerActorName + ) } diff --git a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/CacheServiceManagerSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/CacheServiceManagerSpec.scala index c171010723..dce416490e 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/CacheServiceManagerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/CacheServiceManagerSpec.scala @@ -41,8 +41,8 @@ object CacheServiceManagerSpec { } /** - * This spec is used to test [[org.knora.webapi.store.cacheservice.serialization.CacheSerialization]]. - */ + * This spec is used to test [[org.knora.webapi.store.cacheservice.serialization.CacheSerialization]]. + */ class CacheServiceManagerSpec extends CoreSpec(CacheServiceManagerSpec.config) { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance diff --git a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImplSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImplSpec.scala index c0dba213d0..87a9d4f059 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImplSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/inmem/CacheServiceInMemImplSpec.scala @@ -31,55 +31,54 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM */ class CacheServiceInMemImplSpec extends UnitSpec() { - implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global + implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance + implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global + private val user: UserADM = SharedTestDataADM.imagesUser01 + private val project: ProjectADM = SharedTestDataADM.imagesProject - private val user: UserADM = SharedTestDataADM.imagesUser01 - private val project: ProjectADM = SharedTestDataADM.imagesProject + private val inMemCache: CacheServiceInMemImpl.type = CacheServiceInMemImpl - private val inMemCache: CacheServiceInMemImpl.type = CacheServiceInMemImpl + "The CacheServiceInMemImpl" should { - "The CacheServiceInMemImpl" should { - - "successfully store a user" in { - val resFuture = inMemCache.putUserADM(user) - resFuture map {res => res should equal(true)} - } + "successfully store a user" in { + val resFuture = inMemCache.putUserADM(user) + resFuture map { res => res should equal(true) } + } - "successfully retrieve a user by IRI" in { - val resFuture = inMemCache.getUserADM(UserIdentifierADM(maybeIri = Some(user.id))) - resFuture map {res => res should equal(Some(user))} - } + "successfully retrieve a user by IRI" in { + val resFuture = inMemCache.getUserADM(UserIdentifierADM(maybeIri = Some(user.id))) + resFuture map { res => res should equal(Some(user)) } + } - "successfully retrieve a user by USERNAME" in { - val resFuture = inMemCache.getUserADM(UserIdentifierADM(maybeUsername = Some(user.username))) - resFuture map {res => res should equal(Some(user))} - } + "successfully retrieve a user by USERNAME" in { + val resFuture = inMemCache.getUserADM(UserIdentifierADM(maybeUsername = Some(user.username))) + resFuture map { res => res should equal(Some(user)) } + } - "successfully retrieve a user by EMAIL" in { - val resFuture = inMemCache.getUserADM(UserIdentifierADM(maybeEmail = Some(user.email))) - resFuture map {res => res should equal(Some(user))} - } + "successfully retrieve a user by EMAIL" in { + val resFuture = inMemCache.getUserADM(UserIdentifierADM(maybeEmail = Some(user.email))) + resFuture map { res => res should equal(Some(user)) } + } - "successfully store a project" in { - val resFuture = inMemCache.putProjectADM(project) - resFuture map { res => res should equal(true)} - } + "successfully store a project" in { + val resFuture = inMemCache.putProjectADM(project) + resFuture map { res => res should equal(true) } + } - "successfully retrieve a project by IRI" in { - val resFuture = inMemCache.getProjectADM(ProjectIdentifierADM(maybeIri = Some(project.id))) - resFuture map { res => res should equal(Some(project))} - } + "successfully retrieve a project by IRI" in { + val resFuture = inMemCache.getProjectADM(ProjectIdentifierADM(maybeIri = Some(project.id))) + resFuture map { res => res should equal(Some(project)) } + } - "successfully retrieve a project by SHORTNAME" in { - val resFuture = inMemCache.getProjectADM(ProjectIdentifierADM(maybeShortname = Some(project.shortname))) - resFuture map { res => res should equal(Some(project))} - } + "successfully retrieve a project by SHORTNAME" in { + val resFuture = inMemCache.getProjectADM(ProjectIdentifierADM(maybeShortname = Some(project.shortname))) + resFuture map { res => res should equal(Some(project)) } + } - "successfully retrieve a project by SHORTCODE" in { - val resFuture = inMemCache.getProjectADM(ProjectIdentifierADM(maybeShortcode = Some(project.shortcode))) - resFuture map { res => res should equal(Some(project))} - } + "successfully retrieve a project by SHORTCODE" in { + val resFuture = inMemCache.getProjectADM(ProjectIdentifierADM(maybeShortcode = Some(project.shortcode))) + resFuture map { res => res should equal(Some(project)) } } + } } diff --git a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala index 22d68c7195..bfe7727e20 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/redis/CacheServiceRedisImplSpec.scala @@ -37,9 +37,9 @@ import scala.concurrent.ExecutionContext class CacheServiceRedisImplSpec extends UnitSpec(TestContainerRedis.PortConfig) { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - implicit val ec: ExecutionContext = ExecutionContext.global + implicit val ec: ExecutionContext = ExecutionContext.global - private val user: UserADM = SharedTestDataADM.imagesUser01 + private val user: UserADM = SharedTestDataADM.imagesUser01 private val project: ProjectADM = SharedTestDataADM.imagesProject private val redisCache: CacheServiceRedisImpl = new CacheServiceRedisImpl( diff --git a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerializationSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerializationSpec.scala index 769c22511b..e99f89d1df 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerializationSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/cacheservice/serialization/CacheSerializationSpec.scala @@ -34,8 +34,8 @@ object CacheSerializationSpec { } /** - * This spec is used to test [[CacheSerialization]]. - */ + * This spec is used to test [[CacheSerialization]]. + */ class CacheSerializationSpec extends UnitSpec(CacheSerializationSpec.config) { implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global diff --git a/webapi/src/test/scala/org/knora/webapi/store/iiif/MockSipiConnector.scala b/webapi/src/test/scala/org/knora/webapi/store/iiif/MockSipiConnector.scala index dd45791009..cc4f869a70 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/iiif/MockSipiConnector.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/iiif/MockSipiConnector.scala @@ -31,21 +31,21 @@ import scala.concurrent.ExecutionContext import scala.util.{Failure, Success, Try} /** - * Constants for [[MockSipiConnector]]. - */ + * Constants for [[MockSipiConnector]]. + */ object MockSipiConnector { /** - * A request to [[MockSipiConnector]] with this filename will always cause the responder to simulate a Sipi - * error. - */ + * A request to [[MockSipiConnector]] with this filename will always cause the responder to simulate a Sipi + * error. + */ val FAILURE_FILENAME: String = "failure.jp2" } /** - * Takes the place of [[SipiConnector]] for tests without an actual Sipi server, by returning hard-coded responses - * simulating responses from Sipi. - */ + * Takes the place of [[SipiConnector]] for tests without an actual Sipi server, by returning hard-coded responses + * simulating responses from Sipi. + */ class MockSipiConnector extends Actor with ActorLogging { implicit val system: ActorSystem = context.system @@ -77,20 +77,18 @@ class MockSipiConnector extends Actor with ActorLogging { } private def moveTemporaryFileToPermanentStorage( - moveTemporaryFileToPermanentStorageRequestV2: MoveTemporaryFileToPermanentStorageRequest) - : Try[SuccessResponseV2] = { + moveTemporaryFileToPermanentStorageRequestV2: MoveTemporaryFileToPermanentStorageRequest + ): Try[SuccessResponseV2] = if (moveTemporaryFileToPermanentStorageRequestV2.internalFilename == MockSipiConnector.FAILURE_FILENAME) { Failure(SipiException("Sipi failed to move file to permanent storage")) } else { Success(SuccessResponseV2("Moved file to permanent storage")) } - } - private def deleteTemporaryFile(deleteTemporaryFileRequestV2: DeleteTemporaryFileRequest): Try[SuccessResponseV2] = { + private def deleteTemporaryFile(deleteTemporaryFileRequestV2: DeleteTemporaryFileRequest): Try[SuccessResponseV2] = if (deleteTemporaryFileRequestV2.internalFilename == MockSipiConnector.FAILURE_FILENAME) { Failure(SipiException("Sipi failed to delete temporary file")) } else { Success(SuccessResponseV2("Deleted temporary file")) } - } } diff --git a/webapi/src/test/scala/org/knora/webapi/store/iiif/MockableIIIFManager.scala b/webapi/src/test/scala/org/knora/webapi/store/iiif/MockableIIIFManager.scala index e5b74b56c0..c59c6f9b00 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/iiif/MockableIIIFManager.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/iiif/MockableIIIFManager.scala @@ -24,18 +24,18 @@ import org.knora.webapi.core.LiveActorMaker import org.knora.webapi.settings.SipiConnectorActorName /** - * A subclass of [[IIIFManager]] that allows tests to substitute standard connector for a custom one. - * - * @param mockStoreConnectors a [[Map]] containing the mock connectors to be used instead of the live ones. - * The name of the actor (a constant from [[org.knora.webapi.store]] is used as the - * key in the map. - */ + * A subclass of [[IIIFManager]] that allows tests to substitute standard connector for a custom one. + * + * @param mockStoreConnectors a [[Map]] containing the mock connectors to be used instead of the live ones. + * The name of the actor (a constant from [[org.knora.webapi.store]] is used as the + * key in the map. + */ class MockableIIIFManager(mockStoreConnectors: Map[String, ActorRef]) extends IIIFManager with LiveActorMaker { /** - * Initialised to the value of the key 'SipiConnectorActorName' in `mockStoreConnectors` if provided, otherwise - * the default SipiConnector is used. - */ + * Initialised to the value of the key 'SipiConnectorActorName' in `mockStoreConnectors` if provided, otherwise + * the default SipiConnector is used. + */ override lazy val sipiConnector: ActorRef = mockStoreConnectors.getOrElse(SipiConnectorActorName, makeDefaultSipiConnector) diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/AllTriplestoreSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/AllTriplestoreSpec.scala index ba14ae3101..a67cb972e8 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/AllTriplestoreSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/AllTriplestoreSpec.scala @@ -236,11 +236,10 @@ class AllTriplestoreSpec extends CoreSpec(AllTriplestoreSpec.config) with Implic //println("==>> Reset test case end") storeManager ! SparqlSelectRequest(countTriplesQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println(msg) - afterLoadCount = msg.results.bindings.head.rowMap("no").toInt - (afterLoadCount > 0) should ===(true) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println(msg) + afterLoadCount = msg.results.bindings.head.rowMap("no").toInt + (afterLoadCount > 0) should ===(true) } } } @@ -249,10 +248,9 @@ class AllTriplestoreSpec extends CoreSpec(AllTriplestoreSpec.config) with Implic //println("==>> Named Graph test case start") storeManager ! SparqlSelectRequest(namedGraphQuery) //println(result) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println(msg) - msg.results.bindings.nonEmpty should ===(true) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println(msg) + msg.results.bindings.nonEmpty should ===(true) } //println("==>> Named Graph test case end") } @@ -262,28 +260,25 @@ class AllTriplestoreSpec extends CoreSpec(AllTriplestoreSpec.config) with Implic //println("==>> Update 1 test case start") storeManager ! SparqlSelectRequest(countTriplesQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println("vor insert: " + msg) - msg.results.bindings.head.rowMap("no").toInt should ===(afterLoadCount) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println("vor insert: " + msg) + msg.results.bindings.head.rowMap("no").toInt should ===(afterLoadCount) } storeManager ! SparqlUpdateRequest(insertQuery) expectMsg(SparqlUpdateResponse()) storeManager ! SparqlSelectRequest(checkInsertQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println(msg) - msg.results.bindings.size should ===(3) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println(msg) + msg.results.bindings.size should ===(3) } storeManager ! SparqlSelectRequest(countTriplesQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println("nach instert" + msg) - afterChangeCount = msg.results.bindings.head.rowMap("no").toInt - (afterChangeCount - afterLoadCount) should ===(3) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println("nach instert" + msg) + afterChangeCount = msg.results.bindings.head.rowMap("no").toInt + (afterChangeCount - afterLoadCount) should ===(3) } //println("==>> Update 1 test case end") } @@ -291,27 +286,24 @@ class AllTriplestoreSpec extends CoreSpec(AllTriplestoreSpec.config) with Implic //println("==>> Update 2 test case start") storeManager ! SparqlSelectRequest(countTriplesQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println("vor revert: " + msg) - msg.results.bindings.head.rowMap("no").toInt should ===(afterChangeCount) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println("vor revert: " + msg) + msg.results.bindings.head.rowMap("no").toInt should ===(afterChangeCount) } storeManager ! SparqlUpdateRequest(revertInsertQuery) expectMsg(SparqlUpdateResponse()) storeManager ! SparqlSelectRequest(countTriplesQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println("nach revert: " + msg) - msg.results.bindings.head.rowMap("no").toInt should ===(afterLoadCount) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println("nach revert: " + msg) + msg.results.bindings.head.rowMap("no").toInt should ===(afterLoadCount) } storeManager ! SparqlSelectRequest(checkInsertQuery) - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println("check: " + msg) - msg.results.bindings.size should ===(0) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println("check: " + msg) + msg.results.bindings.size should ===(0) } //println("==>> Update 2 test case end") @@ -325,10 +317,9 @@ class AllTriplestoreSpec extends CoreSpec(AllTriplestoreSpec.config) with Implic storeManager ! SparqlSelectRequest(textSearchQueryGraphDBValueHasString) case _ => storeManager ! SparqlSelectRequest(textSearchQueryFusekiValueHasString) } - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println(msg) - msg.results.bindings.size should ===(3) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println(msg) + msg.results.bindings.size should ===(3) } } } @@ -340,10 +331,9 @@ class AllTriplestoreSpec extends CoreSpec(AllTriplestoreSpec.config) with Implic storeManager ! SparqlSelectRequest(textSearchQueryGraphDBRDFLabel) case _ => storeManager ! SparqlSelectRequest(textSearchQueryFusekiDRFLabel) } - expectMsgPF(timeout) { - case msg: SparqlSelectResult => - //println(msg) - msg.results.bindings.size should ===(1) + expectMsgPF(timeout) { case msg: SparqlSelectResult => + //println(msg) + msg.results.bindings.size should ===(1) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/GraphDBConsistencyCheckingSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/GraphDBConsistencyCheckingSpec.scala index 1e5ace676b..91dbf22303 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/GraphDBConsistencyCheckingSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/GraphDBConsistencyCheckingSpec.scala @@ -15,16 +15,18 @@ import scala.concurrent.duration._ import scala.language.postfixOps /** - * Tests the GraphDB triplestore consistency checking rules in webapi/scripts/KnoraRules.pie. - */ + * Tests the GraphDB triplestore consistency checking rules in webapi/scripts/KnoraRules.pie. + */ class GraphDBConsistencyCheckingSpec extends CoreSpec(GraphDBConsistencyCheckingSpec.config) with ImplicitSender { import GraphDBConsistencyCheckingSpec._ private val timeout = 30.seconds override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/store.triplestore.GraphDBConsistencyCheckingSpec/incunabula-data.ttl", - name = "http://www.knora.org/data/0803/incunabula"), + RdfDataObject( + path = "test_data/store.triplestore.GraphDBConsistencyCheckingSpec/incunabula-data.ttl", + name = "http://www.knora.org/data/0803/incunabula" + ), RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") ) @@ -38,96 +40,86 @@ class GraphDBConsistencyCheckingSpec extends CoreSpec(GraphDBConsistencyChecking "not create a new resource with a missing property that has owl:cardinality 1" in { storeManager ! SparqlUpdateRequest(missingPartOf) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - (msg.contains(s"$CONSISTENCY_CHECK_ERROR cardinality_1_not_less_any_object") && - msg.trim.endsWith( - "http://rdfh.ch/0803/missingPartOf http://www.knora.org/ontology/0803/incunabula#partOf *")) should ===( - true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + (msg.contains(s"$CONSISTENCY_CHECK_ERROR cardinality_1_not_less_any_object") && + msg.trim.endsWith( + "http://rdfh.ch/0803/missingPartOf http://www.knora.org/ontology/0803/incunabula#partOf *" + )) should ===(true) } } "not create a new resource with a missing property that has owl:minCardinality 1" in { storeManager ! SparqlUpdateRequest(missingComment) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - (msg.contains(s"$CONSISTENCY_CHECK_ERROR min_cardinality_1_any_object") && - msg.trim.endsWith( - "http://rdfh.ch/0803/missingComment http://www.knora.org/ontology/knora-base#hasComment *")) should ===( - true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + (msg.contains(s"$CONSISTENCY_CHECK_ERROR min_cardinality_1_any_object") && + msg.trim.endsWith( + "http://rdfh.ch/0803/missingComment http://www.knora.org/ontology/knora-base#hasComment *" + )) should ===(true) } } "not create a new resource with two values for a property that has owl:maxCardinality 1" in { storeManager ! SparqlUpdateRequest(tooManyPublocs) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR max_cardinality_1_with_deletion_flag") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR max_cardinality_1_with_deletion_flag") should ===(true) } } "not create a new resource with more than one lastModificationDate" in { storeManager ! SparqlUpdateRequest(tooManyLastModificationDates) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR max_cardinality_1_without_deletion_flag") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR max_cardinality_1_without_deletion_flag") should ===(true) } } "not create a new resource with a property that cannot have a resource as a subject" in { storeManager ! SparqlUpdateRequest(wrongSubjectClass) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR subject_class_constraint") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR subject_class_constraint") should ===(true) } } "not create a new resource with properties whose objects have the wrong types" in { storeManager ! SparqlUpdateRequest(wrongObjectClass) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR object_class_constraint") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR object_class_constraint") should ===(true) } } "not create a new resource with a link to a resource of the wrong class" in { storeManager ! SparqlUpdateRequest(wrongLinkTargetClass) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR object_class_constraint") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR object_class_constraint") should ===(true) } } "not create a new resource with a property for which there is no cardinality" in { storeManager ! SparqlUpdateRequest(resourcePropWithNoCardinality) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR resource_prop_cardinality_any") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR resource_prop_cardinality_any") should ===(true) } } "not create a new resource containing a value with a property for which there is no cardinality" in { storeManager ! SparqlUpdateRequest(valuePropWithNoCardinality) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR value_prop_cardinality_any") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR value_prop_cardinality_any") should ===(true) } } "not create a new resource with two labels" in { storeManager ! SparqlUpdateRequest(twoLabels) - expectMsgPF(timeout) { - case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => - msg.contains(s"$CONSISTENCY_CHECK_ERROR cardinality_1_not_greater_rdfs_label") should ===(true) + expectMsgPF(timeout) { case akka.actor.Status.Failure(TriplestoreResponseException(msg: String, _)) => + msg.contains(s"$CONSISTENCY_CHECK_ERROR cardinality_1_not_greater_rdfs_label") should ===(true) } } } else { @@ -147,2239 +139,2239 @@ object GraphDBConsistencyCheckingSpec { // Tries to create a new incunabula:page with a missing incunabula:partOf link. private val missingPartOf = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource0 rdf:type ?resourceClass0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label0 ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pagenum - | - | - | ?newValue0_1 rdf:type ?valueType0_1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue0_1 knora-base:valueHasString "recto" . - | - | - | - | ?newValue0_1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0_1 knora-base:valueHasOrder ?nextOrder0_1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource0 ?property0_1 ?newValue0_1 . - | - | - | - | - | # Value 2 - | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue - | - | - | ?newValue0_2 rdf:type ?valueType0_2 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid2" . - | - | - | ?newValue0_2 knora-base:originalFilename "test.jpg" ; - | knora-base:originalMimeType "image/jpeg" ; - | knora-base:internalFilename "full.jp2" ; - | knora-base:internalMimeType "image/jp2" ; - | knora-base:dimX 800 ; - | knora-base:dimY 800 . - | - | - | - | ?newValue0_2 knora-base:valueHasString "test.jpg" . - | - | - | ?newValue0_2 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0_2 knora-base:valueHasOrder ?nextOrder0_2 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource0 ?property0_2 ?newValue0_2 . - | - | - | - | - | # Value 3 - | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue - | - | - | ?newValue0_3 rdf:type ?valueType0_3 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid3" . - | - | - | ?newValue0_3 knora-base:originalFilename "test.jpg" ; - | knora-base:originalMimeType "image/jpeg" ; - | knora-base:internalFilename "thumb.jpg" ; - | knora-base:internalMimeType "image/jpeg" ; - | knora-base:dimX 80 ; - | knora-base:dimY 80 . - | - | - | ?newValue0_3 knora-base:isPreview true . - | - | - | ?newValue0_3 knora-base:valueHasString "test.jpg" . - | - | - | - | ?newValue0_3 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0_3 knora-base:valueHasOrder ?nextOrder0_3 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource0 ?property0_3 ?newValue0_3 . - | - | - | - | - | # Value 4 - | # Property: http://www.knora.org/ontology/0803/incunabula#hasRightSideband - | - | - | - | ?resource0 ?linkProperty0_4 ?linkTarget0_4 . - | - | - | - | ?newLinkValue0_4 rdf:type knora-base:LinkValue ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid4" ; - | rdf:subject ?resource0 ; - | rdf:predicate ?linkProperty0_4 ; - | rdf:object ?linkTarget0_4 ; - | knora-base:valueHasRefCount 1 ; - | - | knora-base:valueHasOrder ?nextOrder0_4 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | ?newLinkValue0_4 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?resource0 ?linkValueProperty0_4 ?newLinkValue0_4 . - | - | - | - | - | # Value 5 - | # Property: http://www.knora.org/ontology/0803/incunabula#origname - | - | - | ?newValue0_5 rdf:type ?valueType0_5 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid5" . - | - | - | - | ?newValue0_5 knora-base:valueHasString "Blatt" . - | - | - | - | - | ?newValue0_5 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0_5 knora-base:valueHasOrder ?nextOrder0_5 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource0 ?property0_5 ?newValue0_5 . - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#seqnum - | - | - | ?newValue0_6 rdf:type ?valueType0_6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue0_6 knora-base:valueHasInteger 1 ; - | knora-base:valueHasString "1" . - | - | - | ?newValue0_6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0_6 knora-base:valueHasOrder ?nextOrder0_6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource0 ?property0_6 ?newValue0_6 . - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf") AS ?resource0) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#page") AS ?resourceClass0) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Page") AS ?label0) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pagenum - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pagenum") AS ?property0_1) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/nQ3tRObaQWe74WQv2_OdCg") AS ?newValue0_1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0_1) - | - | - | - | ?property0_1 knora-base:objectClassConstraint ?propertyRange0_1 . - | ?valueType0_1 rdfs:subClassOf* ?propertyRange0_1 . - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_1 . - | ?restriction0_1 a owl:Restriction . - | ?restriction0_1 owl:onProperty ?property0_1 . - | - | - | - | - | BIND(0 AS ?nextOrder0_1) - | - | - | - | - | - | - | # Value 2 - | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue - | - | BIND(IRI("http://www.knora.org/ontology/knora-base#hasStillImageFileValue") AS ?property0_2) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/GVE754RbT1CykpMnwR3Csw") AS ?newValue0_2) - | BIND(IRI("http://www.knora.org/ontology/knora-base#StillImageFileValue") AS ?valueType0_2) - | - | - | - | ?property0_2 knora-base:objectClassConstraint ?propertyRange0_2 . - | ?valueType0_2 rdfs:subClassOf* ?propertyRange0_2 . - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_2 . - | ?restriction0_2 a owl:Restriction . - | ?restriction0_2 owl:onProperty ?property0_2 . - | - | - | - | - | BIND(0 AS ?nextOrder0_2) - | - | - | - | - | - | - | # Value 3 - | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue - | - | BIND(IRI("http://www.knora.org/ontology/knora-base#hasStillImageFileValue") AS ?property0_3) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/LOT71U6hSQu7shi76oRxWQ") AS ?newValue0_3) - | BIND(IRI("http://www.knora.org/ontology/knora-base#StillImageFileValue") AS ?valueType0_3) - | - | - | - | ?property0_3 knora-base:objectClassConstraint ?propertyRange0_3 . - | ?valueType0_3 rdfs:subClassOf* ?propertyRange0_3 . - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_3 . - | ?restriction0_3 a owl:Restriction . - | ?restriction0_3 owl:onProperty ?property0_3 . - | - | - | - | - | BIND(1 AS ?nextOrder0_3) - | - | - | - | - | - | - | # Value 4 - | # Property: http://www.knora.org/ontology/0803/incunabula#hasRightSideband - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#hasRightSideband") AS ?linkProperty0_4) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#hasRightSidebandValue") AS ?linkValueProperty0_4) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/i5tE5i-RRLOH631soexPFw") AS ?newLinkValue0_4) - | BIND(IRI("http://rdfh.ch/0803/482a33d65c36") AS ?linkTarget0_4) - | - | - | - | ?linkTarget0_4 rdf:type ?linkTargetClass0_4 . - | ?linkTargetClass0_4 rdfs:subClassOf+ knora-base:Resource . - | - | - | - | ?linkProperty0_4 knora-base:objectClassConstraint ?expectedTargetClass0_4 . - | ?linkTargetClass0_4 rdfs:subClassOf* ?expectedTargetClass0_4 . - | - | - | - | MINUS { - | ?linkTarget4 knora-base:isDeleted true . - | } - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_4 . - | ?restriction0_4 a owl:Restriction . - | ?restriction0_4 owl:onProperty ?linkProperty0_4 . - | - | - | - | - | BIND(0 AS ?nextOrder0_4) - | - | - | - | - | - | - | # Value 5 - | # Property: http://www.knora.org/ontology/0803/incunabula#origname - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#origname") AS ?property0_5) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/MLWWT-F8SlKsZmRo4JMLHw") AS ?newValue0_5) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0_5) - | - | - | - | ?property0_5 knora-base:objectClassConstraint ?propertyRange0_5 . - | ?valueType0_5 rdfs:subClassOf* ?propertyRange0_5 . - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_5 . - | ?restriction0_5 a owl:Restriction . - | ?restriction0_5 owl:onProperty ?property0_5 . - | - | - | - | - | BIND(0 AS ?nextOrder0_5) - | - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#seqnum - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#seqnum") AS ?property0_6) - | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/uWQtW_X3RxKjFyGrsQwbpQ") AS ?newValue0_6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#IntValue") AS ?valueType0_6) - | - | - | - | ?property0_6 knora-base:objectClassConstraint ?propertyRange0_6 . - | ?valueType0_6 rdfs:subClassOf* ?propertyRange0_6 . - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_6 . - | ?restriction0_6 a owl:Restriction . - | ?restriction0_6 owl:onProperty ?property0_6 . - | - | - | - | - | BIND(0 AS ?nextOrder0_6) - | - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource0 rdf:type ?resourceClass0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label0 ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pagenum + | + | + | ?newValue0_1 rdf:type ?valueType0_1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue0_1 knora-base:valueHasString "recto" . + | + | + | + | ?newValue0_1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0_1 knora-base:valueHasOrder ?nextOrder0_1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource0 ?property0_1 ?newValue0_1 . + | + | + | + | + | # Value 2 + | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue + | + | + | ?newValue0_2 rdf:type ?valueType0_2 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid2" . + | + | + | ?newValue0_2 knora-base:originalFilename "test.jpg" ; + | knora-base:originalMimeType "image/jpeg" ; + | knora-base:internalFilename "full.jp2" ; + | knora-base:internalMimeType "image/jp2" ; + | knora-base:dimX 800 ; + | knora-base:dimY 800 . + | + | + | + | ?newValue0_2 knora-base:valueHasString "test.jpg" . + | + | + | ?newValue0_2 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0_2 knora-base:valueHasOrder ?nextOrder0_2 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource0 ?property0_2 ?newValue0_2 . + | + | + | + | + | # Value 3 + | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue + | + | + | ?newValue0_3 rdf:type ?valueType0_3 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid3" . + | + | + | ?newValue0_3 knora-base:originalFilename "test.jpg" ; + | knora-base:originalMimeType "image/jpeg" ; + | knora-base:internalFilename "thumb.jpg" ; + | knora-base:internalMimeType "image/jpeg" ; + | knora-base:dimX 80 ; + | knora-base:dimY 80 . + | + | + | ?newValue0_3 knora-base:isPreview true . + | + | + | ?newValue0_3 knora-base:valueHasString "test.jpg" . + | + | + | + | ?newValue0_3 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0_3 knora-base:valueHasOrder ?nextOrder0_3 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource0 ?property0_3 ?newValue0_3 . + | + | + | + | + | # Value 4 + | # Property: http://www.knora.org/ontology/0803/incunabula#hasRightSideband + | + | + | + | ?resource0 ?linkProperty0_4 ?linkTarget0_4 . + | + | + | + | ?newLinkValue0_4 rdf:type knora-base:LinkValue ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid4" ; + | rdf:subject ?resource0 ; + | rdf:predicate ?linkProperty0_4 ; + | rdf:object ?linkTarget0_4 ; + | knora-base:valueHasRefCount 1 ; + | + | knora-base:valueHasOrder ?nextOrder0_4 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | ?newLinkValue0_4 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?resource0 ?linkValueProperty0_4 ?newLinkValue0_4 . + | + | + | + | + | # Value 5 + | # Property: http://www.knora.org/ontology/0803/incunabula#origname + | + | + | ?newValue0_5 rdf:type ?valueType0_5 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid5" . + | + | + | + | ?newValue0_5 knora-base:valueHasString "Blatt" . + | + | + | + | + | ?newValue0_5 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0_5 knora-base:valueHasOrder ?nextOrder0_5 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource0 ?property0_5 ?newValue0_5 . + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#seqnum + | + | + | ?newValue0_6 rdf:type ?valueType0_6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue0_6 knora-base:valueHasInteger 1 ; + | knora-base:valueHasString "1" . + | + | + | ?newValue0_6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0_6 knora-base:valueHasOrder ?nextOrder0_6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource0 ?property0_6 ?newValue0_6 . + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf") AS ?resource0) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#page") AS ?resourceClass0) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Page") AS ?label0) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pagenum + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pagenum") AS ?property0_1) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/nQ3tRObaQWe74WQv2_OdCg") AS ?newValue0_1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0_1) + | + | + | + | ?property0_1 knora-base:objectClassConstraint ?propertyRange0_1 . + | ?valueType0_1 rdfs:subClassOf* ?propertyRange0_1 . + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_1 . + | ?restriction0_1 a owl:Restriction . + | ?restriction0_1 owl:onProperty ?property0_1 . + | + | + | + | + | BIND(0 AS ?nextOrder0_1) + | + | + | + | + | + | + | # Value 2 + | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue + | + | BIND(IRI("http://www.knora.org/ontology/knora-base#hasStillImageFileValue") AS ?property0_2) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/GVE754RbT1CykpMnwR3Csw") AS ?newValue0_2) + | BIND(IRI("http://www.knora.org/ontology/knora-base#StillImageFileValue") AS ?valueType0_2) + | + | + | + | ?property0_2 knora-base:objectClassConstraint ?propertyRange0_2 . + | ?valueType0_2 rdfs:subClassOf* ?propertyRange0_2 . + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_2 . + | ?restriction0_2 a owl:Restriction . + | ?restriction0_2 owl:onProperty ?property0_2 . + | + | + | + | + | BIND(0 AS ?nextOrder0_2) + | + | + | + | + | + | + | # Value 3 + | # Property: http://www.knora.org/ontology/knora-base#hasStillImageFileValue + | + | BIND(IRI("http://www.knora.org/ontology/knora-base#hasStillImageFileValue") AS ?property0_3) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/LOT71U6hSQu7shi76oRxWQ") AS ?newValue0_3) + | BIND(IRI("http://www.knora.org/ontology/knora-base#StillImageFileValue") AS ?valueType0_3) + | + | + | + | ?property0_3 knora-base:objectClassConstraint ?propertyRange0_3 . + | ?valueType0_3 rdfs:subClassOf* ?propertyRange0_3 . + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_3 . + | ?restriction0_3 a owl:Restriction . + | ?restriction0_3 owl:onProperty ?property0_3 . + | + | + | + | + | BIND(1 AS ?nextOrder0_3) + | + | + | + | + | + | + | # Value 4 + | # Property: http://www.knora.org/ontology/0803/incunabula#hasRightSideband + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#hasRightSideband") AS ?linkProperty0_4) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#hasRightSidebandValue") AS ?linkValueProperty0_4) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/i5tE5i-RRLOH631soexPFw") AS ?newLinkValue0_4) + | BIND(IRI("http://rdfh.ch/0803/482a33d65c36") AS ?linkTarget0_4) + | + | + | + | ?linkTarget0_4 rdf:type ?linkTargetClass0_4 . + | ?linkTargetClass0_4 rdfs:subClassOf+ knora-base:Resource . + | + | + | + | ?linkProperty0_4 knora-base:objectClassConstraint ?expectedTargetClass0_4 . + | ?linkTargetClass0_4 rdfs:subClassOf* ?expectedTargetClass0_4 . + | + | + | + | MINUS { + | ?linkTarget4 knora-base:isDeleted true . + | } + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_4 . + | ?restriction0_4 a owl:Restriction . + | ?restriction0_4 owl:onProperty ?linkProperty0_4 . + | + | + | + | + | BIND(0 AS ?nextOrder0_4) + | + | + | + | + | + | + | # Value 5 + | # Property: http://www.knora.org/ontology/0803/incunabula#origname + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#origname") AS ?property0_5) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/MLWWT-F8SlKsZmRo4JMLHw") AS ?newValue0_5) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0_5) + | + | + | + | ?property0_5 knora-base:objectClassConstraint ?propertyRange0_5 . + | ?valueType0_5 rdfs:subClassOf* ?propertyRange0_5 . + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_5 . + | ?restriction0_5 a owl:Restriction . + | ?restriction0_5 owl:onProperty ?property0_5 . + | + | + | + | + | BIND(0 AS ?nextOrder0_5) + | + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#seqnum + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#seqnum") AS ?property0_6) + | BIND(IRI("http://rdfh.ch/0803/missingPartOf/values/uWQtW_X3RxKjFyGrsQwbpQ") AS ?newValue0_6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#IntValue") AS ?valueType0_6) + | + | + | + | ?property0_6 knora-base:objectClassConstraint ?propertyRange0_6 . + | ?valueType0_6 rdfs:subClassOf* ?propertyRange0_6 . + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_6 . + | ?restriction0_6 a owl:Restriction . + | ?restriction0_6 owl:onProperty ?property0_6 . + | + | + | + | + | BIND(0 AS ?nextOrder0_6) + | + |} """.stripMargin // Tries to create a knora-base:Annotation with a missing knora-base:hasComment. private val missingComment = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/knora-base#isAnnotationOf - | - | - | - | ?resource ?linkProperty0 ?linkTarget0 . - | - | - | - | ?newLinkValue0 rdf:type knora-base:LinkValue ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" ; - | rdf:subject ?resource ; - | rdf:predicate ?linkProperty0 ; - | rdf:object ?linkTarget0 ; - | knora-base:valueHasRefCount 1 ; - | - | knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | ?newLinkValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | - | ?resource ?linkValueProperty0 ?newLinkValue0 . - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/missingComment") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/knora-base#Annotation") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test Annotation") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/knora-base#isAnnotationOf - | - | BIND(IRI("http://www.knora.org/ontology/knora-base#isAnnotationOf") AS ?linkProperty0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#isAnnotationOfValue") AS ?linkValueProperty0) - | BIND(IRI("http://rdfh.ch/0803/missingComment/values/RFzfHLk1R-mU66NAFrVTYQ") AS ?newLinkValue0) - | BIND(IRI("http://rdfh.ch/0803/c5058f3a") AS ?linkTarget0) - | - | - | - | ?linkTarget0 rdf:type ?linkTargetClass0 . - | ?linkTargetClass0 rdfs:subClassOf+ knora-base:Resource . - | - | - | - | ?linkProperty0 knora-base:objectClassConstraint ?expectedTargetClass0 . - | ?linkTargetClass0 rdfs:subClassOf* ?expectedTargetClass0 . - | - | - | - | MINUS { - | ?linkTarget0 knora-base:isDeleted true . - | } - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?linkProperty0 . - | - | - | BIND(0 AS ?nextOrder0) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/knora-base#isAnnotationOf + | + | + | + | ?resource ?linkProperty0 ?linkTarget0 . + | + | + | + | ?newLinkValue0 rdf:type knora-base:LinkValue ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" ; + | rdf:subject ?resource ; + | rdf:predicate ?linkProperty0 ; + | rdf:object ?linkTarget0 ; + | knora-base:valueHasRefCount 1 ; + | + | knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | ?newLinkValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | + | ?resource ?linkValueProperty0 ?newLinkValue0 . + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/missingComment") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/knora-base#Annotation") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test Annotation") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/knora-base#isAnnotationOf + | + | BIND(IRI("http://www.knora.org/ontology/knora-base#isAnnotationOf") AS ?linkProperty0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#isAnnotationOfValue") AS ?linkValueProperty0) + | BIND(IRI("http://rdfh.ch/0803/missingComment/values/RFzfHLk1R-mU66NAFrVTYQ") AS ?newLinkValue0) + | BIND(IRI("http://rdfh.ch/0803/c5058f3a") AS ?linkTarget0) + | + | + | + | ?linkTarget0 rdf:type ?linkTargetClass0 . + | ?linkTargetClass0 rdfs:subClassOf+ knora-base:Resource . + | + | + | + | ?linkProperty0 knora-base:objectClassConstraint ?expectedTargetClass0 . + | ?linkTargetClass0 rdfs:subClassOf* ?expectedTargetClass0 . + | + | + | + | MINUS { + | ?linkTarget0 knora-base:isDeleted true . + | } + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?linkProperty0 . + | + | + | BIND(0 AS ?nextOrder0) + |} """.stripMargin // Tries to create an incunabula:book with two incunabula:publoc values (at most one is allowed). private val tooManyPublocs = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | - | ?newValue0 rdf:type ?valueType0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" . - | - | - | - | ?newValue0 knora-base:valueHasString "A beautiful book" . - | - | - | - | ?newValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property0 ?newValue0 . - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | - | ?newValue1 rdf:type ?valueType1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue1 knora-base:valueHasStartJDN 2457360 ; - | knora-base:valueHasEndJDN 2457360 ; - | knora-base:valueHasStartPrecision "DAY" ; - | knora-base:valueHasEndPrecision "DAY" ; - | knora-base:valueHasCalendar "GREGORIAN" ; - | knora-base:valueHasString "2015-12-03" . - | - | - | - | ?newValue1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property1 ?newValue1 . - | - | - | - | - | # Value 2 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | - | ?newValue2 rdf:type ?valueType2 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid2" . - | - | - | - | ?newValue2 knora-base:valueHasString "noch ein letztes" . - | - | - | - | - | ?newValue2 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue2 knora-base:valueHasOrder ?nextOrder2 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property2 ?newValue2 . - | - | - | - | - | # Value 3 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | - | ?newValue3 rdf:type ?valueType3 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid3" . - | - | - | ?newValue3 knora-base:valueHasString "ein Zitat" . - | - | - | ?newValue3 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue3 knora-base:valueHasOrder ?nextOrder3 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property3 ?newValue3 . - | - | - | - | - | # Value 4 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | - | ?newValue4 rdf:type ?valueType4 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid4" . - | - | - | - | ?newValue4 knora-base:valueHasString "und noch eines" . - | - | - | - | ?newValue4 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue4 knora-base:valueHasOrder ?nextOrder4 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property4 ?newValue4 . - | - | - | - | - | # Value 5 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | - | ?newValue5 rdf:type ?valueType5 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid5" . - | - | - | - | ?newValue5 knora-base:valueHasString "This citation refers to another resource" . - | - | - | - | - | ?newValue5 knora-base:valueHasStandoff - | [ - | - | - | rdf:type knora-base:StandoffVisualAttribute ; - | knora-base:standoffHasAttribute "bold" ; - | - | - | knora-base:standoffHasStart 5 ; - | knora-base:standoffHasEnd 13 - | ] . - | - | ?newValue5 knora-base:valueHasStandoff - | [ - | - | - | rdf:type knora-base:StandoffLink ; - | knora-base:standoffHasAttribute "_link" ; - | knora-base:standoffHasLink ; - | - | - | knora-base:standoffHasStart 32 ; - | knora-base:standoffHasEnd 40 - | ] . - | - | - | - | ?newValue5 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue5 knora-base:valueHasOrder ?nextOrder5 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property5 ?newValue5 . - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | ?newValue6 rdf:type ?valueType6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue6 knora-base:valueHasString "Entenhausen" . - | - | - | - | - | ?newValue6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property6 ?newValue6 . - | - | - | - | - | # Value 7 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | ?newValue7 rdf:type ?valueType7 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid7" . - | - | - | - | ?newValue7 knora-base:valueHasString "Bebenhausen" . - | - | - | - | ?newValue7 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue7 knora-base:valueHasOrder ?nextOrder7 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property7 ?newValue7 . - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Book") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) - | - | - | - | ?property0 knora-base:objectClassConstraint ?propertyRange0 . - | ?valueType0 rdfs:subClassOf* ?propertyRange0 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?property0 . - | - | - | - | - | BIND(0 AS ?nextOrder0) - | - | - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) - | - | - | - | ?property1 knora-base:objectClassConstraint ?propertyRange1 . - | ?valueType1 rdfs:subClassOf* ?propertyRange1 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction1 . - | ?restriction1 a owl:Restriction . - | ?restriction1 owl:onProperty ?property1 . - | - | - | - | BIND(0 AS ?nextOrder1) - | - | - | - | - | - | - | # Value 2 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property2) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/oTvvcMRgR_CC-Os-61I-Qw") AS ?newValue2) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType2) - | - | - | - | ?property2 knora-base:objectClassConstraint ?propertyRange2 . - | ?valueType2 rdfs:subClassOf* ?propertyRange2 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction2 . - | ?restriction2 a owl:Restriction . - | ?restriction2 owl:onProperty ?property2 . - | - | - | - | BIND(0 AS ?nextOrder2) - | - | - | - | - | - | - | # Value 3 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property3) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/Jvcncu3iSr2_fWdWdOfn-w") AS ?newValue3) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType3) - | - | - | - | ?property3 knora-base:objectClassConstraint ?propertyRange3 . - | ?valueType3 rdfs:subClassOf* ?propertyRange3 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction3 . - | ?restriction3 a owl:Restriction . - | ?restriction3 owl:onProperty ?property3 . - | - | - | - | BIND(1 AS ?nextOrder3) - | - | - | - | - | - | - | # Value 4 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property4) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/7wJJcQLtS2mG_tyPKCe1Ig") AS ?newValue4) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType4) - | - | - | - | ?property4 knora-base:objectClassConstraint ?propertyRange4 . - | ?valueType4 rdfs:subClassOf* ?propertyRange4 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction4 . - | ?restriction4 a owl:Restriction . - | ?restriction4 owl:onProperty ?property4 . - | - | - | BIND(2 AS ?nextOrder4) - | - | - | - | - | - | - | # Value 5 - | # Property: http://www.knora.org/ontology/0803/incunabula#citation - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property5) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/y7zDf5oNSE6-9GNNgXSbwA") AS ?newValue5) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType5) - | - | - | - | ?property5 knora-base:objectClassConstraint ?propertyRange5 . - | ?valueType5 rdfs:subClassOf* ?propertyRange5 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction5 . - | ?restriction5 a owl:Restriction . - | ?restriction5 owl:onProperty ?property5 . - | - | - | - | BIND(3 AS ?nextOrder5) - | - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/1ryBgY4MSn2Y8K8QAPiJBw0") AS ?newValue6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) - | - | - | - | ?property6 knora-base:objectClassConstraint ?propertyRange6 . - | ?valueType6 rdfs:subClassOf* ?propertyRange6 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction6 . - | ?restriction6 a owl:Restriction . - | ?restriction6 owl:onProperty ?property6 . - | - | - | - | BIND(0 AS ?nextOrder6) - | - | - | # Value 7 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property7) - | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/1ryBgY4MSn2Y8K8QAPiJBw1") AS ?newValue7) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType7) - | - | - | - | ?property7 knora-base:objectClassConstraint ?propertyRange7 . - | ?valueType7 rdfs:subClassOf* ?propertyRange7 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction7 . - | ?restriction7 a owl:Restriction . - | ?restriction7 owl:onProperty ?property7 . - | - | - | - | BIND(1 AS ?nextOrder7) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | + | ?newValue0 rdf:type ?valueType0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" . + | + | + | + | ?newValue0 knora-base:valueHasString "A beautiful book" . + | + | + | + | ?newValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property0 ?newValue0 . + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | + | ?newValue1 rdf:type ?valueType1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue1 knora-base:valueHasStartJDN 2457360 ; + | knora-base:valueHasEndJDN 2457360 ; + | knora-base:valueHasStartPrecision "DAY" ; + | knora-base:valueHasEndPrecision "DAY" ; + | knora-base:valueHasCalendar "GREGORIAN" ; + | knora-base:valueHasString "2015-12-03" . + | + | + | + | ?newValue1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property1 ?newValue1 . + | + | + | + | + | # Value 2 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | + | ?newValue2 rdf:type ?valueType2 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid2" . + | + | + | + | ?newValue2 knora-base:valueHasString "noch ein letztes" . + | + | + | + | + | ?newValue2 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue2 knora-base:valueHasOrder ?nextOrder2 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property2 ?newValue2 . + | + | + | + | + | # Value 3 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | + | ?newValue3 rdf:type ?valueType3 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid3" . + | + | + | ?newValue3 knora-base:valueHasString "ein Zitat" . + | + | + | ?newValue3 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue3 knora-base:valueHasOrder ?nextOrder3 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property3 ?newValue3 . + | + | + | + | + | # Value 4 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | + | ?newValue4 rdf:type ?valueType4 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid4" . + | + | + | + | ?newValue4 knora-base:valueHasString "und noch eines" . + | + | + | + | ?newValue4 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue4 knora-base:valueHasOrder ?nextOrder4 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property4 ?newValue4 . + | + | + | + | + | # Value 5 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | + | ?newValue5 rdf:type ?valueType5 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid5" . + | + | + | + | ?newValue5 knora-base:valueHasString "This citation refers to another resource" . + | + | + | + | + | ?newValue5 knora-base:valueHasStandoff + | [ + | + | + | rdf:type knora-base:StandoffVisualAttribute ; + | knora-base:standoffHasAttribute "bold" ; + | + | + | knora-base:standoffHasStart 5 ; + | knora-base:standoffHasEnd 13 + | ] . + | + | ?newValue5 knora-base:valueHasStandoff + | [ + | + | + | rdf:type knora-base:StandoffLink ; + | knora-base:standoffHasAttribute "_link" ; + | knora-base:standoffHasLink ; + | + | + | knora-base:standoffHasStart 32 ; + | knora-base:standoffHasEnd 40 + | ] . + | + | + | + | ?newValue5 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue5 knora-base:valueHasOrder ?nextOrder5 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property5 ?newValue5 . + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | ?newValue6 rdf:type ?valueType6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue6 knora-base:valueHasString "Entenhausen" . + | + | + | + | + | ?newValue6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property6 ?newValue6 . + | + | + | + | + | # Value 7 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | ?newValue7 rdf:type ?valueType7 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid7" . + | + | + | + | ?newValue7 knora-base:valueHasString "Bebenhausen" . + | + | + | + | ?newValue7 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue7 knora-base:valueHasOrder ?nextOrder7 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property7 ?newValue7 . + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Book") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) + | + | + | + | ?property0 knora-base:objectClassConstraint ?propertyRange0 . + | ?valueType0 rdfs:subClassOf* ?propertyRange0 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?property0 . + | + | + | + | + | BIND(0 AS ?nextOrder0) + | + | + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) + | + | + | + | ?property1 knora-base:objectClassConstraint ?propertyRange1 . + | ?valueType1 rdfs:subClassOf* ?propertyRange1 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction1 . + | ?restriction1 a owl:Restriction . + | ?restriction1 owl:onProperty ?property1 . + | + | + | + | BIND(0 AS ?nextOrder1) + | + | + | + | + | + | + | # Value 2 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property2) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/oTvvcMRgR_CC-Os-61I-Qw") AS ?newValue2) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType2) + | + | + | + | ?property2 knora-base:objectClassConstraint ?propertyRange2 . + | ?valueType2 rdfs:subClassOf* ?propertyRange2 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction2 . + | ?restriction2 a owl:Restriction . + | ?restriction2 owl:onProperty ?property2 . + | + | + | + | BIND(0 AS ?nextOrder2) + | + | + | + | + | + | + | # Value 3 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property3) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/Jvcncu3iSr2_fWdWdOfn-w") AS ?newValue3) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType3) + | + | + | + | ?property3 knora-base:objectClassConstraint ?propertyRange3 . + | ?valueType3 rdfs:subClassOf* ?propertyRange3 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction3 . + | ?restriction3 a owl:Restriction . + | ?restriction3 owl:onProperty ?property3 . + | + | + | + | BIND(1 AS ?nextOrder3) + | + | + | + | + | + | + | # Value 4 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property4) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/7wJJcQLtS2mG_tyPKCe1Ig") AS ?newValue4) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType4) + | + | + | + | ?property4 knora-base:objectClassConstraint ?propertyRange4 . + | ?valueType4 rdfs:subClassOf* ?propertyRange4 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction4 . + | ?restriction4 a owl:Restriction . + | ?restriction4 owl:onProperty ?property4 . + | + | + | BIND(2 AS ?nextOrder4) + | + | + | + | + | + | + | # Value 5 + | # Property: http://www.knora.org/ontology/0803/incunabula#citation + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#citation") AS ?property5) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/y7zDf5oNSE6-9GNNgXSbwA") AS ?newValue5) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType5) + | + | + | + | ?property5 knora-base:objectClassConstraint ?propertyRange5 . + | ?valueType5 rdfs:subClassOf* ?propertyRange5 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction5 . + | ?restriction5 a owl:Restriction . + | ?restriction5 owl:onProperty ?property5 . + | + | + | + | BIND(3 AS ?nextOrder5) + | + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/1ryBgY4MSn2Y8K8QAPiJBw0") AS ?newValue6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) + | + | + | + | ?property6 knora-base:objectClassConstraint ?propertyRange6 . + | ?valueType6 rdfs:subClassOf* ?propertyRange6 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction6 . + | ?restriction6 a owl:Restriction . + | ?restriction6 owl:onProperty ?property6 . + | + | + | + | BIND(0 AS ?nextOrder6) + | + | + | # Value 7 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property7) + | BIND(IRI("http://rdfh.ch/0803/tooManyPublocs/values/1ryBgY4MSn2Y8K8QAPiJBw1") AS ?newValue7) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType7) + | + | + | + | ?property7 knora-base:objectClassConstraint ?propertyRange7 . + | ?valueType7 rdfs:subClassOf* ?propertyRange7 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction7 . + | ?restriction7 a owl:Restriction . + | ?restriction7 owl:onProperty ?property7 . + | + | + | + | BIND(1 AS ?nextOrder7) + |} """.stripMargin private val tooManyLastModificationDates = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:lastModificationDate "2016-01-23T11:31:24Z"^^xsd:dateTime ; - | knora-base:lastModificationDate ?currentTime ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | - | ?newValue0 rdf:type ?valueType0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" . - | - | - | - | ?newValue0 knora-base:valueHasString "A beautiful book" . - | - | - | - | ?newValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property0 ?newValue0 . - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | - | ?newValue1 rdf:type ?valueType1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue1 knora-base:valueHasStartJDN 2457360 ; - | knora-base:valueHasEndJDN 2457360 ; - | knora-base:valueHasStartPrecision "DAY" ; - | knora-base:valueHasEndPrecision "DAY" ; - | knora-base:valueHasCalendar "GREGORIAN" ; - | knora-base:valueHasString "2015-12-03" . - | - | - | - | - | ?newValue1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property1 ?newValue1 . - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | ?newValue6 rdf:type ?valueType6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue6 knora-base:valueHasString "Entenhausen" . - | - | - | - | - | - | ?newValue6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property6 ?newValue6 . - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Book") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) - | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) - | - | - | - | ?property0 knora-base:objectClassConstraint ?propertyRange0 . - | ?valueType0 rdfs:subClassOf* ?propertyRange0 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?property0 . - | - | - | - | BIND(0 AS ?nextOrder0) - | - | - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) - | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) - | - | - | - | ?property1 knora-base:objectClassConstraint ?propertyRange1 . - | ?valueType1 rdfs:subClassOf* ?propertyRange1 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction1 . - | ?restriction1 a owl:Restriction . - | ?restriction1 owl:onProperty ?property1 . - | - | - | - | BIND(0 AS ?nextOrder1) - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) - | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) - | - | - | - | ?property6 knora-base:objectClassConstraint ?propertyRange6 . - | ?valueType6 rdfs:subClassOf* ?propertyRange6 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction6 . - | ?restriction6 a owl:Restriction . - | ?restriction6 owl:onProperty ?property6 . - | - | - | - | BIND(0 AS ?nextOrder6) - | - | - | - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:lastModificationDate "2016-01-23T11:31:24Z"^^xsd:dateTime ; + | knora-base:lastModificationDate ?currentTime ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | + | ?newValue0 rdf:type ?valueType0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" . + | + | + | + | ?newValue0 knora-base:valueHasString "A beautiful book" . + | + | + | + | ?newValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property0 ?newValue0 . + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | + | ?newValue1 rdf:type ?valueType1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue1 knora-base:valueHasStartJDN 2457360 ; + | knora-base:valueHasEndJDN 2457360 ; + | knora-base:valueHasStartPrecision "DAY" ; + | knora-base:valueHasEndPrecision "DAY" ; + | knora-base:valueHasCalendar "GREGORIAN" ; + | knora-base:valueHasString "2015-12-03" . + | + | + | + | + | ?newValue1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property1 ?newValue1 . + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | ?newValue6 rdf:type ?valueType6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue6 knora-base:valueHasString "Entenhausen" . + | + | + | + | + | + | ?newValue6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property6 ?newValue6 . + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Book") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) + | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) + | + | + | + | ?property0 knora-base:objectClassConstraint ?propertyRange0 . + | ?valueType0 rdfs:subClassOf* ?propertyRange0 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?property0 . + | + | + | + | BIND(0 AS ?nextOrder0) + | + | + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) + | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) + | + | + | + | ?property1 knora-base:objectClassConstraint ?propertyRange1 . + | ?valueType1 rdfs:subClassOf* ?propertyRange1 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction1 . + | ?restriction1 a owl:Restriction . + | ?restriction1 owl:onProperty ?property1 . + | + | + | + | BIND(0 AS ?nextOrder1) + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) + | BIND(IRI("http://rdfh.ch/0803/tooManyLastModificationDates/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) + | + | + | + | ?property6 knora-base:objectClassConstraint ?propertyRange6 . + | ?valueType6 rdfs:subClassOf* ?propertyRange6 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction6 . + | ?restriction6 a owl:Restriction . + | ?restriction6 owl:onProperty ?property6 . + | + | + | + | BIND(0 AS ?nextOrder6) + | + | + | + |} """.stripMargin private val wrongSubjectClass = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:valueHasString "A resource is not allowed to have a valueHasString property" ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:lastModificationDate ?currentTime ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | - | ?newValue0 rdf:type ?valueType0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" . - | - | - | - | ?newValue0 knora-base:valueHasString "A beautiful book" . - | - | - | - | - | ?newValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property0 ?newValue0 . - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | - | ?newValue1 rdf:type ?valueType1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue1 knora-base:valueHasStartJDN 2457360 ; - | knora-base:valueHasEndJDN 2457360 ; - | knora-base:valueHasStartPrecision "DAY" ; - | knora-base:valueHasEndPrecision "DAY" ; - | knora-base:valueHasCalendar "GREGORIAN" ; - | knora-base:valueHasString "2015-12-03" . - | - | - | - | ?newValue1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property1 ?newValue1 . - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | ?newValue6 rdf:type ?valueType6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue6 knora-base:valueHasString "Entenhausen" . - | - | - | - | - | ?newValue6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | ?resource ?property6 ?newValue6 . - | - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Book") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) - | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) - | - | - | - | ?property0 knora-base:objectClassConstraint ?propertyRange0 . - | ?valueType0 rdfs:subClassOf* ?propertyRange0 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?property0 . - | - | - | - | BIND(0 AS ?nextOrder0) - | - | - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) - | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) - | - | - | - | ?property1 knora-base:objectClassConstraint ?propertyRange1 . - | ?valueType1 rdfs:subClassOf* ?propertyRange1 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction1 . - | ?restriction1 a owl:Restriction . - | ?restriction1 owl:onProperty ?property1 . - | - | - | - | BIND(0 AS ?nextOrder1) - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) - | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) - | - | - | - | ?property6 knora-base:objectClassConstraint ?propertyRange6 . - | ?valueType6 rdfs:subClassOf* ?propertyRange6 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction6 . - | ?restriction6 a owl:Restriction . - | ?restriction6 owl:onProperty ?property6 . - | - | - | - | BIND(0 AS ?nextOrder6) - | - | - | - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:valueHasString "A resource is not allowed to have a valueHasString property" ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:lastModificationDate ?currentTime ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | + | ?newValue0 rdf:type ?valueType0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" . + | + | + | + | ?newValue0 knora-base:valueHasString "A beautiful book" . + | + | + | + | + | ?newValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property0 ?newValue0 . + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | + | ?newValue1 rdf:type ?valueType1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue1 knora-base:valueHasStartJDN 2457360 ; + | knora-base:valueHasEndJDN 2457360 ; + | knora-base:valueHasStartPrecision "DAY" ; + | knora-base:valueHasEndPrecision "DAY" ; + | knora-base:valueHasCalendar "GREGORIAN" ; + | knora-base:valueHasString "2015-12-03" . + | + | + | + | ?newValue1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property1 ?newValue1 . + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | ?newValue6 rdf:type ?valueType6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue6 knora-base:valueHasString "Entenhausen" . + | + | + | + | + | ?newValue6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | ?resource ?property6 ?newValue6 . + | + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Book") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) + | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) + | + | + | + | ?property0 knora-base:objectClassConstraint ?propertyRange0 . + | ?valueType0 rdfs:subClassOf* ?propertyRange0 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?property0 . + | + | + | + | BIND(0 AS ?nextOrder0) + | + | + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) + | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) + | + | + | + | ?property1 knora-base:objectClassConstraint ?propertyRange1 . + | ?valueType1 rdfs:subClassOf* ?propertyRange1 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction1 . + | ?restriction1 a owl:Restriction . + | ?restriction1 owl:onProperty ?property1 . + | + | + | + | BIND(0 AS ?nextOrder1) + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) + | BIND(IRI("http://rdfh.ch/0803/wrongSubjectClass/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) + | + | + | + | ?property6 knora-base:objectClassConstraint ?propertyRange6 . + | ?valueType6 rdfs:subClassOf* ?propertyRange6 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction6 . + | ?restriction6 a owl:Restriction . + | ?restriction6 owl:onProperty ?property6 . + | + | + | + | BIND(0 AS ?nextOrder6) + | + | + | + |} """.stripMargin private val wrongObjectClass = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:lastModificationDate ?currentTime ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | - | ?newValue0 rdf:type ?valueType0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" . - | - | - | - | ?newValue0 knora-base:valueHasString "A beautiful book" . - | - | - | - | - | ?newValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property1 ?newValue0 . # ?property0 and ?property1 are reversed to cause an error. - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | - | ?newValue1 rdf:type ?valueType1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue1 knora-base:valueHasStartJDN 2457360 ; - | knora-base:valueHasEndJDN 2457360 ; - | knora-base:valueHasStartPrecision "DAY" ; - | knora-base:valueHasEndPrecision "DAY" ; - | knora-base:valueHasCalendar "GREGORIAN" ; - | knora-base:valueHasString "2015-12-03" . - | - | ?newValue1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property0 ?newValue1 . # ?property0 and ?property1 are reversed to cause an error. - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | ?newValue6 rdf:type ?valueType6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue6 knora-base:valueHasString "Entenhausen" . - | - | - | - | ?newValue6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | ?resource ?property6 ?newValue6 . - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Book") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) - | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) - | - | - | - | ?property0 knora-base:objectClassConstraint ?propertyRange0 . - | ?valueType0 rdfs:subClassOf* ?propertyRange0 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?property0 . - | - | - | - | BIND(0 AS ?nextOrder0) - | - | - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) - | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) - | - | - | - | ?property1 knora-base:objectClassConstraint ?propertyRange1 . - | ?valueType1 rdfs:subClassOf* ?propertyRange1 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction1 . - | ?restriction1 a owl:Restriction . - | ?restriction1 owl:onProperty ?property1 . - | - | - | - | BIND(0 AS ?nextOrder1) - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) - | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) - | - | - | - | ?property6 knora-base:objectClassConstraint ?propertyRange6 . - | ?valueType6 rdfs:subClassOf* ?propertyRange6 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction6 . - | ?restriction6 a owl:Restriction . - | ?restriction6 owl:onProperty ?property6 . - | - | - | - | BIND(0 AS ?nextOrder6) - | - | - | - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:lastModificationDate ?currentTime ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | + | ?newValue0 rdf:type ?valueType0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" . + | + | + | + | ?newValue0 knora-base:valueHasString "A beautiful book" . + | + | + | + | + | ?newValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property1 ?newValue0 . # ?property0 and ?property1 are reversed to cause an error. + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | + | ?newValue1 rdf:type ?valueType1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue1 knora-base:valueHasStartJDN 2457360 ; + | knora-base:valueHasEndJDN 2457360 ; + | knora-base:valueHasStartPrecision "DAY" ; + | knora-base:valueHasEndPrecision "DAY" ; + | knora-base:valueHasCalendar "GREGORIAN" ; + | knora-base:valueHasString "2015-12-03" . + | + | ?newValue1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property0 ?newValue1 . # ?property0 and ?property1 are reversed to cause an error. + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | ?newValue6 rdf:type ?valueType6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue6 knora-base:valueHasString "Entenhausen" . + | + | + | + | ?newValue6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | ?resource ?property6 ?newValue6 . + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Book") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) + | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) + | + | + | + | ?property0 knora-base:objectClassConstraint ?propertyRange0 . + | ?valueType0 rdfs:subClassOf* ?propertyRange0 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?property0 . + | + | + | + | BIND(0 AS ?nextOrder0) + | + | + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) + | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) + | + | + | + | ?property1 knora-base:objectClassConstraint ?propertyRange1 . + | ?valueType1 rdfs:subClassOf* ?propertyRange1 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction1 . + | ?restriction1 a owl:Restriction . + | ?restriction1 owl:onProperty ?property1 . + | + | + | + | BIND(0 AS ?nextOrder1) + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) + | BIND(IRI("http://rdfh.ch/0803/wrongObjectClass/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) + | + | + | + | ?property6 knora-base:objectClassConstraint ?propertyRange6 . + | ?valueType6 rdfs:subClassOf* ?propertyRange6 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction6 . + | ?restriction6 a owl:Restriction . + | ?restriction6 owl:onProperty ?property6 . + | + | + | + | BIND(0 AS ?nextOrder6) + | + | + | + |} """.stripMargin private val resourcePropWithNoCardinality = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - |PREFIX incunabula: - |PREFIX salsah-gui: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | - | - | # A property that incunabula:book has no cardinality for. - | incunabula:unused rdf:type owl:ObjectProperty ; - | rdfs:subPropertyOf knora-base:hasValue ; - | rdfs:label "Unused property"@en ; - | rdfs:comment "A property used only in tests"@en ; - | knora-base:subjectClassConstraint incunabula:book ; - | knora-base:objectClassConstraint knora-base:TextValue ; - | salsah-gui:guiElement salsah-gui:SimpleText ; - | salsah-gui:guiAttribute "min=4" , - | "max=8" . - | - | - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:lastModificationDate ?currentTime ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | - | ?newValue0 rdf:type ?valueType0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" . - | - | - | - | ?newValue0 knora-base:valueHasString "A beautiful book" . - | - | - | ?newValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property0 ?newValue0 . - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | - | ?newValue1 rdf:type ?valueType1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue1 knora-base:valueHasStartJDN 2457360 ; - | knora-base:valueHasEndJDN 2457360 ; - | knora-base:valueHasStartPrecision "DAY" ; - | knora-base:valueHasEndPrecision "DAY" ; - | knora-base:valueHasCalendar "GREGORIAN" ; - | knora-base:valueHasString "2015-12-03" . - | - | - | - | ?newValue1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property1 ?newValue1 . - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | ?newValue6 rdf:type ?valueType6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue6 knora-base:valueHasString "Entenhausen" . - | - | - | ?newValue6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | - | ?resource ?property6 ?newValue6 . - | - | - | - | - | # Value 7 (there's no cardinality for it, so it should cause an error) - | # Property: http://www.knora.org/ontology/0803/incunabula#unused - | - | - | ?newValue7 rdf:type ?valueType7 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid7" . - | - | - | - | ?newValue7 knora-base:valueHasString "recto" . - | - | - | - | - | ?newValue7 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue7 knora-base:valueHasOrder ?nextOrder7 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property7 ?newValue7 . - | - | - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Book") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) - | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) - | - | - | - | ?property0 knora-base:objectClassConstraint ?propertyRange0 . - | ?valueType0 rdfs:subClassOf* ?propertyRange0 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?property0 . - | - | - | - | - | BIND(0 AS ?nextOrder0) - | - | - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) - | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) - | - | - | - | ?property1 knora-base:objectClassConstraint ?propertyRange1 . - | ?valueType1 rdfs:subClassOf* ?propertyRange1 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction1 . - | ?restriction1 a owl:Restriction . - | ?restriction1 owl:onProperty ?property1 . - | - | - | - | - | BIND(0 AS ?nextOrder1) - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) - | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) - | - | - | - | ?property6 knora-base:objectClassConstraint ?propertyRange6 . - | ?valueType6 rdfs:subClassOf* ?propertyRange6 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction6 . - | ?restriction6 a owl:Restriction . - | ?restriction6 owl:onProperty ?property6 . - | - | - | - | - | BIND(0 AS ?nextOrder6) - | - | - | - | - | # Value 7 - | # Property: http://www.knora.org/ontology/0803/incunabula#unused - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#unused") AS ?property7) - | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/nQ3tRObaQWe74WQv2_OdCg") AS ?newValue7) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType7) - | - | - | - | - | BIND(0 AS ?nextOrder7) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + |PREFIX incunabula: + |PREFIX salsah-gui: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | + | + | # A property that incunabula:book has no cardinality for. + | incunabula:unused rdf:type owl:ObjectProperty ; + | rdfs:subPropertyOf knora-base:hasValue ; + | rdfs:label "Unused property"@en ; + | rdfs:comment "A property used only in tests"@en ; + | knora-base:subjectClassConstraint incunabula:book ; + | knora-base:objectClassConstraint knora-base:TextValue ; + | salsah-gui:guiElement salsah-gui:SimpleText ; + | salsah-gui:guiAttribute "min=4" , + | "max=8" . + | + | + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:lastModificationDate ?currentTime ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | + | ?newValue0 rdf:type ?valueType0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" . + | + | + | + | ?newValue0 knora-base:valueHasString "A beautiful book" . + | + | + | ?newValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property0 ?newValue0 . + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | + | ?newValue1 rdf:type ?valueType1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue1 knora-base:valueHasStartJDN 2457360 ; + | knora-base:valueHasEndJDN 2457360 ; + | knora-base:valueHasStartPrecision "DAY" ; + | knora-base:valueHasEndPrecision "DAY" ; + | knora-base:valueHasCalendar "GREGORIAN" ; + | knora-base:valueHasString "2015-12-03" . + | + | + | + | ?newValue1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property1 ?newValue1 . + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | ?newValue6 rdf:type ?valueType6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue6 knora-base:valueHasString "Entenhausen" . + | + | + | ?newValue6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | + | ?resource ?property6 ?newValue6 . + | + | + | + | + | # Value 7 (there's no cardinality for it, so it should cause an error) + | # Property: http://www.knora.org/ontology/0803/incunabula#unused + | + | + | ?newValue7 rdf:type ?valueType7 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid7" . + | + | + | + | ?newValue7 knora-base:valueHasString "recto" . + | + | + | + | + | ?newValue7 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue7 knora-base:valueHasOrder ?nextOrder7 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property7 ?newValue7 . + | + | + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Book") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) + | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) + | + | + | + | ?property0 knora-base:objectClassConstraint ?propertyRange0 . + | ?valueType0 rdfs:subClassOf* ?propertyRange0 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?property0 . + | + | + | + | + | BIND(0 AS ?nextOrder0) + | + | + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) + | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) + | + | + | + | ?property1 knora-base:objectClassConstraint ?propertyRange1 . + | ?valueType1 rdfs:subClassOf* ?propertyRange1 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction1 . + | ?restriction1 a owl:Restriction . + | ?restriction1 owl:onProperty ?property1 . + | + | + | + | + | BIND(0 AS ?nextOrder1) + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) + | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) + | + | + | + | ?property6 knora-base:objectClassConstraint ?propertyRange6 . + | ?valueType6 rdfs:subClassOf* ?propertyRange6 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction6 . + | ?restriction6 a owl:Restriction . + | ?restriction6 owl:onProperty ?property6 . + | + | + | + | + | BIND(0 AS ?nextOrder6) + | + | + | + | + | # Value 7 + | # Property: http://www.knora.org/ontology/0803/incunabula#unused + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#unused") AS ?property7) + | BIND(IRI("http://rdfh.ch/0803/resourcePropWithNoCardinality/values/nQ3tRObaQWe74WQv2_OdCg") AS ?newValue7) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType7) + | + | + | + | + | BIND(0 AS ?nextOrder7) + |} """.stripMargin private val valuePropWithNoCardinality = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:lastModificationDate ?currentTime ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | - | ?newValue0 rdf:type ?valueType0 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid0" . - | - | - | - | ?newValue0 knora-base:valueHasString "A beautiful book" . - | - | - | ?newValue0 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | ?resource ?property0 ?newValue0 . - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | - | ?newValue1 rdf:type ?valueType1 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid1" . - | - | - | - | ?newValue1 knora-base:valueHasStartJDN 2457360 ; - | knora-base:valueHasEndJDN 2457360 ; - | knora-base:valueHasStartPrecision "DAY" ; - | knora-base:valueHasEndPrecision "DAY" ; - | knora-base:valueHasCalendar "GREGORIAN" ; - | knora-base:valueHasString "2015-12-03" . - | - | - | - | ?newValue1 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | ?resource ?property1 ?newValue1 . - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | - | # A property that knora-base:TextValue has no cardinality for. - | knora-base:valueHasTest rdf:type owl:DatatypeProperty ; - | rdfs:subPropertyOf knora-base:valueHas ; - | knora-base:subjectClassConstraint knora-base:TextValue ; - | knora-base:objectDatatypeConstraint xsd:integer . - | - | - | ?newValue6 rdf:type ?valueType6 ; - | knora-base:isDeleted "false"^^xsd:boolean ; - | knora-base:valueHasUUID "uuid6" . - | - | - | - | ?newValue6 knora-base:valueHasString "Entenhausen" . - | - | ?newValue6 knora-base:valueHasTest "3"^^xsd:integer . # No cardinality for this property, so it should cause an error. - | - | ?newValue6 ?creatorIri ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; - | knora-base:valueCreationDate ?currentTime . - | - | - | - | - | - | ?resource ?property6 ?newValue6 . - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) - | BIND(str("Test-Book") AS ?label) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0803/incunabula#title - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) - | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) - | - | - | - | ?property0 knora-base:objectClassConstraint ?propertyRange0 . - | ?valueType0 rdfs:subClassOf* ?propertyRange0 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction0 . - | ?restriction0 a owl:Restriction . - | ?restriction0 owl:onProperty ?property0 . - | - | - | - | - | BIND(0 AS ?nextOrder0) - | - | - | - | - | - | - | # Value 1 - | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) - | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) - | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) - | - | - | - | ?property1 knora-base:objectClassConstraint ?propertyRange1 . - | ?valueType1 rdfs:subClassOf* ?propertyRange1 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction1 . - | ?restriction1 a owl:Restriction . - | ?restriction1 owl:onProperty ?property1 . - | - | - | - | - | BIND(0 AS ?nextOrder1) - | - | - | - | - | - | # Value 6 - | # Property: http://www.knora.org/ontology/0803/incunabula#publoc - | - | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) - | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) - | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) - | - | - | - | ?property6 knora-base:objectClassConstraint ?propertyRange6 . - | ?valueType6 rdfs:subClassOf* ?propertyRange6 . - | - | - | - | ?resourceClass rdfs:subClassOf* ?restriction6 . - | ?restriction6 a owl:Restriction . - | ?restriction6 owl:onProperty ?property6 . - | - | - | - | - | BIND(0 AS ?nextOrder6) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:lastModificationDate ?currentTime ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | + | ?newValue0 rdf:type ?valueType0 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid0" . + | + | + | + | ?newValue0 knora-base:valueHasString "A beautiful book" . + | + | + | ?newValue0 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue0 knora-base:valueHasOrder ?nextOrder0 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | ?resource ?property0 ?newValue0 . + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | + | ?newValue1 rdf:type ?valueType1 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid1" . + | + | + | + | ?newValue1 knora-base:valueHasStartJDN 2457360 ; + | knora-base:valueHasEndJDN 2457360 ; + | knora-base:valueHasStartPrecision "DAY" ; + | knora-base:valueHasEndPrecision "DAY" ; + | knora-base:valueHasCalendar "GREGORIAN" ; + | knora-base:valueHasString "2015-12-03" . + | + | + | + | ?newValue1 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue1 knora-base:valueHasOrder ?nextOrder1 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | ?resource ?property1 ?newValue1 . + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | + | # A property that knora-base:TextValue has no cardinality for. + | knora-base:valueHasTest rdf:type owl:DatatypeProperty ; + | rdfs:subPropertyOf knora-base:valueHas ; + | knora-base:subjectClassConstraint knora-base:TextValue ; + | knora-base:objectDatatypeConstraint xsd:integer . + | + | + | ?newValue6 rdf:type ?valueType6 ; + | knora-base:isDeleted "false"^^xsd:boolean ; + | knora-base:valueHasUUID "uuid6" . + | + | + | + | ?newValue6 knora-base:valueHasString "Entenhausen" . + | + | ?newValue6 knora-base:valueHasTest "3"^^xsd:integer . # No cardinality for this property, so it should cause an error. + | + | ?newValue6 ?creatorIri ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | ?newValue6 knora-base:valueHasOrder ?nextOrder6 ; + | knora-base:valueCreationDate ?currentTime . + | + | + | + | + | + | ?resource ?property6 ?newValue6 . + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0803/incunabula") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#book") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/b83acc5f05") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0803") AS ?projectIri) + | BIND(str("Test-Book") AS ?label) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0803/incunabula#title + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#title") AS ?property0) + | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality/values/IKVNJVSWTryEtK4i9OCSIQ") AS ?newValue0) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType0) + | + | + | + | ?property0 knora-base:objectClassConstraint ?propertyRange0 . + | ?valueType0 rdfs:subClassOf* ?propertyRange0 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction0 . + | ?restriction0 a owl:Restriction . + | ?restriction0 owl:onProperty ?property0 . + | + | + | + | + | BIND(0 AS ?nextOrder0) + | + | + | + | + | + | + | # Value 1 + | # Property: http://www.knora.org/ontology/0803/incunabula#pubdate + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#pubdate") AS ?property1) + | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality/values/L4YSL2SeSkKVt-J9OQAMog") AS ?newValue1) + | BIND(IRI("http://www.knora.org/ontology/knora-base#DateValue") AS ?valueType1) + | + | + | + | ?property1 knora-base:objectClassConstraint ?propertyRange1 . + | ?valueType1 rdfs:subClassOf* ?propertyRange1 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction1 . + | ?restriction1 a owl:Restriction . + | ?restriction1 owl:onProperty ?property1 . + | + | + | + | + | BIND(0 AS ?nextOrder1) + | + | + | + | + | + | # Value 6 + | # Property: http://www.knora.org/ontology/0803/incunabula#publoc + | + | BIND(IRI("http://www.knora.org/ontology/0803/incunabula#publoc") AS ?property6) + | BIND(IRI("http://rdfh.ch/0803/valuePropWithNoCardinality/values/1ryBgY4MSn2Y8K8QAPiJBw") AS ?newValue6) + | BIND(IRI("http://www.knora.org/ontology/knora-base#TextValue") AS ?valueType6) + | + | + | + | ?property6 knora-base:objectClassConstraint ?propertyRange6 . + | ?valueType6 rdfs:subClassOf* ?propertyRange6 . + | + | + | + | ?resourceClass rdfs:subClassOf* ?restriction6 . + | ?restriction6 a owl:Restriction . + | ?restriction6 owl:onProperty ?property6 . + | + | + | + | + | BIND(0 AS ?nextOrder6) + |} """.stripMargin private val wrongLinkTargetClass = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX xsd: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource0 rdf:type ?resourceClass0 ; - | knora-base:isDeleted false ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label0 ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0001/anything#hasBlueThing - | - | # The property hasBlueThing has an objectClassConstraint of BlueThing, so using a Thing as a link target should fail. - | - | ?resource0 ?linkProperty0_0 ?linkTarget0_0 . - | - | - | - | ?newLinkValue0_0 rdf:type knora-base:LinkValue ; - | rdf:subject ?resource0 ; - | rdf:predicate ?linkProperty0_0 ; - | rdf:object ?linkTarget0_0 ; - | knora-base:valueHasString "http://rdfh.ch/0001/a-thing" ; - | knora-base:valueHasRefCount 1 ; - | - | knora-base:valueHasOrder ?nextOrder0_0 ; - | knora-base:isDeleted false ; - | knora-base:valueHasUUID "uuid0" ; - | knora-base:valueCreationDate ?currentTime . - | - | ?newLinkValue0_0 knora-base:attachedToUser ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . - | - | - | - | - | ?resource0 ?linkValueProperty0_0 ?newLinkValue0_0 . - | - | - | - | - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0001/anything") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0001/wrongTargetClass") AS ?resource0) - | BIND(IRI("http://www.knora.org/ontology/0001/anything#BlueThing") AS ?resourceClass0) - | BIND(IRI("http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0001") AS ?projectIri) - | BIND(str("Test Thing") AS ?label0) - | BIND(NOW() AS ?currentTime) - | - | - | - | # Value 0 - | # Property: http://www.knora.org/ontology/0001/anything#hasBlueThing - | - | BIND(IRI("http://www.knora.org/ontology/0001/anything#hasBlueThing") AS ?linkProperty0_0) - | BIND(IRI("http://www.knora.org/ontology/0001/anything#hasBlueThingValue") AS ?linkValueProperty0_0) - | BIND(IRI("http://rdfh.ch/0001/wrongTargetClass/values/GjV_4ayjRDebneEQM0zHuw") AS ?newLinkValue0_0) - | BIND(IRI("http://rdfh.ch/0001/a-thing") AS ?linkTarget0) - | - | - | - | ?linkTarget0_0 rdf:type ?linkTargetClass0_0 ; - | knora-base:isDeleted false . - | ?linkTargetClass0_0 rdfs:subClassOf+ knora-base:Resource . - | - | - | - | ?resourceClass0 rdfs:subClassOf* ?restriction0_0 . - | ?restriction0_0 a owl:Restriction . - | ?restriction0_0 owl:onProperty ?linkProperty0_0 . - | - | - | - | - | - | - | - | - | - | BIND(0 AS ?nextOrder0_0) - | - | - | - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX xsd: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource0 rdf:type ?resourceClass0 ; + | knora-base:isDeleted false ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label0 ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0001/anything#hasBlueThing + | + | # The property hasBlueThing has an objectClassConstraint of BlueThing, so using a Thing as a link target should fail. + | + | ?resource0 ?linkProperty0_0 ?linkTarget0_0 . + | + | + | + | ?newLinkValue0_0 rdf:type knora-base:LinkValue ; + | rdf:subject ?resource0 ; + | rdf:predicate ?linkProperty0_0 ; + | rdf:object ?linkTarget0_0 ; + | knora-base:valueHasString "http://rdfh.ch/0001/a-thing" ; + | knora-base:valueHasRefCount 1 ; + | + | knora-base:valueHasOrder ?nextOrder0_0 ; + | knora-base:isDeleted false ; + | knora-base:valueHasUUID "uuid0" ; + | knora-base:valueCreationDate ?currentTime . + | + | ?newLinkValue0_0 knora-base:attachedToUser ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" . + | + | + | + | + | ?resource0 ?linkValueProperty0_0 ?newLinkValue0_0 . + | + | + | + | + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0001/anything") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0001/wrongTargetClass") AS ?resource0) + | BIND(IRI("http://www.knora.org/ontology/0001/anything#BlueThing") AS ?resourceClass0) + | BIND(IRI("http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0001") AS ?projectIri) + | BIND(str("Test Thing") AS ?label0) + | BIND(NOW() AS ?currentTime) + | + | + | + | # Value 0 + | # Property: http://www.knora.org/ontology/0001/anything#hasBlueThing + | + | BIND(IRI("http://www.knora.org/ontology/0001/anything#hasBlueThing") AS ?linkProperty0_0) + | BIND(IRI("http://www.knora.org/ontology/0001/anything#hasBlueThingValue") AS ?linkValueProperty0_0) + | BIND(IRI("http://rdfh.ch/0001/wrongTargetClass/values/GjV_4ayjRDebneEQM0zHuw") AS ?newLinkValue0_0) + | BIND(IRI("http://rdfh.ch/0001/a-thing") AS ?linkTarget0) + | + | + | + | ?linkTarget0_0 rdf:type ?linkTargetClass0_0 ; + | knora-base:isDeleted false . + | ?linkTargetClass0_0 rdfs:subClassOf+ knora-base:Resource . + | + | + | + | ?resourceClass0 rdfs:subClassOf* ?restriction0_0 . + | ?restriction0_0 a owl:Restriction . + | ?restriction0_0 owl:onProperty ?linkProperty0_0 . + | + | + | + | + | + | + | + | + | + | BIND(0 AS ?nextOrder0_0) + | + | + | + |} """.stripMargin private val twoLabels = """ - |PREFIX rdf: - |PREFIX rdfs: - |PREFIX owl: - |PREFIX xsd: - |PREFIX knora-base: - | - |INSERT { - | GRAPH ?dataNamedGraph { - | ?resource rdf:type ?resourceClass ; - | knora-base:isDeleted false ; - | knora-base:attachedToUser ?creatorIri ; - | knora-base:attachedToProject ?projectIri ; - | rdfs:label ?label; - | rdfs:label "Second label not allowed" ; - | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; - | knora-base:creationDate ?currentTime . - | } - |} - | - | - | USING - | - |WHERE { - | BIND(IRI("http://www.knora.org/data/0001/anything") AS ?dataNamedGraph) - | BIND(IRI("http://rdfh.ch/0001/twoLabels") AS ?resource) - | BIND(IRI("http://www.knora.org/ontology/0001/anything#BlueThing") AS ?resourceClass) - | BIND(IRI("http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q") AS ?creatorIri) - | BIND(IRI("http://rdfh.ch/projects/0001") AS ?projectIri) - | BIND(str("Test Thing") AS ?label) - | BIND(NOW() AS ?currentTime) - |} + |PREFIX rdf: + |PREFIX rdfs: + |PREFIX owl: + |PREFIX xsd: + |PREFIX knora-base: + | + |INSERT { + | GRAPH ?dataNamedGraph { + | ?resource rdf:type ?resourceClass ; + | knora-base:isDeleted false ; + | knora-base:attachedToUser ?creatorIri ; + | knora-base:attachedToProject ?projectIri ; + | rdfs:label ?label; + | rdfs:label "Second label not allowed" ; + | knora-base:hasPermissions "V knora-admin:UnknownUser|M knora-admin:ProjectMember" ; + | knora-base:creationDate ?currentTime . + | } + |} + | + | + | USING + | + |WHERE { + | BIND(IRI("http://www.knora.org/data/0001/anything") AS ?dataNamedGraph) + | BIND(IRI("http://rdfh.ch/0001/twoLabels") AS ?resource) + | BIND(IRI("http://www.knora.org/ontology/0001/anything#BlueThing") AS ?resourceClass) + | BIND(IRI("http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q") AS ?creatorIri) + | BIND(IRI("http://rdfh.ch/projects/0001") AS ?projectIri) + | BIND(str("Test Thing") AS ?label) + | BIND(NOW() AS ?currentTime) + |} """.stripMargin } diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/HttpTriplestoreConnectorSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/HttpTriplestoreConnectorSpec.scala index abc4a955a1..f5511734b9 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/HttpTriplestoreConnectorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/HttpTriplestoreConnectorSpec.scala @@ -33,11 +33,11 @@ class HttpTriplestoreConnectorSpec extends CoreSpec() with ImplicitSender { "report a connection timeout with an appropriate error message" in { storeManager ! SimulateTimeoutRequest() - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => - assert(msg.cause.isInstanceOf[TriplestoreTimeoutException]) - assert( - msg.cause.getMessage == "The triplestore took too long to process a request. This can happen because the triplestore needed too much time to search through the data that is currently in the triplestore. Query optimisation may help.") + expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => + assert(msg.cause.isInstanceOf[TriplestoreTimeoutException]) + assert( + msg.cause.getMessage == "The triplestore took too long to process a request. This can happen because the triplestore needed too much time to search through the data that is currently in the triplestore. Query optimisation may help." + ) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322Spec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322Spec.scala index 793bba0c90..856a457e19 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1322Spec.scala @@ -38,12 +38,12 @@ class UpgradePluginPR1322Spec extends UpgradePluginSpec { val query: String = """ - |PREFIX knora-base: - | - |SELECT ?value WHERE { - | ?value knora-base:valueHasUUID ?valueHasUUID . - |} ORDER BY ?value - |""".stripMargin + |PREFIX knora-base: + | + |SELECT ?value WHERE { + | ?value knora-base:valueHasUUID ?valueHasUUID . + |} ORDER BY ?value + |""".stripMargin val queryResult1: SparqlSelectResult = repository.doSelect(selectQuery = query) diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746Spec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746Spec.scala index a88dc60a61..b7be2e4ded 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR1746Spec.scala @@ -27,7 +27,7 @@ import org.knora.webapi.messages.util.rdf._ class UpgradePluginPR1746Spec extends UpgradePluginSpec with LazyLogging { private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory(defaultFeatureFactoryConfig) - private def checkLiteral(model: RdfModel, subj: IriNode, pred: IriNode, expectedObj: RdfLiteral): Unit = { + private def checkLiteral(model: RdfModel, subj: IriNode, pred: IriNode, expectedObj: RdfLiteral): Unit = model .find( subj = Some(subj), @@ -44,7 +44,6 @@ class UpgradePluginPR1746Spec extends UpgradePluginSpec with LazyLogging { case None => throw AssertionException(s"No statement found with subject $subj and predicate $pred") } - } "Upgrade plugin PR1746" should { "replace empty string with FIXME" in { diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala index fac368e6f3..647beae4b8 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala @@ -26,17 +26,17 @@ import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf._ /** - * Provides helper methods for specs that test upgrade plugins. - */ + * Provides helper methods for specs that test upgrade plugins. + */ abstract class UpgradePluginSpec extends CoreSpec() { protected val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil(defaultFeatureFactoryConfig) /** - * Parses a TriG file and returns it as an [[RdfModel]]. - * - * @param path the file path of the TriG file. - * @return an [[RdfModel]]. - */ + * Parses a TriG file and returns it as an [[RdfModel]]. + * + * @param path the file path of the TriG file. + * @return an [[RdfModel]]. + */ def trigFileToModel(path: String): RdfModel = { val fileInputStream = new BufferedInputStream(new FileInputStream(path)) val rdfModel: RdfModel = rdfFormatUtil.inputStreamToRdfModel(inputStream = fileInputStream, rdfFormat = TriG) @@ -45,16 +45,21 @@ abstract class UpgradePluginSpec extends CoreSpec() { } /** - * Wraps expected SPARQL SELECT results in a [[SparqlSelectResultBody]]. - * - * @param rows the expected results. - * @return a [[SparqlSelectResultBody]] containing the expected results. - */ + * Wraps expected SPARQL SELECT results in a [[SparqlSelectResultBody]]. + * + * @param rows the expected results. + * @return a [[SparqlSelectResultBody]] containing the expected results. + */ def expectedResult(rows: Seq[Map[String, String]]): SparqlSelectResultBody = { val rowMaps = rows.map { mapToWrap => - VariableResultsRow(new ErrorHandlingMap[String, String](mapToWrap, { key: String => - s"No value found for SPARQL query variable '$key' in query result row" - })) + VariableResultsRow( + new ErrorHandlingMap[String, String]( + mapToWrap, + { key: String => + s"No value found for SPARQL query variable '$key' in query result row" + } + ) + ) } SparqlSelectResultBody(bindings = rowMaps) diff --git a/webapi/src/test/scala/org/knora/webapi/util/AkkaHttpUtils.scala b/webapi/src/test/scala/org/knora/webapi/util/AkkaHttpUtils.scala index 2c8e6e86c5..ef23790fdc 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/AkkaHttpUtils.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/AkkaHttpUtils.scala @@ -31,16 +31,16 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContext, Future} /** - * Object containing methods for dealing with [[HttpResponse]] - */ + * Object containing methods for dealing with [[HttpResponse]] + */ object AkkaHttpUtils extends LazyLogging { /** - * Given an [[HttpResponse]] containing json, return the said json. - * - * @param response the [[HttpResponse]] containing json - * @return an [[JsObject]] - */ + * Given an [[HttpResponse]] containing json, return the said json. + * + * @param response the [[HttpResponse]] containing json + * @return an [[JsObject]] + */ def httpResponseToJson(response: HttpResponse)(implicit ec: ExecutionContext, system: ActorSystem): JsObject = { import DefaultJsonProtocol._ diff --git a/webapi/src/test/scala/org/knora/webapi/util/Base64UrlCheckDigitSpec.scala b/webapi/src/test/scala/org/knora/webapi/util/Base64UrlCheckDigitSpec.scala index 92afb26e29..3c0985c517 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/Base64UrlCheckDigitSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/Base64UrlCheckDigitSpec.scala @@ -22,8 +22,8 @@ package org.knora.webapi.util import org.knora.webapi.CoreSpec /** - * Tests [[Base64UrlCheckDigit]]. - */ + * Tests [[Base64UrlCheckDigit]]. + */ class Base64UrlCheckDigitSpec extends CoreSpec { private val base64UrlCheckDigit = new Base64UrlCheckDigit private val correctResourceID = "cmfk1DMHRBiR4-_6HXpEFA" @@ -49,19 +49,22 @@ class Base64UrlCheckDigitSpec extends CoreSpec { "reject a string with a missing character" in { val resourceIDWithMissingCharacter = "cmfk1DMHRBiR4-6HXpEFA" - val resourceIDWithMissingCharacterAndCorrectCheckDigit = resourceIDWithMissingCharacter + correctResourceIDCheckDigit + val resourceIDWithMissingCharacterAndCorrectCheckDigit = + resourceIDWithMissingCharacter + correctResourceIDCheckDigit assert(!base64UrlCheckDigit.isValid(resourceIDWithMissingCharacterAndCorrectCheckDigit)) } "reject a string with an incorrect character" in { val resourceIDWithIncorrectCharacter = "cmfk1DMHRBir4-_6HXpEFA" - val resourceIDWithIncorrectCharacterAndCorrectCheckDigit = resourceIDWithIncorrectCharacter + correctResourceIDCheckDigit + val resourceIDWithIncorrectCharacterAndCorrectCheckDigit = + resourceIDWithIncorrectCharacter + correctResourceIDCheckDigit assert(!base64UrlCheckDigit.isValid(resourceIDWithIncorrectCharacterAndCorrectCheckDigit)) } "reject a string with swapped characters" in { val resourceIDWithSwappedCharacters = "cmfk1DMHRBiR4_-6HXpEFA" - val resourceIDWithSwappedCharactersAndCorrectCheckDigit = resourceIDWithSwappedCharacters + correctResourceIDCheckDigit + val resourceIDWithSwappedCharactersAndCorrectCheckDigit = + resourceIDWithSwappedCharacters + correctResourceIDCheckDigit assert(!base64UrlCheckDigit.isValid(resourceIDWithSwappedCharactersAndCorrectCheckDigit)) } } diff --git a/webapi/src/test/scala/org/knora/webapi/util/MutableTestIri.scala b/webapi/src/test/scala/org/knora/webapi/util/MutableTestIri.scala index ba3956fea0..5b01831be3 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/MutableTestIri.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/MutableTestIri.scala @@ -23,39 +23,36 @@ import org.knora.webapi.IRI import org.knora.webapi.messages.StringFormatter /** - * Holds an optional, mutable IRI for use in tests. - */ + * Holds an optional, mutable IRI for use in tests. + */ class MutableTestIri { private val stringFormatter = StringFormatter.getGeneralInstance private var maybeIri: Option[IRI] = None /** - * Checks whether an IRI is valid. If it's valid, stores the IRI, otherwise throws an exception. - * - * @param iri the IRI to be stored. - */ - def set(iri: IRI): Unit = { + * Checks whether an IRI is valid. If it's valid, stores the IRI, otherwise throws an exception. + * + * @param iri the IRI to be stored. + */ + def set(iri: IRI): Unit = maybeIri = Some(stringFormatter.validateAndEscapeIri(iri, throw TestIriException(s"Got an invalid IRI: <$iri>"))) - } /** - * Removes any stored IRI. - */ - def unset(): Unit = { + * Removes any stored IRI. + */ + def unset(): Unit = maybeIri = None - } /** - * Gets the stored IRI, or throws an exception if the IRI is not set. - * - * @return the stored IRI. - */ - def get: IRI = { + * Gets the stored IRI, or throws an exception if the IRI is not set. + * + * @return the stored IRI. + */ + def get: IRI = maybeIri.getOrElse(throw TestIriException("This test could not be run because a previous test failed")) - } } /** - * Thrown if a stored IRI was needed but was not set. - */ + * Thrown if a stored IRI was needed but was not set. + */ case class TestIriException(message: String) extends Exception(message) diff --git a/webapi/src/test/scala/org/knora/webapi/util/MutableTestString.scala b/webapi/src/test/scala/org/knora/webapi/util/MutableTestString.scala index e78564580f..ef765674e7 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/MutableTestString.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/MutableTestString.scala @@ -1,36 +1,33 @@ package org.knora.webapi.util /** - * Holds an optional, mutable IRI for use in tests. - */ + * Holds an optional, mutable IRI for use in tests. + */ class MutableTestString { private var maybeString: Option[String] = None /** - * Stores the string. - * @param string the string to be stored. - */ - def set(value: String): Unit = { + * Stores the string. + * @param string the string to be stored. + */ + def set(value: String): Unit = maybeString = Some(value) - } /** - * Removes any stored string. - */ - def unset(): Unit = { + * Removes any stored string. + */ + def unset(): Unit = maybeString = None - } /** - * Gets the stored string, or throws an exception if the string is not set. - * @return the stored string. - */ - def get: String = { + * Gets the stored string, or throws an exception if the string is not set. + * @return the stored string. + */ + def get: String = maybeString.getOrElse(throw TestUserProfileException("This test could not be run because a previous test failed")) - } } /** - * Thrown if a stored string was needed but was not set. - */ + * Thrown if a stored string was needed but was not set. + */ case class TestStringException(message: String) extends Exception(message) diff --git a/webapi/src/test/scala/org/knora/webapi/util/MutableUserADM.scala b/webapi/src/test/scala/org/knora/webapi/util/MutableUserADM.scala index c4505e0b0d..dcc2d30cca 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/MutableUserADM.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/MutableUserADM.scala @@ -3,37 +3,35 @@ package org.knora.webapi.util import org.knora.webapi.messages.admin.responder.usersmessages.UserADM /** - * Holds an optional, mutable IRI for use in tests. - */ + * Holds an optional, mutable IRI for use in tests. + */ class MutableUserADM { private var maybeUserProfile: Option[UserADM] = None /** - * Stores the user's profile. - * @param userProfile the user's profile to be stored. - */ - def set(userProfile: UserADM): Unit = { + * Stores the user's profile. + * @param userProfile the user's profile to be stored. + */ + def set(userProfile: UserADM): Unit = maybeUserProfile = Some(userProfile) - } /** - * Removes any stored IRI. - */ - def unset(): Unit = { + * Removes any stored IRI. + */ + def unset(): Unit = maybeUserProfile = None - } /** - * Gets the stored IRI, or throws an exception if the IRI is not set. - * @return the stored IRI. - */ - def get: UserADM = { + * Gets the stored IRI, or throws an exception if the IRI is not set. + * @return the stored IRI. + */ + def get: UserADM = maybeUserProfile.getOrElse( - throw TestUserProfileException("This test could not be run because a previous test failed")) - } + throw TestUserProfileException("This test could not be run because a previous test failed") + ) } /** - * Thrown if a stored IRI was needed but was not set. - */ + * Thrown if a stored IRI was needed but was not set. + */ case class TestUserProfileException(message: String) extends Exception(message) diff --git a/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala b/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala index c905aa162e..29044a5d7b 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala @@ -31,15 +31,15 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} /** - * This trait is only used for testing. It is necessary so that E2E tests will only start - * after the KnoraService is ready. - */ + * This trait is only used for testing. It is necessary so that E2E tests will only start + * after the KnoraService is ready. + */ trait StartupUtils extends LazyLogging { this: Core => /** - * Returns only when the application state is 'Running'. - */ + * Returns only when the application state is 'Running'. + */ def applicationStateRunning(): Unit = { implicit val timeout: Timeout = Timeout(5.second) @@ -54,8 +54,8 @@ trait StartupUtils extends LazyLogging { } /** - * A blocking future running on the blocking dispatcher. - */ + * A blocking future running on the blocking dispatcher. + */ private def blockingFuture(): Future[Unit] = { val delay: Long = 3.second.toMillis diff --git a/webapi/src/test/scala/org/knora/webapi/util/StringLiteralSequenceV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/util/StringLiteralSequenceV2Spec.scala index 1b20df861b..9dddea0dbc 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/StringLiteralSequenceV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/StringLiteralSequenceV2Spec.scala @@ -24,8 +24,8 @@ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.store.triplestoremessages.{StringLiteralSequenceV2, StringLiteralV2} /** - * Tests [[StringLiteralSequenceV2]]. - */ + * Tests [[StringLiteralSequenceV2]]. + */ class StringLiteralSequenceV2Spec extends CoreSpec() { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance diff --git a/webapi/src/test/scala/org/knora/webapi/util/TestExtractorMethods.scala b/webapi/src/test/scala/org/knora/webapi/util/TestExtractorMethods.scala index eef8258e85..5b41997821 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/TestExtractorMethods.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/TestExtractorMethods.scala @@ -27,18 +27,17 @@ import spray.json.JsString import scala.concurrent.ExecutionContext /** - * Created by subotic on 06.02.17. - */ + * Created by subotic on 06.02.17. + */ object ResourceResponseExtractorMethods { /** - * Gets the field `res_id` from a JSON response to resource creation. - * - * @param response the response sent back from the API. - * @return the value of `res_id`. - */ - def getResIriFromJsonResponse(response: HttpResponse)(implicit ec: ExecutionContext, system: ActorSystem) = { - + * Gets the field `res_id` from a JSON response to resource creation. + * + * @param response the response sent back from the API. + * @return the value of `res_id`. + */ + def getResIriFromJsonResponse(response: HttpResponse)(implicit ec: ExecutionContext, system: ActorSystem) = AkkaHttpUtils.httpResponseToJson(response).fields.get("res_id") match { case Some(JsString(resourceId)) => resourceId case None => throw InvalidApiJsonException(s"The response does not contain a field called 'res_id'") @@ -46,20 +45,17 @@ object ResourceResponseExtractorMethods { throw InvalidApiJsonException(s"The response does not contain a res_id of type JsString, but ${other}") } - } - } object ValuesResponseExtractorMethods { /** - * Gets the field `id` from a JSON response to value creation (new value). - * - * @param response the response sent back from the API. - * @return the value of `res_id`. - */ - def getNewValueIriFromJsonResponse(response: HttpResponse)(implicit ec: ExecutionContext, system: ActorSystem) = { - + * Gets the field `id` from a JSON response to value creation (new value). + * + * @param response the response sent back from the API. + * @return the value of `res_id`. + */ + def getNewValueIriFromJsonResponse(response: HttpResponse)(implicit ec: ExecutionContext, system: ActorSystem) = AkkaHttpUtils.httpResponseToJson(response).fields.get("id") match { case Some(JsString(resourceId)) => resourceId case None => throw InvalidApiJsonException(s"The response does not contain a field called 'res_id'") @@ -67,5 +63,4 @@ object ValuesResponseExtractorMethods { throw InvalidApiJsonException(s"The response does not contain a res_id of type JsString, but $other") } - } }