From c41e5db99d0c00de7525ed6528ff1a71e3e1d7c1 Mon Sep 17 00:00:00 2001 From: grabdoc Date: Wed, 13 Nov 2024 23:26:30 -0600 Subject: [PATCH] #710 - add first oracle stored procedure test --- api-rest/src/main/resources/application.yml | 3 +- .../oracle/OracleProcedureControllerTest.java | 44 +++++++++++++++++++ .../test/resources/oracle/oracle-sakila.sql | 38 +++++++++++++++- .../core/service/JdbcProcedureService.java | 28 +++++++++--- .../jdbc/core/service/ProcedureService.java | 4 +- 5 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 api-rest/src/test/java/com/homihq/db2rest/rest/oracle/OracleProcedureControllerTest.java diff --git a/api-rest/src/main/resources/application.yml b/api-rest/src/main/resources/application.yml index d2a30e2d..dcf100f3 100644 --- a/api-rest/src/main/resources/application.yml +++ b/api-rest/src/main/resources/application.yml @@ -75,7 +75,8 @@ logging: springframework: web: INFO beans: INFO - jdbc: INFO + jdbc: DEBUG + management: endpoints: diff --git a/api-rest/src/test/java/com/homihq/db2rest/rest/oracle/OracleProcedureControllerTest.java b/api-rest/src/test/java/com/homihq/db2rest/rest/oracle/OracleProcedureControllerTest.java new file mode 100644 index 00000000..f98128b0 --- /dev/null +++ b/api-rest/src/test/java/com/homihq/db2rest/rest/oracle/OracleProcedureControllerTest.java @@ -0,0 +1,44 @@ +package com.homihq.db2rest.rest.oracle; + +import com.homihq.db2rest.MySQLBaseIntegrationTest; +import com.homihq.db2rest.OracleBaseIntegrationTest; +import org.junit.jupiter.api.*; +import org.springframework.http.MediaType; + +import java.util.Map; + +import static com.homihq.db2rest.jdbc.rest.RdbmsRestApi.VERSION; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.Matchers.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@Order(250) +class OracleProcedureControllerTest extends OracleBaseIntegrationTest { + + @Disabled + @Test + @DisplayName("Execute stored procedure on oracle db") + void execute() throws Exception { + var json = """ + { + "movieTitle": "ACADEMY DINOSAUR" + } + """; + + mockMvc.perform(post(VERSION + "/oradb/procedure/GetMovieRentalRateProc") + .characterEncoding(UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", instanceOf(Map.class))) + .andExpect(jsonPath("$.*", hasSize(2))) + .andExpect(jsonPath("$.rentalRate", equalTo(0.99))) + //.andDo(print()) + .andDo(document("oracle-execute-procedure")); + } +} diff --git a/api-rest/src/test/resources/oracle/oracle-sakila.sql b/api-rest/src/test/resources/oracle/oracle-sakila.sql index 84fabb2d..db4698bc 100644 --- a/api-rest/src/test/resources/oracle/oracle-sakila.sql +++ b/api-rest/src/test/resources/oracle/oracle-sakila.sql @@ -743,4 +743,40 @@ ALTER TABLE inventory ADD CONSTRAINT fk_inventory_store FOREIGN KEY (store_id) R ALTER TABLE staff ADD CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id); -ALTER TABLE payment ADD CONSTRAINT fk_payment_rental FOREIGN KEY (rental_id) REFERENCES rental (rental_id) ON DELETE SET NULL; \ No newline at end of file +ALTER TABLE payment ADD CONSTRAINT fk_payment_rental FOREIGN KEY (rental_id) REFERENCES rental (rental_id) ON DELETE SET NULL; + + + +CREATE OR REPLACE PROCEDURE GetMovieRentalRateProc ( + movieTitle IN VARCHAR2, + p_rental_rate OUT NUMBER +) IS + /* + * Purpose: Retrieves the rental rate for a given movie title + * Parameters: + * p_movie_title - Input parameter for the movie title + * p_rental_rate - Output parameter for the rental rate + * Returns: Rental rate via OUT parameter, -1 if movie not found + */ +BEGIN + -- Input parameter validation + IF movieTitle IS NULL THEN + RAISE_APPLICATION_ERROR(-20001, 'Movie title cannot be null'); +END IF; + + -- Get rental rate for the movie +SELECT rental_rate +INTO p_rental_rate +FROM film +WHERE UPPER(title) = UPPER(movieTitle); + +EXCEPTION + WHEN NO_DATA_FOUND THEN + p_rental_rate := -1; +WHEN OTHERS THEN + -- Log error and re-raise + p_rental_rate := NULL; + RAISE_APPLICATION_ERROR(-20002, + 'Error getting rental rate: ' || SQLERRM); +END GetMovieRentalRateProc; +/ diff --git a/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/JdbcProcedureService.java b/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/JdbcProcedureService.java index 928c3e5d..0d9932af 100644 --- a/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/JdbcProcedureService.java +++ b/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/JdbcProcedureService.java @@ -1,10 +1,15 @@ package com.homihq.db2rest.jdbc.core.service; +import com.homihq.db2rest.core.exception.RpcException; import com.homihq.db2rest.jdbc.JdbcManager; +import com.homihq.db2rest.jdbc.config.dialect.Dialect; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcCall; import java.util.Map; @@ -17,14 +22,25 @@ public class JdbcProcedureService implements ProcedureService { private final JdbcManager jdbcManager; @Override - public SimpleJdbcCall getSimpleJdbcCall(String dbId,String subRoutineName) { + public Map execute(String dbId, String subRoutineName, Map inParams) { JdbcTemplate jdbcTemplate = jdbcManager.getNamedParameterJdbcTemplate(dbId).getJdbcTemplate(); - return new SimpleJdbcCall(jdbcTemplate).withProcedureName(subRoutineName); + Dialect dialect = jdbcManager.getDialect(dbId); + + log.info("Dialect selected: {}", dialect); + log.info("inParams: {}", inParams); + + return doExecuteInternal(jdbcTemplate, subRoutineName, inParams); } - @Override - public Map execute(String dbId, String subRoutineName, Map inParams) { - JdbcTemplate jdbcTemplate = jdbcManager.getNamedParameterJdbcTemplate(dbId).getJdbcTemplate(); - return doExecute(jdbcTemplate, dbId, subRoutineName, inParams); + private Map doExecuteInternal(JdbcTemplate jdbcTemplate, + String subRoutineName, Map inParams) { + jdbcTemplate.setResultsMapCaseInsensitive(true); + SqlParameterSource in = new MapSqlParameterSource().addValues(inParams); + + try { + return new SimpleJdbcCall(jdbcTemplate).withProcedureName(subRoutineName).execute(inParams); + } catch (InvalidDataAccessApiUsageException ex) { + throw new RpcException(subRoutineName, inParams); + } } } diff --git a/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/ProcedureService.java b/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/ProcedureService.java index efe9051a..3152c758 100644 --- a/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/ProcedureService.java +++ b/rdbms-support/src/main/java/com/homihq/db2rest/jdbc/core/service/ProcedureService.java @@ -1,11 +1,9 @@ package com.homihq.db2rest.jdbc.core.service; -import org.springframework.jdbc.core.simple.SimpleJdbcCall; import java.util.Map; -public interface ProcedureService extends SubRoutine { - SimpleJdbcCall getSimpleJdbcCall(String dbId, String subRoutineName); +public interface ProcedureService { Map execute(String dbId, String subRoutineName, Map inParams); }