It's all about the answers!

Ask a question

Validate User entered attribute against values that reside in an external text file


Jack C (134) | asked Jan 02 '14, 10:40 p.m.
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?

Comments
sam detweiler commented Jan 03 '14, 12:45 a.m.

where is this file? server or client side?

note that many servers restrict where in the filesystem web apps can access.

how do you want to do this validation? check on save is probably doable, but allows the user to enter bad data.

how many items are in the list? it might be better to provide a selection list, with the http filtered value set.


Jack C commented Jan 03 '14, 10:34 a.m.

The text file containing all the valid ticket numbers resides on the Server side so that all Users that complete the work item can have their ticket number validated.

Users won't allow to enter any data that does not meet the formatting requirements of the ticket number. We have a validation set up to validate the formatting. But even if a User enters a ticket number value that meets the correct formatting, the value entered may not be on the list.

There are hundreds of items and the list of numbers are updated periodically. 


sam detweiler commented Jan 03 '14, 10:40 a.m.

yeh.. hundreds of items in the dropdown list is not really useful for the users.

we had the same problem with a support ticket number field.. we could validate it (numbers/text in the right order) but couldn't tell if it was a truly valid entry.

my proposed solution was a live integration between the two systems, such that the rtc user never entered the ticket number, the rtc workitem was created because of the ticket and there was a link between the two (in both systems).
some data was synched back & forth.

altho that design was accepted, built and tested, a new mgmt team came in and changed directions on the systems involved so this never went to production.


Jack C commented Jan 03 '14, 1:31 p.m.

That's an interesting solution. But our current business process does not work like the one described in your proposed solution. Hmmmm.... Can you explain more about the 'live integration linkage'? How did you intend to do this live integration?

Many Thanks in advance.


sam detweiler commented Jan 03 '14, 1:48 p.m.

there were two implementations

1. I wrote an RTC External Repository Manager which communicated with the support system. this required a web service application to take the incoming requests from the support system and interact with RTC (create/update workitems).  this is a lot of work.. recovery, queuing, retstart, config on the fly, RTC version interoperability testing, etc..

2. we used an external vendor product, Kovair Omnibus, to do this. (we were going to use the same product to synch RTC with other systems as well). they already had all the other operational stuff as part of their product, so there was only some business rule mapping and data object field mappings/translations to configure.

3 answers



permanent link
Eric Jodet (6.3k5111120) | answered Jan 03 '14, 4:02 a.m.
JAZZ DEVELOPER
 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
Jack C commented Jan 03 '14, 1:23 p.m.

There will be several hundreds of ticket number values. This will be overwhelming to be displayed to the User in a drop down.


Brian Fleming commented Jan 07 '14, 4:47 p.m.

You could use a Value Set Picker as the presentation kind to prevent such an overwhelming list in a dropdown.  See number 5 in the procedure here:
http://pic.dhe.ibm.com/infocenter/clmhelp/v4r0m3/index.jsp?re=1&topic=/com.ibm.team.workitem.doc/topics/t_configuring_http_filtered_value_set.html&scope=null


permanent link
sam detweiler (12.5k6195201) | answered Jan 03 '14, 1:41 p.m.
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.

permanent link
sam detweiler (12.5k6195201) | answered Jan 04 '14, 10:06 p.m.
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>

Your answer


Register or to post your answer.


Dashboards and work items are no longer publicly available, so some links may be invalid. We now provide similar information through other means. Learn more here.