New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[discuss] SOAP API: add method for searching within issues #560
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,13 @@ | |
<xsd:schema targetNamespace="http://futureware.biz/mantisconnect"> | ||
<xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> | ||
<xsd:import namespace="http://schemas.xmlsoap.org/wsdl/"/> | ||
<xsd:complexType name="IntegerArray"> | ||
<xsd:complexContent> | ||
<xsd:restriction base="SOAP-ENC:Array"> | ||
<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="xsd:integer[]"/> | ||
</xsd:restriction> | ||
</xsd:complexContent> | ||
</xsd:complexType> | ||
<xsd:complexType name="StringArray"> | ||
<xsd:complexContent> | ||
<xsd:restriction base="SOAP-ENC:Array"> | ||
|
@@ -259,6 +266,43 @@ | |
<xsd:element name="url" type="xsd:string" minOccurs="0"/> | ||
</xsd:all> | ||
</xsd:complexType> | ||
<xsd:complexType name="FilterSearchData"> | ||
<xsd:all> | ||
<xsd:element name="project_id" type="tns:IntegerArray" minOccurs="1"/> | ||
<xsd:element name="search" type="xsd:string" minOccurs="0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want a more self-explanatory term than search? E.g. text_search, full_text_search or similar There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the web mantisbt filter UI is it named as search so i used the same name. |
||
<xsd:element name="category" type="tns:StringArray" minOccurs="0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. category_name would be clearer IMO |
||
<xsd:element name="severity_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="status_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="priority_id" type="tns:StringArray" minOccurs="0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is priority_id a StringArray? |
||
<xsd:element name="reporter_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="handler_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="note_user_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="resolution_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="product_version" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="user_monitor_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="hide_status_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="sort" type="xsd:string" minOccurs="0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sort_field ? |
||
<xsd:element name="sort_direction" type="xsd:string" minOccurs="0"/> | ||
<xsd:element name="sticky" type="xsd:boolean" minOccurs="0"/> | ||
<xsd:element name="view_state_id" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="fixed_in_version" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="target_version" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="platform" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="os" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="os_build" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="start_day" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="start_month" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="start_year" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="end_day" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="end_month" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="end_year" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="tag_string" type="tns:StringArray" minOccurs="0"/> | ||
<xsd:element name="tag_select" type="tns:IntegerArray" minOccurs="0"/> | ||
<xsd:element name="custom_fields" type="tns:FilterCustomFieldArray" minOccurs="0"/> | ||
<xsd:element name="page_number" type="xsd:integer" minOccurs="1"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically speaking the page_number and issues_per_page are not part of the filter definition, but are part of a search request. I wonder if we should have a FilterDefinition and SearchRequest. A FilterSearchData can reference a filter id or a filter definition + page size / number. This makes the filter definition re-usable with other methods to add/get filters for example. |
||
<xsd:element name="issues_per_page" type="xsd:integer" minOccurs="1"/> | ||
</xsd:all> | ||
</xsd:complexType> | ||
<xsd:complexType name="FilterDataArray"> | ||
<xsd:complexContent> | ||
<xsd:restriction base="SOAP-ENC:Array"> | ||
|
@@ -288,6 +332,20 @@ | |
<xsd:element name="require_closed" type="xsd:boolean" minOccurs="0"/> | ||
</xsd:all> | ||
</xsd:complexType> | ||
<xsd:complexType name="FilterCustomField"> | ||
<xsd:all> | ||
<xsd:element name="name" type="xsd:string" minOccurs="0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We typically use ObjectRef for cases where a user can supply an id or a name to reference an entity. |
||
<xsd:element name="id" type="xsd:integer" minOccurs="0"/> | ||
<xsd:element name="value" type="tns:StringArray" minOccurs="0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we require the value for custom field? Otherwise, why would the record exist? |
||
</xsd:all> | ||
</xsd:complexType> | ||
<xsd:complexType name="FilterCustomFieldArray"> | ||
<xsd:complexContent> | ||
<xsd:restriction base="SOAP-ENC:Array"> | ||
<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:FilterCustomField[]"/> | ||
</xsd:restriction> | ||
</xsd:complexContent> | ||
</xsd:complexType> | ||
<xsd:complexType name="CustomFieldDefinitionDataArray"> | ||
<xsd:complexContent> | ||
<xsd:restriction base="SOAP-ENC:Array"> | ||
|
@@ -747,6 +805,18 @@ | |
<part name="per_page" type="xsd:integer" /></message> | ||
<message name="mc_filter_get_issue_headersResponse"> | ||
<part name="return" type="tns:IssueHeaderDataArray" /></message> | ||
<message name="mc_filter_search_issue_headersRequest"> | ||
<part name="username" type="xsd:string" /> | ||
<part name="password" type="xsd:string" /> | ||
<part name="filter" type="tns:FilterSearchData" /></message> | ||
<message name="mc_filter_search_issue_headersResponse"> | ||
<part name="return" type="tns:IssueHeaderDataArray" /></message> | ||
<message name="mc_filter_search_issuesRequest"> | ||
<part name="username" type="xsd:string" /> | ||
<part name="password" type="xsd:string" /> | ||
<part name="filter" type="tns:FilterSearchData" /></message> | ||
<message name="mc_filter_search_issuesResponse"> | ||
<part name="return" type="tns:IssueDataArray" /></message> | ||
<message name="mc_config_get_stringRequest"> | ||
<part name="username" type="xsd:string" /> | ||
<part name="password" type="xsd:string" /> | ||
|
@@ -1094,6 +1164,16 @@ | |
<input message="tns:mc_filter_get_issue_headersRequest"/> | ||
<output message="tns:mc_filter_get_issue_headersResponse"/> | ||
</operation> | ||
<operation name="mc_filter_search_issue_headers"> | ||
<documentation>Get the issue headers that match the custom filter and paging details.</documentation> | ||
<input message="tns:mc_filter_search_issue_headersRequest"/> | ||
<output message="tns:mc_filter_search_issue_headersResponse"/> | ||
</operation> | ||
<operation name="mc_filter_search_issues"> | ||
<documentation>Get the issues that match the custom filter and paging details.</documentation> | ||
<input message="tns:mc_filter_search_issuesRequest"/> | ||
<output message="tns:mc_filter_search_issuesResponse"/> | ||
</operation> | ||
<operation name="mc_config_get_string"> | ||
<documentation>Get the value for the specified configuration variable.</documentation> | ||
<input message="tns:mc_config_get_stringRequest"/> | ||
|
@@ -1432,6 +1512,16 @@ | |
<input><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></input> | ||
<output><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></output> | ||
</operation> | ||
<operation name="mc_filter_search_issue_headers"> | ||
<soap:operation soapAction="http://www.mantisbt.org/bugs/api/soap/mantisconnect.php/mc_filter_search_issue_headers" style="rpc"/> | ||
<input><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></input> | ||
<output><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></output> | ||
</operation> | ||
<operation name="mc_filter_search_issues"> | ||
<soap:operation soapAction="http://www.mantisbt.org/bugs/api/soap/mantisconnect.php/mc_filter_search_issues" style="rpc"/> | ||
<input><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></input> | ||
<output><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></output> | ||
</operation> | ||
<operation name="mc_config_get_string"> | ||
<soap:operation soapAction="http://www.mantisbt.org/bugs/api/soap/mantisconnect.php/mc_config_get_string" style="rpc"/> | ||
<input><soap:body use="encoded" namespace="http://futureware.biz/mantisconnect" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/></input> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,50 @@ | |
* @link http://www.mantisbt.org | ||
*/ | ||
|
||
# Path to MantisBT is assumed to be the grand parent directory. If this is not | ||
# the case, then this variable should be set to the MantisBT path. | ||
# This can not be a configuration option, then MantisConnect configuration | ||
# needs MantisBT to be included first to make use of the constants and possibly | ||
# configuration defined in MantisBT. | ||
|
||
require_api( 'filter_constants_inc.php' ); | ||
|
||
// doesn't contain 'custom_fields' | ||
$_SOAP_API_TO_FILTER_API_NAMES = array ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically we use $g_some_normal_name |
||
'search' => FILTER_PROPERTY_SEARCH, | ||
'category' => FILTER_PROPERTY_CATEGORY_ID, | ||
'severity_id' => FILTER_PROPERTY_SEVERITY, | ||
'status_id' => FILTER_PROPERTY_STATUS, | ||
'priority_id' => FILTER_PROPERTY_PRIORITY, | ||
'reporter_id' => FILTER_PROPERTY_REPORTER_ID, | ||
'handler_id' => FILTER_PROPERTY_HANDLER_ID, | ||
'project_id' => FILTER_PROPERTY_PROJECT_ID, | ||
'note_user_id' => FILTER_PROPERTY_NOTE_USER_ID, | ||
'resolution_id' => FILTER_PROPERTY_RESOLUTION, | ||
'version' => FILTER_PROPERTY_VERSION, | ||
|
||
'user_monitor_id' => FILTER_PROPERTY_MONITOR_USER_ID, | ||
'hide_status_id' => FILTER_PROPERTY_HIDE_STATUS, | ||
'sort' => FILTER_PROPERTY_SORT_FIELD_NAME, | ||
'sort_direction' => FILTER_PROPERTY_SORT_DIRECTION, | ||
'sticky' => FILTER_PROPERTY_STICKY, | ||
'view_state' => FILTER_PROPERTY_VIEW_STATE, | ||
'fixed_in_version' => FILTER_PROPERTY_FIXED_IN_VERSION, | ||
'target_version' => FILTER_PROPERTY_TARGET_VERSION, | ||
'platform' => FILTER_PROPERTY_PLATFORM, | ||
'os' => FILTER_PROPERTY_OS, | ||
'os_build' => FILTER_PROPERTY_OS_BUILD, | ||
'start_day' => FILTER_PROPERTY_START_DAY, | ||
'start_month' => FILTER_PROPERTY_START_MONTH, | ||
'start_year' => FILTER_PROPERTY_START_YEAR, | ||
'end_day' => FILTER_PROPERTY_END_DAY, | ||
'end_month' => FILTER_PROPERTY_END_MONTH, | ||
'end_year' => FILTER_PROPERTY_END_YEAR, | ||
'tag_string' => FILTER_PROPERTY_TAG_STRING, | ||
'tag_select' => FILTER_PROPERTY_TAG_SELECT, | ||
); | ||
|
||
|
||
/** | ||
* Get all user defined issue filters for the given project. | ||
* | ||
|
@@ -147,3 +191,165 @@ function mc_filter_get_issue_headers( $p_username, $p_password, $p_project_id, $ | |
|
||
return $t_result; | ||
} | ||
|
||
/** | ||
* Get all issue headers matching the custom filter. | ||
* | ||
* @param string $p_username The name of the user trying to access the filters. | ||
* @param string $p_password The password of the user. | ||
* @param FilterSearchData $p_filter_search The custom filter. | ||
* @return array that represents an IssueHeaderDataArray structure | ||
*/ | ||
function mc_filter_search_issue_headers( $p_username, $p_password, $p_filter_search ) { | ||
|
||
global $_SOAP_API_TO_FILTER_API_NAMES; | ||
|
||
$t_user_id = mci_check_login( $p_username, $p_password ); | ||
|
||
if( $t_user_id === false ) { | ||
return mci_soap_fault_login_failed(); | ||
} | ||
|
||
// object to array | ||
if( is_object($p_filter_search) ) { | ||
$p_filter_search = get_object_vars($p_filter_search); | ||
} | ||
|
||
$t_project_id = $p_filter_search['project_id']; | ||
|
||
if( !mci_has_readonly_access( $t_user_id, $t_project_id ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are checking access against a single project here. We are unlikely to disclose projects, but it is likely that APIs will fail with some html that will break soap. We should run this validation on all the project ids supplied and fail if the list contains any non-accessible projects. Another option is to filter the project_ids based on access and just pass to the filters the ones that the user has access to. |
||
return mci_soap_fault_access_denied( $t_user_id ); | ||
} | ||
|
||
$t_filter = array( '_view_type' => 'advanced' ); | ||
|
||
// default fields | ||
foreach( $_SOAP_API_TO_FILTER_API_NAMES as $t_soap_name => $t_filter_name ) { | ||
if ( isset ( $p_filter_search[$t_soap_name]) ) { | ||
|
||
$t_value = $p_filter_search[$t_soap_name]; | ||
error_log('Simple extraction, value is ' . print_r($t_value, true) . ' .' ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would drop these error_logs in favor of log_event() calls (see core/logging_api.php). This directs the log to MantisBT log files and have the log entries visibility controlled by $g_logging_level (see config_defaults_inc.php) having LOG_FILTERING set. |
||
$t_filter[$t_filter_name] = $t_value; | ||
} | ||
} | ||
|
||
// custom fields | ||
if( isset ( $p_filter_search['custom_fields']) ) { | ||
foreach( $p_filter_search['custom_fields'] as $t_custom_field ) { | ||
|
||
// object to array | ||
if( is_object($t_custom_field) ) { | ||
$t_custom_field = get_object_vars($t_custom_field); | ||
} | ||
|
||
// if is set custom_field's id, use it primary | ||
if ( isset($t_custom_field['id']) ) { | ||
$t_custom_field_id = $t_custom_field['id']; | ||
} | ||
else { | ||
$t_custom_field_id = custom_field_get_id_from_name( $t_custom_field['name'] ); | ||
} | ||
|
||
$t_value = $t_custom_field['value']; | ||
error_log('custom field id ' . $t_custom_field_id . print_r($t_value, true) . ' .' ); | ||
$t_filter['custom_fields'][$t_custom_field_id] = $t_value; | ||
} | ||
} | ||
|
||
$t_filter = filter_ensure_valid_filter( $t_filter ); | ||
|
||
$t_result = array(); | ||
$t_page_number = 0; | ||
$t_per_page = $p_filter_search['issues_per_page']; | ||
$t_page_count = 0; | ||
$t_bug_count = 0; | ||
$t_rows = filter_get_bug_rows( $t_page_number, $t_per_page, $t_page_count, $t_bug_count, $t_filter ); | ||
|
||
foreach( $t_rows as $t_issue_data ) { | ||
$t_result[] = mci_issue_data_as_header_array( $t_issue_data ); | ||
} | ||
|
||
return $t_result; | ||
} | ||
|
||
/** | ||
* Get all issues matching the custom filter. | ||
* | ||
* @param string $p_username The name of the user trying to access the filters. | ||
* @param string $p_password The password of the user. | ||
* @param FilterSearchData $p_filter_search The custom filter. | ||
* @return array that represents an IssueDataArray structure | ||
*/ | ||
function mc_filter_search_issues( $p_username, $p_password, $p_filter_search ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like we have too much code duplication between the two methods. How about the following:
Suggests for better name for mc_issue_get_many() is certainly welcome! In some cases, this may mean an extra round trip, but it is useful to have our methods usable in more than one scenario. We can even go to the model where we just return ids from the search and then have methods than return array of headers or array of full issues. |
||
|
||
global $_SOAP_API_TO_FILTER_API_NAMES; | ||
|
||
$t_user_id = mci_check_login( $p_username, $p_password ); | ||
|
||
if( $t_user_id === false ) { | ||
return mci_soap_fault_login_failed(); | ||
} | ||
|
||
$t_lang = mci_get_user_lang( $t_user_id ); | ||
|
||
// object to array | ||
if( is_object($p_filter_search) ) { | ||
$p_filter_search = get_object_vars($p_filter_search); | ||
} | ||
|
||
$t_project_id = $p_filter_search['project_id']; | ||
|
||
if( !mci_has_readonly_access( $t_user_id, $t_project_id ) ) { | ||
return mci_soap_fault_access_denied( $t_user_id ); | ||
} | ||
|
||
$t_filter = array( '_view_type' => 'advanced' ); | ||
|
||
// default fields | ||
foreach( $_SOAP_API_TO_FILTER_API_NAMES as $t_soap_name => $t_filter_name ) { | ||
if ( isset ( $p_filter_search[$t_soap_name]) ) { | ||
|
||
$t_value = $p_filter_search[$t_soap_name]; | ||
error_log('Simple extraction, value is ' . print_r($t_value, true) . ' .' ); | ||
$t_filter[$t_filter_name] = $t_value; | ||
} | ||
} | ||
|
||
// custom fields | ||
if( isset ( $p_filter_search['custom_fields']) ) { | ||
foreach( $p_filter_search['custom_fields'] as $t_custom_field ) { | ||
|
||
// object to array | ||
if( is_object($t_custom_field) ) { | ||
$t_custom_field = get_object_vars($t_custom_field); | ||
} | ||
|
||
// if is set custom_field's id, use it primary | ||
if ( isset($t_custom_field['id']) ) { | ||
$t_custom_field_id = $t_custom_field['id']; | ||
} | ||
else { | ||
$t_custom_field_id = custom_field_get_id_from_name( $t_custom_field['name'] ); | ||
} | ||
|
||
$t_value = $t_custom_field['value']; | ||
error_log('custom field id ' . $t_custom_field_id . print_r($t_value, true) . ' .' ); | ||
$t_filter['custom_fields'][$t_custom_field_id] = $t_value; | ||
} | ||
} | ||
|
||
$t_filter = filter_ensure_valid_filter( $t_filter ); | ||
|
||
$t_result = array(); | ||
$t_page_number = 0; | ||
$t_per_page = $p_filter_search['issues_per_page']; | ||
$t_page_count = 0; | ||
$t_bug_count = 0; | ||
$t_rows = filter_get_bug_rows( $t_page_number, $t_per_page, $t_page_count, $t_bug_count, $t_filter ); | ||
|
||
foreach( $t_rows as $t_issue_data ) { | ||
$t_result[] = mci_issue_data_as_array( $t_issue_data, $t_user_id, $t_lang ); | ||
} | ||
|
||
return $t_result; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should make fields like project_id, issues_per_page and page_number required. Each one has a sensible default value: project_id should default to 0 (for all projects), issues_per_project to the configuration option that determines number of issues on View Issues page, page number to first page (I assume we are 1-based).