Validate User entered attribute against values that reside in an external text file
I have a "Ticket Number" attribute on a Work Item that needs to be validate against a list of valid Ticket Numbers that reside on an external text file. If a User enters a ticket number value that does not exist on the text file, he will not be allowed to save the work item. How do I go about implementing this?
3 answers
Hello,
you may want to use an HTTP ValueSet Provider to populate the possible and valid values for ticket number,
so that:
- user will not enter but select a valid value
- no validation will be required
The only constraint here is to have the file content as XML accessible from HTTP
Eric
Comments
also, what behavior do you expect? can't exit the field if the number isn't good or can't save the record.
all the server side solutions are the latter, some of the client side solutions are the former.
I think you would have to create a custom web application to read in and manage the list of valid numbers,
and provide a web service UI (rest or somesuch) to validate a selection. this service would handle caching and list reload, etc.. and u could do it right now, today.
then write an advisor that takes the input data on workitem save and calls that outside RTC web service.
(in the same server or not). the advisor could get the web service URL and attribute name from config parameters. I think the amount of code for both is pretty tiny.
I would probably do REST based service that returns 0 or 1 (false/true) to minimize the footprint,
and enable using the httpclient java support.
note: to do the http valueset you have to do the web app as the server anyhow and can only return XML with the LIST of items.. (which you don't want to do due to the size and UI usability impacts).
this solution is UI neutral, but only implements the error on save approach.
all the server side solutions are the latter, some of the client side solutions are the former.
I think you would have to create a custom web application to read in and manage the list of valid numbers,
and provide a web service UI (rest or somesuch) to validate a selection. this service would handle caching and list reload, etc.. and u could do it right now, today.
then write an advisor that takes the input data on workitem save and calls that outside RTC web service.
(in the same server or not). the advisor could get the web service URL and attribute name from config parameters. I think the amount of code for both is pretty tiny.
I would probably do REST based service that returns 0 or 1 (false/true) to minimize the footprint,
and enable using the httpclient java support.
note: to do the http valueset you have to do the web app as the server anyhow and can only return XML with the LIST of items.. (which you don't want to do due to the size and UI usability impacts).
this solution is UI neutral, but only implements the error on save approach.
here is a draft plugin that would implement my suggestion to use an external web service via rest.
this compiles successfully, but has not been tested in any way.
properties are xml in the process config of the advisor
here is one I did a while back.
<followup-action id="PerforceJobs.createJobs" name="Create Perforce Jobs">
<properties>
<property id="debug" value="true"/>
<property id="server" value="perforce"/>
<property id="port" value="1666"/>
<property id="service.userid" value="service"/>
and the code to get the parms
<code>
// get the plugin parameters into an easy access hash
Hashtable<String,String> props = new Hashtable<String,String>();
for (IProcessConfigurationElement child : participantConfig.getChildren())
{
for (IProcessConfigurationElement child1 : child.getChildren())
{
// if the configuration property name matches the one we are interested in
if(OurProperty.equalsIgnoreCase(child1.getName()))
props.put(child1.getAttribute("id"), child1.getAttribute("value"));
}
}
</code>
<code>
package com.sd.test;
import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.eclipse.core.runtime.IProgressMonitor;
import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.IAdvisorInfo;
import com.ibm.team.process.common.advice.IAdvisorInfoCollector;
import com.ibm.team.process.common.advice.runtime.IOperationAdvisor;
import com.ibm.team.repository.common.IAuditable;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.service.AbstractService;
import com.ibm.team.workitem.common.IAuditableCommon;
import com.ibm.team.workitem.common.ISaveParameter;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.IAttribute;
public class ValidateViaHttp extends AbstractService implements
IOperationAdvisor
{
@Override
public void run(AdvisableOperation operation,
IProcessConfigurationElement advisorConfiguration,
IAdvisorInfoCollector collector, IProgressMonitor monitor)
throws TeamRepositoryException
{
// get the operation data
Object data = operation.getOperationData();
// is this a 'save' operation?
if (data instanceof ISaveParameter)
{
// get the affected object
IAuditable auditable = ((ISaveParameter) data).getNewState();
// if this is a workitem
if (auditable instanceof IWorkItem)
{
// get the worker objects
IAuditableCommon iac = ((ISaveParameter) data)
.getSaveOperationParameter().getAuditableCommon();
// reference the right object type (cast)
IWorkItem workItem = (IWorkItem) auditable;
// access the workitem common service
// must be defined in the plugin.xml as required.
IWorkItemCommon iwc = getService(IWorkItemCommon.class);
HttpClient client = new HttpClient();
String serverurl = getStringConfigProperty("serverurl");
IAttribute attribute = iwc.findAttribute(workItem.getProjectArea(), advisorConfiguration
.getAttribute("attribute_id_string"), null);
// Create a method instance.
GetMethod method = new GetMethod(serverurl + "?validate="
+ workItem.getValue(attribute).toString());
// Provide custom retry handler is necessary
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try
{
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK)
{
System.err.println("Method failed: "
+ method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not
// binary data
System.out.println(new String(responseBody));
if (responseBody[0] != '1')
{
IAdvisorInfo info = collector.createProblemInfo(
"your error", "message here", "error");
// tell the caller to post an error, and reject the
// action
collector.addInfo(info);
}
}
catch (HttpException e)
{
System.err.println("Fatal protocol violation: "
+ e.getMessage());
e.printStackTrace();
}
catch (IOException e)
{
System.err.println("Fatal transport error: "
+ e.getMessage());
e.printStackTrace();
}
finally
{
// Release the connection.
method.releaseConnection();
}
}
}
}
}
</code>
this compiles successfully, but has not been tested in any way.
properties are xml in the process config of the advisor
here is one I did a while back.
<followup-action id="PerforceJobs.createJobs" name="Create Perforce Jobs">
<properties>
<property id="debug" value="true"/>
<property id="server" value="perforce"/>
<property id="port" value="1666"/>
<property id="service.userid" value="service"/>
and the code to get the parms
<code>
// get the plugin parameters into an easy access hash
Hashtable<String,String> props = new Hashtable<String,String>();
for (IProcessConfigurationElement child : participantConfig.getChildren())
{
for (IProcessConfigurationElement child1 : child.getChildren())
{
// if the configuration property name matches the one we are interested in
if(OurProperty.equalsIgnoreCase(child1.getName()))
props.put(child1.getAttribute("id"), child1.getAttribute("value"));
}
}
</code>
<code>
package com.sd.test;
import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.eclipse.core.runtime.IProgressMonitor;
import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.IAdvisorInfo;
import com.ibm.team.process.common.advice.IAdvisorInfoCollector;
import com.ibm.team.process.common.advice.runtime.IOperationAdvisor;
import com.ibm.team.repository.common.IAuditable;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.service.AbstractService;
import com.ibm.team.workitem.common.IAuditableCommon;
import com.ibm.team.workitem.common.ISaveParameter;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.IAttribute;
public class ValidateViaHttp extends AbstractService implements
IOperationAdvisor
{
@Override
public void run(AdvisableOperation operation,
IProcessConfigurationElement advisorConfiguration,
IAdvisorInfoCollector collector, IProgressMonitor monitor)
throws TeamRepositoryException
{
// get the operation data
Object data = operation.getOperationData();
// is this a 'save' operation?
if (data instanceof ISaveParameter)
{
// get the affected object
IAuditable auditable = ((ISaveParameter) data).getNewState();
// if this is a workitem
if (auditable instanceof IWorkItem)
{
// get the worker objects
IAuditableCommon iac = ((ISaveParameter) data)
.getSaveOperationParameter().getAuditableCommon();
// reference the right object type (cast)
IWorkItem workItem = (IWorkItem) auditable;
// access the workitem common service
// must be defined in the plugin.xml as required.
IWorkItemCommon iwc = getService(IWorkItemCommon.class);
HttpClient client = new HttpClient();
String serverurl = getStringConfigProperty("serverurl");
IAttribute attribute = iwc.findAttribute(workItem.getProjectArea(), advisorConfiguration
.getAttribute("attribute_id_string"), null);
// Create a method instance.
GetMethod method = new GetMethod(serverurl + "?validate="
+ workItem.getValue(attribute).toString());
// Provide custom retry handler is necessary
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try
{
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK)
{
System.err.println("Method failed: "
+ method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not
// binary data
System.out.println(new String(responseBody));
if (responseBody[0] != '1')
{
IAdvisorInfo info = collector.createProblemInfo(
"your error", "message here", "error");
// tell the caller to post an error, and reject the
// action
collector.addInfo(info);
}
}
catch (HttpException e)
{
System.err.println("Fatal protocol violation: "
+ e.getMessage());
e.printStackTrace();
}
catch (IOException e)
{
System.err.println("Fatal transport error: "
+ e.getMessage());
e.printStackTrace();
}
finally
{
// Release the connection.
method.releaseConnection();
}
}
}
}
}
</code>
Comments
sam detweiler
Jan 03 '14, 12:45 a.m.Jack C
Jan 03 '14, 10:34 a.m.sam detweiler
Jan 03 '14, 10:40 a.m.Jack C
Jan 03 '14, 1:31 p.m.sam detweiler
Jan 03 '14, 1:48 p.m.