diff --git a/api/soap/mc_api.php b/api/soap/mc_api.php index 9732f90714..6460f3a26e 100644 --- a/api/soap/mc_api.php +++ b/api/soap/mc_api.php @@ -31,6 +31,7 @@ require_api( 'api_token_api.php' ); +use Mantis\Exceptions\ClientException; use Mantis\Exceptions\LegacyApiFaultException; /** @@ -287,12 +288,17 @@ static function faultFromException( Exception $p_exception ) { * @param stdClass|array $p_object Object. * @return array */ - static function objectToArray($p_object ) { - if( is_object( $p_object ) ) { - return get_object_vars( $p_object ); + static function objectToArray( $p_object, $p_recursive = false ) { + $t_object = is_object( $p_object ) ? get_object_vars( $p_object ) : $p_object; + if( $p_recursive && is_array( $t_object ) ) { + foreach( $t_object as $t_key => $t_value ) { + if( is_object( $t_object[$t_key] ) || is_array( $t_object[$t_key] ) ) { + $t_object[$t_key] = ApiObjectFactory::objectToArray( $t_object[$t_key], $p_recursive ); + } + } } - return $p_object; + return $t_object; } /** @@ -353,7 +359,7 @@ static function isFault( $p_maybe_fault ) { */ static function throwIfFault( $p_maybe_fault ) { if( ApiObjectFactory::isFault( $p_maybe_fault ) ) { - throw new LegacyApiFaultException( $p_maybe_fault->fault_string, $p_maybe_fault->status_code ); + throw new LegacyApiFaultException( $p_maybe_fault->getMessage(), $p_maybe_fault->getCode() ); } } } @@ -876,9 +882,10 @@ function mci_get_version( $p_version, $p_project_id ) { * * @param string|object $p_version The version string or object with name or id or both. * @param int $p_project_id The project id. + * @param string $p_field_name Version field name (e.g. version, target_version, fixed_in_version) * @return int|RestFault|SoapFault The version id, 0 if not supplied. */ -function mci_get_version_id( $p_version, $p_project_id ) { +function mci_get_version_id( $p_version, $p_project_id, $p_field_name = 'version' ) { $t_version_id = 0; $t_version_for_error = ''; @@ -903,7 +910,11 @@ function mci_get_version_id( $p_version, $p_project_id ) { $t_error_when_version_not_found = config_get( 'webservice_error_when_version_not_found' ); if( $t_error_when_version_not_found == ON ) { $t_project_name = project_get_name( $p_project_id ); - return ApiObjectFactory::faultBadRequest( "Version '$t_version_for_error' does not exist in project '$t_project_name'." ); + throw new ClientException( + "Version '$t_version_for_error' does not exist in project '$t_project_name'.", + ERROR_INVALID_FIELD_VALUE, + array( 'version' ) + ); } $t_version_when_not_found = config_get( 'webservice_version_when_not_found' ); @@ -983,7 +994,11 @@ function mci_get_category_id( $p_category, $p_project_id ) { $t_category_id = $fn_get_category_id_internal( $p_category, $p_project_id ); if( $t_category_id == 0 && !config_get( 'allow_no_category' ) ) { if( !isset( $p_category ) ) { - return ApiObjectFactory::faultBadRequest( 'Category field must be supplied.' ); + throw new ClientException( + 'Category field must be supplied.', + ERROR_EMPTY_FIELD, + array( 'category' ) + ); } # category may be a string, array with id, array with name, or array diff --git a/api/soap/mc_issue_api.php b/api/soap/mc_issue_api.php index 8dd95cc148..57e614048d 100644 --- a/api/soap/mc_issue_api.php +++ b/api/soap/mc_issue_api.php @@ -906,228 +906,14 @@ function mc_issue_add( $p_username, $p_password, $p_issue ) { return mci_fault_login_failed(); } - if( is_object( $p_issue ) ) { - $p_issue = ApiObjectFactory::objectToArray( $p_issue ); - } - - if( !isset( $p_issue['summary'] ) ) { - return ApiObjectFactory::faultBadRequest( 'Summary not specified' ); - } - - if( !isset( $p_issue['description'] ) ) { - return ApiObjectFactory::faultBadRequest( 'Description not specified' ); - } - - if( !isset( $p_issue['project'] ) ) { - return ApiObjectFactory::faultBadRequest( 'Project not specified' ); - } - - $t_project = $p_issue['project']; - - $t_project_id = mci_get_project_id( $t_project ); - $g_project_override = $t_project_id; # ensure that helper_get_current_project() calls resolve to this project id - - if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) { - return mci_fault_access_denied( $t_user_id ); - } - - $t_handler_id = isset( $p_issue['handler'] ) ? mci_get_user_id( $p_issue['handler'] ) : 0; - $t_priority_id = isset( $p_issue['priority'] ) ? mci_get_priority_id( $p_issue['priority'] ) : config_get( 'default_bug_priority' ); - $t_severity_id = isset( $p_issue['severity'] ) ? mci_get_severity_id( $p_issue['severity'] ) : config_get( 'default_bug_severity' ); - $t_status_id = isset( $p_issue['status'] ) ? mci_get_status_id( $p_issue['status'] ) : config_get( 'bug_submit_status' ); - $t_reproducibility_id = isset( $p_issue['reproducibility'] ) ? mci_get_reproducibility_id( $p_issue['reproducibility'] ) : config_get( 'default_bug_reproducibility' ); - $t_resolution_id = isset( $p_issue['resolution'] ) ? mci_get_resolution_id( $p_issue['resolution'] ) : config_get( 'default_bug_resolution' ); - $t_projection_id = isset( $p_issue['projection'] ) ? mci_get_projection_id( $p_issue['projection'] ) : config_get( 'default_bug_resolution' ); - $t_eta_id = isset( $p_issue['eta'] ) ? mci_get_eta_id( $p_issue['eta'] ) : config_get( 'default_bug_eta' ); - $t_view_state_id = isset( $p_issue['view_state'] ) ? mci_get_view_state_id( $p_issue['view_state'] ) : config_get( 'default_bug_view_status' ); - $t_summary = $p_issue['summary']; - $t_description = $p_issue['description']; - $t_notes = isset( $p_issue['notes'] ) ? $p_issue['notes'] : array(); - - # TODO: #17777: Add test case for mc_issue_add() and mc_issue_note_add() reporter override - if( isset( $p_issue['reporter'] ) ) { - $t_reporter_id = mci_get_user_id( $p_issue['reporter'] ); - - if( $t_reporter_id != $t_user_id ) { - # Make sure that active user has access level required to specify a different reporter. - $t_specify_reporter_access_level = config_get( 'webservice_specify_reporter_on_add_access_level_threshold' ); - if( !access_has_project_level( $t_specify_reporter_access_level, $t_project_id, $t_user_id ) ) { - return mci_fault_access_denied( $t_user_id, 'Active user does not have access level required to specify a different issue reporter' ); - } - } - } else { - $t_reporter_id = $t_user_id; - } - - if( ( $t_project_id == 0 ) || !project_exists( $t_project_id ) ) { - if( $t_project_id != 0 ) { - return ApiObjectFactory::faultNotFound( "Project '" . $t_project->name . "' does not exist." ); - } - - return ApiObjectFactory::faultNotFound( "Project with id '" . $t_project_id . "' does not exist." ); - } - - if( !access_has_project_level( config_get( 'report_bug_threshold' ), $t_project_id, $t_user_id ) ) { - return mci_fault_access_denied( $t_user_id, 'User does not have access right to report issues' ); - } - - $t_access_check_result = mci_issue_handler_access_check( $t_user_id, $t_project_id, /* old */ 0, /* new */ $t_handler_id ); - if( $t_access_check_result !== true ) { - return $t_access_check_result; - } - - $t_category = isset( $p_issue['category'] ) ? $p_issue['category'] : null; - - $t_category_id = mci_get_category_id( $t_category, $t_project_id ); - if( ApiObjectFactory::isFault( $t_category_id ) ) { - return $t_category_id; - } - - $t_version_id = isset( $p_issue['version'] ) ? mci_get_version_id( $p_issue['version'], $t_project_id ) : 0; - if( ApiObjectFactory::isFault( $t_version_id ) ) { - return $t_version_id; - } - - $t_fixed_in_version_id = isset( $p_issue['fixed_in_version'] ) ? mci_get_version_id( $p_issue['fixed_in_version'], $t_project_id ) : 0; - if( ApiObjectFactory::isFault( $t_fixed_in_version_id ) ) { - return $t_fixed_in_version_id; - } - - $t_target_version_id = isset( $p_issue['target_version'] ) ? mci_get_version_id( $p_issue['target_version'], $t_project_id ) : 0; - if( ApiObjectFactory::isFault( $t_target_version_id ) ) { - return $t_target_version_id; - } - - if( is_blank( $t_summary ) ) { - return ApiObjectFactory::faultBadRequest( 'Mandatory field \'summary\' is missing.' ); - } - - if( is_blank( $t_description ) ) { - return ApiObjectFactory::faultBadRequest( 'Mandatory field \'description\' is missing.' ); - } - - $t_bug_data = new BugData; - $t_bug_data->profile_id = 0; - $t_bug_data->project_id = $t_project_id; - $t_bug_data->reporter_id = $t_reporter_id; - $t_bug_data->handler_id = $t_handler_id; - $t_bug_data->priority = $t_priority_id; - $t_bug_data->severity = $t_severity_id; - $t_bug_data->reproducibility = $t_reproducibility_id; - $t_bug_data->status = $t_status_id; - $t_bug_data->resolution = $t_resolution_id; - $t_bug_data->projection = $t_projection_id; - $t_bug_data->category_id = $t_category_id; - $t_bug_data->date_submitted = isset( $p_issue['date_submitted'] ) ? strtotime( $p_issue['date_submitted'] ) : ''; - $t_bug_data->last_updated = isset( $p_issue['last_updated'] ) ? strtotime( $p_issue['last_updated'] ) : ''; - $t_bug_data->eta = $t_eta_id; - $t_bug_data->profile_id = isset( $p_issue['profile_id'] ) ? $p_issue['profile_id'] : 0; - $t_bug_data->os = isset( $p_issue['os'] ) ? $p_issue['os'] : ''; - $t_bug_data->os_build = isset( $p_issue['os_build'] ) ? $p_issue['os_build'] : ''; - $t_bug_data->platform = isset( $p_issue['platform'] ) ? $p_issue['platform'] : ''; - - if( $t_version_id != 0 ) { - $t_bug_data->version = version_get_field( $t_version_id, 'version' ); - } - - if( $t_fixed_in_version_id != 0 ) { - $t_bug_data->fixed_in_version = version_get_field( $t_fixed_in_version_id, 'version' ); - } - - if( $t_target_version_id != 0 && access_has_project_level( config_get( 'roadmap_update_threshold' ), $t_bug_data->project_id, $t_user_id ) ) { - $t_bug_data->target_version = version_get_field( $t_target_version_id, 'version' ); - } - - $t_bug_data->build = isset( $p_issue['build'] ) ? $p_issue['build'] : ''; - $t_bug_data->view_state = $t_view_state_id; - $t_bug_data->summary = $t_summary; - $t_bug_data->sponsorship_total = isset( $p_issue['sponsorship_total'] ) ? $p_issue['sponsorship_total'] : 0; - if( isset( $p_issue['sticky'] ) && - access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $t_project_id ), $t_project_id ) ) { - $t_bug_data->sticky = $p_issue['sticky']; - } - - if( isset( $p_issue['due_date'] ) && - access_has_project_level( config_get( 'due_date_update_threshold' ), $t_bug_data->project_id ) ) { - $t_bug_data->due_date = strtotime( $p_issue['due_date'] ); - } else { - $t_bug_data->due_date = date_get_null(); - } - - # omitted: - # var $bug_text_id - # $t_bug_data->profile_id; - # extended info - $t_bug_data->description = $t_description; - $t_bug_data->steps_to_reproduce = isset( $p_issue['steps_to_reproduce'] ) ? $p_issue['steps_to_reproduce'] : ''; - $t_bug_data->additional_information = isset( $p_issue['additional_information'] ) ? $p_issue['additional_information'] : ''; - - # submit the issue - $t_issue_id = $t_bug_data->create(); - $t_bug_data->process_mentions(); - - log_event( LOG_WEBSERVICE, 'created new issue id \'' . $t_issue_id . '\'' ); - - $t_cf_result = mci_project_custom_fields_validate( $t_project_id, $p_issue['custom_fields'] ); - if( ApiObjectFactory::isFault( $t_cf_result ) ) { - return $t_cf_result; - } - - $t_set_custom_field_error = mci_issue_set_custom_fields( $t_issue_id, $p_issue['custom_fields'], false ); - if( $t_set_custom_field_error != null ) { - return $t_set_custom_field_error; - } - - if( isset( $p_issue['monitors'] ) ) { - mci_issue_set_monitors( $t_issue_id, $t_user_id, $p_issue['monitors'] ); - } - - if( isset( $t_notes ) && is_array( $t_notes ) ) { - foreach( $t_notes as $t_note ) { - $t_note = ApiObjectFactory::objectToArray( $t_note ); - - if( isset( $t_note['view_state'] ) ) { - $t_view_state = $t_note['view_state']; - } else { - $t_view_state = config_get( 'default_bugnote_view_status' ); - } - - $t_note_type = isset( $t_note['note_type'] ) ? (int)$t_note['note_type'] : BUGNOTE; - $t_note_attr = isset( $t_note['note_type'] ) ? $t_note['note_attr'] : ''; - - $t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state ); - $t_note_id = bugnote_add( - $t_issue_id, - $t_note['text'], - mci_get_time_tracking_from_note( $t_issue_id, $t_note ), - $t_view_state_id == VS_PRIVATE, - $t_note_type, - $t_note_attr, - $t_user_id, - false ); # don't send mail - - bugnote_process_mentions( $t_issue_id, $t_note_id, $t_note['text'] ); - - log_event( LOG_WEBSERVICE, 'bugnote id \'' . $t_note_id . '\' added to issue \'' . $t_issue_id . '\'' ); - } - } - - if( isset( $p_issue['tags'] ) && is_array( $p_issue['tags'] ) ) { - $t_tags_result = mci_tag_set_for_issue( $t_issue_id, $p_issue['tags'], $t_user_id ); - if( ApiObjectFactory::isFault( $t_tags_result ) ) { - return $t_tags_result; - } - } - - email_bug_added( $t_issue_id ); - - if( $t_bug_data->status != config_get( 'bug_submit_status' ) ) { - history_log_event( $t_issue_id, 'status', config_get( 'bug_submit_status' ) ); - } + $t_issue = ApiObjectFactory::objectToArray( $p_issue, /* recursive */ true ); + $t_data = array( + 'payload' => array( 'issue' => $t_issue ) + ); - if( $t_bug_data->resolution != config_get( 'default_bug_resolution' ) ) { - history_log_event( $t_issue_id, 'resolution', config_get( 'default_bug_resolution' ) ); - } + $t_command = new IssueAddCommand( $t_data ); + $t_result = $t_command->execute(); + $t_issue_id = (int)$t_result['issue_id']; return $t_issue_id; } diff --git a/api/soap/mc_project_api.php b/api/soap/mc_project_api.php index 9f9f62580f..440c3a8d50 100644 --- a/api/soap/mc_project_api.php +++ b/api/soap/mc_project_api.php @@ -23,6 +23,8 @@ * @link http://www.mantisbt.org */ +use Mantis\Exceptions\ClientException; + /** * Use a standard filter to get issues associated with the specified user. * @@ -675,13 +677,19 @@ function mci_project_custom_fields_validate( $p_project_id, &$p_custom_fields ) $t_custom_field = ApiObjectFactory::objectToArray( $t_custom_field ); if( !isset( $t_custom_field['value'] ) ) { - $t_error = 'Custom field has no value specified.'; - return ApiObjectFactory::faultBadRequest( $t_error ); + throw new ClientException( + 'Custom field has no value specified.', + ERROR_EMPTY_FIELD, + "custom_field['value']" + ); } if( !isset( $t_custom_field['field'] ) ) { - $t_error = 'Custom field with no specified id or name.'; - return ApiObjectFactory::faultBadRequest( $t_error ); + throw new ClientException( + 'Custom field with no specified id or name.', + ERROR_EMPTY_FIELD, + "custom_field['field']" + ); } $t_custom_field['field'] = ApiObjectFactory::objectToArray( $t_custom_field['field'] ); @@ -698,8 +706,11 @@ function mci_project_custom_fields_validate( $p_project_id, &$p_custom_fields ) continue; } - $t_error = 'Custom field with no specified id or name.'; - return ApiObjectFactory::faultBadRequest( $t_error ); + throw new ClientException( + 'Custom field with no specified id or name.', + ERROR_EMPTY_FIELD, + "custom_field['field']['id']" + ); } } @@ -717,15 +728,21 @@ function mci_project_custom_fields_validate( $p_project_id, &$p_custom_fields ) if( $t_def['require_report'] ) { if( !isset( $t_custom_field_values[$t_name] ) || is_blank( $t_custom_field_values[$t_name] ) ) { - $t_error = "Mandatory field '$t_name' is missing."; - return ApiObjectFactory::faultBadRequest( $t_error ); + throw new ClientException( + "Mandatory field '$t_name' is missing.", + ERROR_EMPTY_FIELD, + array( $t_name ) + ); } } if( isset( $t_custom_field_values[$t_name] ) && !custom_field_validate( $t_custom_field_id, $t_custom_field_values[$t_name] ) ) { - $t_error = "Invalid custom field '$t_name' value."; - return ApiObjectFactory::faultBadRequest( $t_error ); + throw new ClientException( + "Invalid custom field '$t_name' value.", + ERROR_EMPTY_FIELD, + array( $t_name ) + ); } } diff --git a/core/commands/IssueAddCommand.php b/core/commands/IssueAddCommand.php index c7ac0cb9b1..782ff1a43e 100644 --- a/core/commands/IssueAddCommand.php +++ b/core/commands/IssueAddCommand.php @@ -205,16 +205,9 @@ function validate() { $t_category = isset( $t_issue['category'] ) ? $t_issue['category'] : null; $t_category_id = mci_get_category_id( $t_category, $t_project_id ); - ApiObjectFactory::throwIfFault( $t_category_id ); - - $t_version_id = isset( $t_issue['version'] ) ? mci_get_version_id( $t_issue['version'], $t_project_id ) : 0; - ApiObjectFactory::throwIfFault( $t_version_id ); - - $t_fixed_in_version_id = isset( $t_issue['fixed_in_version'] ) ? mci_get_version_id( $t_issue['fixed_in_version'], $t_project_id ) : 0; - ApiObjectFactory::throwIfFault( $t_fixed_in_version_id ); - - $t_target_version_id = isset( $t_issue['target_version'] ) ? mci_get_version_id( $t_issue['target_version'], $t_project_id ) : 0; - ApiObjectFactory::throwIfFault( $t_target_version_id ); + $t_version_id = isset( $t_issue['version'] ) ? mci_get_version_id( $t_issue['version'], $t_project_id, 'version' ) : 0; + $t_fixed_in_version_id = isset( $t_issue['fixed_in_version'] ) ? mci_get_version_id( $t_issue['fixed_in_version'], $t_project_id, 'fixed_in_version' ) : 0; + $t_target_version_id = isset( $t_issue['target_version'] ) ? mci_get_version_id( $t_issue['target_version'], $t_project_id, 'target_version' ) : 0; $this->issue = new BugData; $this->issue->profile_id = 0; @@ -297,11 +290,7 @@ function validate() { } } - ### NOTE: wasn't in mci_issue_add() - helper_call_custom_function( 'issue_create_validate', array( $this->issue ) ); - - $t_cf_result = mci_project_custom_fields_validate( $t_project_id, $t_issue['custom_fields'] ); - ApiObjectFactory::throwIfFault( $t_cf_result ); + mci_project_custom_fields_validate( $t_project_id, $t_issue['custom_fields'] ); if( isset( $t_issue['attachments'] ) && !empty( $t_issue['attachments'] ) ) { if( !file_allow_bug_upload( $t_issue_id ) ) { @@ -313,6 +302,9 @@ function validate() { $this->files = $t_issue['attachments']; } + ### NOTE: wasn't in mci_issue_add() + helper_call_custom_function( 'issue_create_validate', array( $this->issue ) ); + ### NOTE: wasn't in mci_issue_add() # Allow plugins to pre-process bug data $this->issue = event_signal( 'EVENT_REPORT_BUG_DATA', $this->issue );