I have written a common function to connect to DB. This function should be used by multiple script JQL functions.
But it fails to load. Here is what I am doing,
STEP 1: Written a class called 'DBUtils' . Inside that class, defined function called executeQuery(). I have added this script under '
/var/atlassian/application-data/jira/scripts/com/onresolve/jira/groovy/jql' directory.
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.ComponentManager;
import groovy.sql.Sql;
import java.sql.Connection;
import org.ofbiz.core.entity.ConnectionFactory;
import org.ofbiz.core.entity.DelegatorInterface;
import org.apache.log4j.Logger;
/**
* This script is to connect to JIRA DB and execute the give query
* @param sqlStmt query to be executed
* @return results as list of HashMaps Ex: [ {key, value}, {key, value} ]. Whoever call this function, has to parse the output and fetch the required input based on the given query
*
* @since 27th March, 2019
* @version 1.0
*/
class DBUtils {
def executeQuery(String sqlStmt) {
// Defining arrayList to store result returns by query.
def results = [];
Logger log = Logger.getLogger("Connecting to DB");
if(sqlStmt.split(' ')[0].toLowerCase() == 'select') {
// Connecting to the database.
def componentManager = ComponentManager.getInstance();
def delegator = (DelegatorInterface) componentManager.getComponentInstanceOfType(DelegatorInterface.class);
String helperName = delegator.getGroupHelperName("default");
Connection conn = ConnectionFactory.getConnection(helperName);
Sql sql = new Sql(conn);
// Execute the given query
try {
results = sql.rows(sqlStmt)
} catch (Exception e) {
log.error("Getting error while Script JQL Function trying to connect to DB and execute the query : ${sqlStmt}" + e.getMessage() + "\n");
} finally {
sql.close();
}
}
return results
}
}
STEP 2: Have written separate JQL function script and tried using DBUtils class's function (executeQuery()) by creating a object (new DBUtils().executeQuery(query)). But this script is failed to load.
package com.onresolve.jira.groovy.jql;
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.jql.parser.JqlQueryParser;
import com.atlassian.jira.jql.query.LuceneQueryBuilder;
import com.atlassian.jira.jql.query.QueryCreationContext;
import com.atlassian.jira.jql.validator.NumberOfArgumentsValidator;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.util.MessageSet;
import com.atlassian.query.clause.TerminalClause;
import com.atlassian.query.operand.FunctionOperand;
import DBUtils;
import groovy.util.logging.Log4j;
/**
* The ChangedFrom program is implemented to get the list of feature issues which had given value for the given custom field.
* This program will enable history search for the custom fields on feature issue.
*
* @arguments It accepts two arguments.
* Field Name : Name of the custom field
* Changed from value : Value of the custom field
*
* @since 19th March, 2019
* @version 1.0
**/
@Log4j
class ChangedFrom extends AbstractScriptedJqlFunction implements JqlQueryFunction {
// Defining an objects to use JQL query parser
def queryParser = ComponentAccessor.getComponent(JqlQueryParser);
def luceneQueryBuilder = ComponentAccessor.getComponent(LuceneQueryBuilder);
def searchService = ComponentAccessor.getComponent(SearchService);
def applicationUser = ComponentAccessor.getJiraAuthenticationContext().getUser();
/**
* This method is to get the description of the script JQL function.
* This description will be displayed when we search for the ChangedFrom() function in issue navigator in jira application.
**/
@Override
String getDescription() {
"Return feature issues which had the given value for the given custom field"
}
/**
* validate function is to validate the operand and JQL query which is formed based on operand.
* @return It return the error message if operand is incorrect/null
**/
@Override
MessageSet validate(ApplicationUser user, FunctionOperand operand, TerminalClause terminalClause) {
def messageSet = new NumberOfArgumentsValidator(2, getI18n()).validate(operand);
if (messageSet.hasAnyErrors()) {
return messageSet;
}
// Forming the JQL and validating it
def field = '"' + operand.args.first() + '"';
def value = '"' + operand.args.last() + '"';
def queryStr = "${field} = ${value}";
def query = queryParser.parseQuery(queryStr);
messageSet = searchService.validateQuery(user, query);
messageSet
}
/**
* getArguments() function is to define the description and property of the arguments.
* @return argument property as a List<Map>
**/
@Override
List<Map> getArguments() {
[
[
description: "Field Name",
optional: false,
],
[
description: "Changed From Value",
optional: false,
]
]
}
// To define the function name for script JQL function.
@Override
String getFunctionName() {
"changedFrom"
}
/**
* getQuery function is to get the query context which is entered in issue navigator, perform defined logic/operation to get deseried outcome,
* form outcome and return the JQL result
*
* @return list of feature issues in issue navigator in JIRA application
**/
@Override
Query getQuery(QueryCreationContext queryCreationContext, FunctionOperand operand, TerminalClause terminalClause) {
//log.setLevel(org.apache.log4j.Level.DEBUG); //Uncomment this code to enable debugging logs
// Grep custom field name and it's value from operand
def fieldName = operand.args.first().toString();
def value = operand.args.last().toString();
//log.debug("Field Name = ${fieldName} - - - Value = ${value}"); //Uncomment this code to enable debugging logs
// Defining arrayList to store feature keys.
def featureLinkList = [];
// SQL query to fetch value from db
def sqlStmt = "select some query";
if(sqlStmt.split(' ')[0].toLowerCase() == 'select') {
// Connecting to JIRA DB and execute given query
def dbUtils = new DBUtils();
def temp = dbUtils.executeQuery(sqlStmt);
temp.each {
featureLinkList.add("${it.featurekey}");
}
}
//log.debug("SQL Query : ${sqlStmt}"); //Uncomment this code to enable debugging logs
/**
* Framing the Query(using the data fetched from DB) to fetch Feature issues through JQL
* if the SQL doesn't return anything, then it will return 'No Issues found'
*/
if(featureLinkList)
{
def queryTemp = featureLinkList.unique().join(',').toString();
def secondQuery = "key in (" + queryTemp + ")";
def secondquery = queryParser.parseQuery(secondQuery);
luceneQueryBuilder.createLuceneQuery(queryCreationContext, secondquery.whereClause);
}
else
{
def secondQuery = "key in (FEATURE-0)";
def secondquery = queryParser.parseQuery(secondQuery);
luceneQueryBuilder.createLuceneQuery(queryCreationContext, secondquery.whereClause);
}
}
}//End of the script;
What am i doing wrong? Can anyone pls help me?
Hi @nallu ,
I have diagnosed your "changedFrom.groovy" script and can see the following problems:
1- Your class name is different from the file name (Case sensitive). These must be the same as recommended in Java standards.
2- You are missing the following import in the changedFrom file:
import org.apache.lucene.search.Query
Please refer to this guide section to see where we show this import being used. (This is probably the main cause of your problem)
3- I cannot see your package structure but make sure the ChangedFrom class can find your DBUtils class
I would advise you do not allow user input to be directly interpolated into a SQL query. This is how you would become susceptible to SQL injection attacks.
See these links here:
https://www.owasp.org/index.php/SQL_Injection
https://www.w3schools.com/sql/sql_injection.asp
You should use a Prepared Statement to define your SQL before it is executed. See here for why and how you should use Prepared Statements.
Regards,
Matthew
Hi @Matthew Clark , Thanks so much for taking a look.
When I keep both file name and class name same as DBUtils, It throws below exception
root@devjira100:~> tail -f /var/atlassian/application-data/jira/log/atlassian-jira.log
DBUtils obj = new DBUtils();
^
file:/var/atlassian/application-data/jira/scripts/com/onresolve/jira/groovy/jql/changedFrom.groovy: 36: reference to DBUtils is ambiguous, both class DBUtils and DBUtils match
@ line 36, column 24.
DBUtils obj = new DBUtils();
^
2 errors
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @nallu
Have you changed the scripts at all because that line number 36 does not correspond to the scripts I have seen?
When I mentioned changing the class name I meant just change the script file called changedFrom.groovy to match the class name ChangedFrom.
Can you show me the full script that presents this new error?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Matthew Clark , 36 line is where i m creating an object for DBUtils class. But I haven't explicitly import the DBUtils class in ChangedFrom script since both resides in same directory. Do I have to? If so, How should i do it?
Here is a snippet of the code,
1 package com.onresolve.jira.groovy.jql;
2
3 import com.atlassian.jira.bc.issue.search.SearchService;
4 import com.atlassian.jira.component.ComponentAccessor;
5 import com.atlassian.jira.ComponentManager;
6 import com.atlassian.jira.jql.parser.JqlQueryParser;
7 import com.atlassian.jira.jql.query.LuceneQueryBuilder;
8 import com.atlassian.jira.jql.query.QueryCreationContext;
9 import com.atlassian.jira.jql.validator.NumberOfArgumentsValidator;
10 import com.atlassian.jira.user.ApplicationUser;
11 import com.atlassian.jira.util.MessageSet;
12 import com.atlassian.query.clause.TerminalClause;
13 import com.atlassian.query.operand.FunctionOperand;
14 import groovy.sql.Sql;
15 import groovy.util.logging.Log4j;
16 import java.sql.Connection;
17 import org.apache.lucene.search.Query;
18 import org.ofbiz.core.entity.ConnectionFactory;
19 import org.ofbiz.core.entity.DelegatorInterface;
20
21 /**
22 * The ChangedFrom program is implemented to get the list of feature issues which had given value for the given custom field.
23 * This program will enable history search for the custom fields on feature issue.
24 *
25 * @arguments It accepts two arguments.
26 * Field Name : Name of the custom field
27 * Changed from value : Value of the custom field
28 *
29 * @since 19th March, 2019
30 * @version 1.0
31 **/
32
33 @Log4j
34 class ChangedFrom extends AbstractScriptedJqlFunction implements JqlQueryFunction {
35
36 DBUtils obj = new DBUtils();
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I think that is because you are no longer importing your custom DBUtils class in your list of imports at the top of your file.
It was in your original so you must have accidentally deleted it.
Update:
I misread the last part of your update. Yes, you do need to import the DBUtils class. If the DBUtils.groovy file and the "ChangedFrom.groovy" file are in the root directory <JiraHome>/scripts/ then you can import it the same way you did in the first "ChangedFrom.groovy" script you sent us.
import DBUtils
But if you wanted to put the util class in a sub directory you can. We show an example of this here.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @nallu ,
Better ask here https://community.developer.atlassian.com/ .
I not sure what mean 'script is failed to load'. Could You please give logs?
B.R.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.