Skip to content

Commit

Permalink
Merge pull request palladiumkenya#1837 from ojwanganto/feat/sql-query…
Browse files Browse the repository at this point in the history
…-endpoint

(feat) add sql query endpoint
  • Loading branch information
makombe authored Mar 13, 2024
2 parents bf9bfda + cab6260 commit 22bba38
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import org.openmrs.Location;
import org.openmrs.Patient;
import org.openmrs.Visit;
import org.openmrs.annotation.Authorized;
import org.openmrs.api.OpenmrsService;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
Expand Down Expand Up @@ -95,4 +97,14 @@ public interface KenyaEmrService extends OpenmrsService {

public List<Object> executeSqlQuery(String query, Map<String, Object> substitutions);
public List<Object> executeHqlQuery(String query, Map<String, Object> substitutions);

/**
* Executes a sql query with params.
* Adapted from Bahmni core
* @param sqlQuery
* @param params
* @return
*/
@Authorized
public List<SimpleObject> search(String sqlQuery, Map<String, String[]> params);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@
import org.openmrs.module.kenyaemr.metadata.CommonMetadata;
import org.openmrs.module.kenyaemr.metadata.FacilityMetadata;
import org.openmrs.module.kenyaemr.metadata.HivMetadata;
import org.openmrs.module.kenyaemr.util.RowMapper;
import org.openmrs.module.kenyaemr.util.SqlQueryHelper;
import org.openmrs.module.kenyaemr.wrapper.Facility;
import org.openmrs.module.metadatadeploy.MetadataUtils;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.openmrs.util.DatabaseUpdater;
import org.openmrs.util.OpenmrsUtil;
import org.openmrs.util.PrivilegeConstants;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;

/**
* Implementations of business logic methods for KenyaEMR
Expand Down Expand Up @@ -258,4 +261,45 @@ public List<Object> executeSqlQuery(String query, Map<String, Object> substituti
public List<Object> executeHqlQuery(String query, Map<String, Object> substitutions) {
return dao.executeHqlQuery(query, substitutions);
}

/**
* @param queryId
* @param params
* @return
*/
@Override
public List<SimpleObject> search(String queryId, Map<String, String[]> params) {
Map<String, String[]> updatedParams = conditionallyAddVisitLocation(params);
List<SimpleObject> results = new ArrayList<SimpleObject>();
SqlQueryHelper sqlQueryHelper = new SqlQueryHelper();
String query = getSql(queryId);
try(Connection conn = DatabaseUpdater.getConnection();
PreparedStatement statement = sqlQueryHelper.constructPreparedStatement(query,updatedParams,conn);
ResultSet resultSet = statement.executeQuery()) {

RowMapper rowMapper = new RowMapper();
while (resultSet.next()) {
results.add(rowMapper.mapRow(resultSet));
}
return results;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private String getSql(String queryId) {
String query = Context.getAdministrationService().getGlobalProperty(queryId);
if (query == null) throw new RuntimeException("No such query:" + queryId);
return query;
}

private Map<String, String[]> conditionallyAddVisitLocation(Map<String, String[]> params) {
Map<String, String[]> updatedParams = new HashMap<String, String[]>(params);
if (params.containsKey("location_uuid")) {
String locationUuid = params.get("location_uuid")[0];
String[] visitLocationValue = {locationUuid};
updatedParams.put("visit_location_uuid", visitLocationValue);
}
return updatedParams;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.kenyaemr.model;

public class AdditionalSearchParam {
private String additionalSearchHandler;
private String tests;

public AdditionalSearchParam(String additionalSearchHandler, String tests) {
this.additionalSearchHandler = additionalSearchHandler;
this.tests = tests;
}

public AdditionalSearchParam() {
}

public String getAdditionalSearchHandler() {
return additionalSearchHandler;
}

public void setAdditionalSearchHandler(String additionalSearchHandler) {
this.additionalSearchHandler = additionalSearchHandler;
}

public String getTests(){
return tests;
}

public void setTests(String tests){
this.tests = tests;
}
}
35 changes: 35 additions & 0 deletions api/src/main/java/org/openmrs/module/kenyaemr/util/RowMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.kenyaemr.util;

import org.openmrs.module.webservices.rest.SimpleObject;
import org.springframework.jdbc.support.JdbcUtils;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

public class RowMapper {
public SimpleObject mapRow(ResultSet rs) throws SQLException {
SimpleObject row = new SimpleObject();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for (int index = 1; index <= columnCount; index++) {
String column = JdbcUtils.lookupColumnName(rsmd, index);
Object value = rs.getObject(column);
if (value == null) {
row.put(column, "");
} else {
row.put(column, value);
}
}
return row;
}
}
143 changes: 143 additions & 0 deletions api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package org.openmrs.module.kenyaemr.util;

/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.openmrs.module.kenyaemr.model.AdditionalSearchParam;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Handler for raw SQL
*/
public class SqlQueryHelper {
private final Pattern paramPlaceHolderPattern;
private static final String PARAM_PLACE_HOLDER_REGEX = "\\$\\{[^{]*\\}";
private static final Logger log = LogManager.getLogger(SqlQueryHelper.class);

public SqlQueryHelper() {
this.paramPlaceHolderPattern = Pattern.compile(PARAM_PLACE_HOLDER_REGEX);
}

List<String> getParamNamesFromPlaceHolders(String query){
List<String> params = new ArrayList<String>();
Matcher matcher = paramPlaceHolderPattern.matcher(query);
while(matcher.find()){
params.add(stripDelimiters(matcher.group()));
}
return params;
}

private String stripDelimiters(String text) {
return text.replaceAll("[${}]", "");
}

public String transformIntoPreparedStatementFormat(String queryString){
return queryString.replaceAll(PARAM_PLACE_HOLDER_REGEX,"?");
}

public PreparedStatement constructPreparedStatement(String queryString, Map<String, String[]> params, Connection conn) throws SQLException {
String finalQueryString = queryString;
if (params.get("additionalParams") != null && params.get("additionalParams") != null) {
finalQueryString = parseAdditionalParams(params.get("additionalParams")[0], queryString);
}

List<String> paramNamesFromPlaceHolders = getParamNamesFromPlaceHolders(finalQueryString);
String statement = transformIntoPreparedStatementFormat(finalQueryString);
PreparedStatement preparedStatement = conn.prepareStatement(statement);
if(params != null && params.size() > 0 ){
int i=1;
for (String paramName : paramNamesFromPlaceHolders) {
String paramValue = params.get(paramName)[0];
preparedStatement.setObject(i++,paramValue);
}
}
return preparedStatement;
}

String parseAdditionalParams(String additionalParams, String queryString) {
String queryWithAdditionalParams = queryString;
try {
AdditionalSearchParam additionalSearchParams = new ObjectMapper().readValue(additionalParams, AdditionalSearchParam.class);
String test = additionalSearchParams.getTests();
queryWithAdditionalParams = queryString.replaceAll("\\$\\{testName\\}", test);
} catch (IOException e) {
log.error("Failed to parse Additional Search Parameters.");
e.printStackTrace();
}
return queryWithAdditionalParams;
}

public static String escapeSQL(String str, boolean escapeDoubleQuotes, Character escapeChar) {
if (StringUtils.isBlank(str)) {
return str;
}
char escChar = '\\';
if (escapeChar != null) {
escChar = escapeChar.charValue();
}
String strToCheck = str.trim().replace("0x", "0X").replace("/*", "\\/*");
StringBuilder sBuilder = new StringBuilder();
int stringLength = strToCheck.length();
for (int i = 0; i < stringLength; ++i) {
char c = strToCheck.charAt(i);
switch (c) {
case 0:
sBuilder.append(escChar);
sBuilder.append('0');
break;
case ';':
sBuilder.append(escChar);
sBuilder.append(';');
break;
case '\n': /* Must be escaped for logs */
sBuilder.append(escChar);
sBuilder.append('n');
break;
case '\r':
sBuilder.append(escChar);
sBuilder.append('r');
break;
case '\\':
sBuilder.append(escChar);
sBuilder.append('\\');
break;
case '\'':
sBuilder.append(escChar);
sBuilder.append('\'');
break;
case '"':
if (escapeDoubleQuotes) {
sBuilder.append('\\');
}
sBuilder.append('"');
break;
case '\032':
sBuilder.append(escChar);
sBuilder.append('Z');
break;
default:
sBuilder.append(c);
}
}
return sBuilder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,6 @@ public Object getAllPatientFlags(HttpServletRequest request, @RequestParam("pati

/**
* Returns custom patient object
* @param request
* @param patientUuid
* @return
*/
Expand Down Expand Up @@ -337,7 +336,6 @@ public Object getPatientIdByPatientUuid(@RequestParam("patientUuid") String pati

/**
* Returns regimen history for a patient
* @param request
* @param category // ARV or TB
* @param patientUuid
* @return
Expand Down Expand Up @@ -559,7 +557,6 @@ public Object getLastRegimenEncounterUuid(@RequestParam("category") String categ

/**
* Get a list of standard regimen
* @param request
* @param
* @return
* @throws IOException
Expand Down Expand Up @@ -672,9 +669,6 @@ public Object getStandardRegimen() throws SAXException, IOException, ParserConfi

/**
* Returns regimen change/stop reasons
* @param request
* @param category // ARV or TB
* @param patientUuid
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = "/regimenReason")
Expand Down Expand Up @@ -2663,4 +2657,18 @@ private String mapConceptNamesToShortNames(String conceptUuid) {
return name;

}

/**
*
* @param query
* @param request
* @return
* @throws Exception
*/
@RequestMapping(method = RequestMethod.GET, value = "/sql")
@ResponseBody
public List<org.openmrs.module.webservices.rest.SimpleObject> search(@RequestParam("q") String query, HttpServletRequest request) throws Exception {
return Context.getService(KenyaEmrService.class).search(query, request.getParameterMap());

}
}
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<target>1.6</target>
<source>1.6</source>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
<plugin>
Expand Down

0 comments on commit 22bba38

Please sign in to comment.