diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/api/KenyaEmrService.java b/api/src/main/java/org/openmrs/module/kenyaemr/api/KenyaEmrService.java index b5e21f9aad..ee5d63f53b 100755 --- a/api/src/main/java/org/openmrs/module/kenyaemr/api/KenyaEmrService.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/api/KenyaEmrService.java @@ -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; @@ -95,4 +97,14 @@ public interface KenyaEmrService extends OpenmrsService { public List executeSqlQuery(String query, Map substitutions); public List executeHqlQuery(String query, Map substitutions); + + /** + * Executes a sql query with params. + * Adapted from Bahmni core + * @param sqlQuery + * @param params + * @return + */ + @Authorized + public List search(String sqlQuery, Map params); } \ No newline at end of file diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/api/impl/KenyaEmrServiceImpl.java b/api/src/main/java/org/openmrs/module/kenyaemr/api/impl/KenyaEmrServiceImpl.java index f6678d753b..c24db10d81 100755 --- a/api/src/main/java/org/openmrs/module/kenyaemr/api/impl/KenyaEmrServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/api/impl/KenyaEmrServiceImpl.java @@ -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 @@ -258,4 +261,45 @@ public List executeSqlQuery(String query, Map substituti public List executeHqlQuery(String query, Map substitutions) { return dao.executeHqlQuery(query, substitutions); } + + /** + * @param queryId + * @param params + * @return + */ + @Override + public List search(String queryId, Map params) { + Map updatedParams = conditionallyAddVisitLocation(params); + List results = new ArrayList(); + 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 conditionallyAddVisitLocation(Map params) { + Map updatedParams = new HashMap(params); + if (params.containsKey("location_uuid")) { + String locationUuid = params.get("location_uuid")[0]; + String[] visitLocationValue = {locationUuid}; + updatedParams.put("visit_location_uuid", visitLocationValue); + } + return updatedParams; + } } \ No newline at end of file diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/model/AdditionalSearchParam.java b/api/src/main/java/org/openmrs/module/kenyaemr/model/AdditionalSearchParam.java new file mode 100644 index 0000000000..7084f62b39 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/model/AdditionalSearchParam.java @@ -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; + } +} diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/util/RowMapper.java b/api/src/main/java/org/openmrs/module/kenyaemr/util/RowMapper.java new file mode 100644 index 0000000000..3571df14cc --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/util/RowMapper.java @@ -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; + } +} diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java b/api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java new file mode 100644 index 0000000000..b45c1b6346 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java @@ -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 getParamNamesFromPlaceHolders(String query){ + List params = new ArrayList(); + 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 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 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(); + } +} diff --git a/omod/src/main/java/org/openmrs/module/kenyaemr/web/controller/KenyaemrCoreRestController.java b/omod/src/main/java/org/openmrs/module/kenyaemr/web/controller/KenyaemrCoreRestController.java index ac2c361c8e..7e77052cf7 100644 --- a/omod/src/main/java/org/openmrs/module/kenyaemr/web/controller/KenyaemrCoreRestController.java +++ b/omod/src/main/java/org/openmrs/module/kenyaemr/web/controller/KenyaemrCoreRestController.java @@ -305,7 +305,6 @@ public Object getAllPatientFlags(HttpServletRequest request, @RequestParam("pati /** * Returns custom patient object - * @param request * @param patientUuid * @return */ @@ -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 @@ -559,7 +557,6 @@ public Object getLastRegimenEncounterUuid(@RequestParam("category") String categ /** * Get a list of standard regimen - * @param request * @param * @return * @throws IOException @@ -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") @@ -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 search(@RequestParam("q") String query, HttpServletRequest request) throws Exception { + return Context.getService(KenyaEmrService.class).search(query, request.getParameterMap()); + + } } diff --git a/pom.xml b/pom.xml index dad540a9be..fcdc55305a 100755 --- a/pom.xml +++ b/pom.xml @@ -381,8 +381,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.6 - 1.6 + 1.8 + 1.8