From bc9c7b92974c536dd423804d8d696be3c61b0ea1 Mon Sep 17 00:00:00 2001 From: ojwanganto Date: Mon, 22 Jan 2024 23:46:19 +0300 Subject: [PATCH 1/2] adapt endpoint for running and returning sql results --- .../module/kenyaemr/api/KenyaEmrService.java | 12 ++ .../api/impl/KenyaEmrServiceImpl.java | 55 ++++++- .../kenyaemr/model/AdditionalSearchParam.java | 39 +++++ .../module/kenyaemr/util/RowMapper.java | 35 +++++ .../module/kenyaemr/util/SqlQueryHelper.java | 140 ++++++++++++++++++ .../KenyaemrCoreRestController.java | 20 ++- 6 files changed, 290 insertions(+), 11 deletions(-) create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/model/AdditionalSearchParam.java create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/util/RowMapper.java create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java 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..3be5083323 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,46 @@ 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 visitLocation = ""; + String[] visitLcoationValue = {visitLocation}; + updatedParams.put("visit_location_uuid", visitLcoationValue); + } + 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..bec4a635ba --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java @@ -0,0 +1,140 @@ +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; + +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 ){ + 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 72779332d3..816841e9ff 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 @@ -314,7 +314,6 @@ public Object getAllPatientFlags(HttpServletRequest request, @RequestParam("pati /** * Returns custom patient object - * @param request * @param patientUuid * @return */ @@ -346,7 +345,6 @@ public Object getPatientIdByPatientUuid(@RequestParam("patientUuid") String pati /** * Returns regimen history for a patient - * @param request * @param category // ARV or TB * @param patientUuid * @return @@ -568,7 +566,6 @@ public Object getLastRegimenEncounterUuid(@RequestParam("category") String categ /** * Get a list of standard regimen - * @param request * @param * @return * @throws IOException @@ -681,9 +678,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") @@ -2672,4 +2666,18 @@ private String mapConceptNamesToShortNames(String conceptUuid) { return name; } + + /** + * + * @param query + * @param request + * @return + * @throws Exception + */ + @RequestMapping(method = RequestMethod.GET, value = "/sql") // gets all flags for a patient + @ResponseBody + public List search(@RequestParam("q") String query, HttpServletRequest request) throws Exception { + return Context.getService(KenyaEmrService.class).search(query, request.getParameterMap()); + + } } From cab6260867def885595a6096c82b7a6fa6b2c650 Mon Sep 17 00:00:00 2001 From: ojwanganto Date: Wed, 13 Mar 2024 13:08:05 +0300 Subject: [PATCH 2/2] Add sql rest endpoint --- .../module/kenyaemr/api/impl/KenyaEmrServiceImpl.java | 5 ++--- .../org/openmrs/module/kenyaemr/util/SqlQueryHelper.java | 7 +++++-- .../web/controller/KenyaemrCoreRestController.java | 2 +- pom.xml | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) 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 3be5083323..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 @@ -297,9 +297,8 @@ private Map conditionallyAddVisitLocation(Map updatedParams = new HashMap(params); if (params.containsKey("location_uuid")) { String locationUuid = params.get("location_uuid")[0]; - String visitLocation = ""; - String[] visitLcoationValue = {visitLocation}; - updatedParams.put("visit_location_uuid", visitLcoationValue); + String[] visitLocationValue = {locationUuid}; + updatedParams.put("visit_location_uuid", visitLocationValue); } return updatedParams; } 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 index bec4a635ba..b45c1b6346 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/util/SqlQueryHelper.java @@ -25,6 +25,9 @@ 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 = "\\$\\{[^{]*\\}"; @@ -35,7 +38,7 @@ public SqlQueryHelper() { } List getParamNamesFromPlaceHolders(String query){ - List params = new ArrayList<>(); + List params = new ArrayList(); Matcher matcher = paramPlaceHolderPattern.matcher(query); while(matcher.find()){ params.add(stripDelimiters(matcher.group())); @@ -60,7 +63,7 @@ public PreparedStatement constructPreparedStatement(String queryString, Map paramNamesFromPlaceHolders = getParamNamesFromPlaceHolders(finalQueryString); String statement = transformIntoPreparedStatementFormat(finalQueryString); PreparedStatement preparedStatement = conn.prepareStatement(statement); - if(params != null ){ + if(params != null && params.size() > 0 ){ int i=1; for (String paramName : paramNamesFromPlaceHolders) { String paramValue = params.get(paramName)[0]; 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 816841e9ff..6a9a1119c1 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 @@ -2674,7 +2674,7 @@ private String mapConceptNamesToShortNames(String conceptUuid) { * @return * @throws Exception */ - @RequestMapping(method = RequestMethod.GET, value = "/sql") // gets all flags for a patient + @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 3a40ed45ae..f4fdd70b9e 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