generated from kestra-io/plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
329 additions
and
18 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
plugin-jdbc-db2/src/main/java/io/kestra/plugin/jdbc/db2/Queries.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package io.kestra.plugin.jdbc.db2; | ||
|
||
import io.kestra.core.models.annotations.Example; | ||
import io.kestra.core.models.annotations.Plugin; | ||
import io.kestra.core.models.tasks.RunnableTask; | ||
import io.kestra.core.runners.RunContext; | ||
import io.kestra.plugin.jdbc.AbstractCellConverter; | ||
import io.kestra.plugin.jdbc.AbstractJdbcQueries; | ||
import io.kestra.plugin.jdbc.AbstractJdbcQuery; | ||
import io.kestra.plugin.jdbc.AutoCommitInterface; | ||
import io.micronaut.http.uri.UriBuilder; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
import lombok.experimental.SuperBuilder; | ||
|
||
import java.net.URI; | ||
import java.sql.DriverManager; | ||
import java.sql.SQLException; | ||
import java.time.ZoneId; | ||
import java.util.Properties; | ||
|
||
@SuperBuilder | ||
@ToString | ||
@EqualsAndHashCode | ||
@Getter | ||
@NoArgsConstructor | ||
@Schema( | ||
title = "Perform multiple queries on a DB2 database." | ||
) | ||
@Plugin( | ||
examples = { | ||
@Example( | ||
title = "Send a SQL query to a DB2 Database and fetch a row as output.", | ||
full = true, | ||
code = """ | ||
id: db2_query | ||
namespace: company.team | ||
tasks: | ||
- id: queries | ||
type: io.kestra.plugin.jdbc.db2.Queries | ||
url: jdbc:db2://127.0.0.1:50000/ | ||
username: db2inst | ||
password: db2_password | ||
sql: select * from employee; select * from laptop; | ||
fetchType: FETCH | ||
""" | ||
) | ||
} | ||
) | ||
public class Queries extends AbstractJdbcQueries implements RunnableTask<AbstractJdbcQueries.MultiQueryOutput> { | ||
|
||
@Override | ||
protected AbstractCellConverter getCellConverter(ZoneId zoneId) { | ||
return new Db2CellConverter(zoneId); | ||
} | ||
|
||
@Override | ||
public void registerDriver() throws SQLException { | ||
DriverManager.registerDriver(new com.ibm.db2.jcc.DB2Driver()); | ||
} | ||
|
||
@Override | ||
public Properties connectionProperties(RunContext runContext) throws Exception { | ||
return super.connectionProperties(runContext, "jdbc:db2"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
195 changes: 195 additions & 0 deletions
195
plugin-jdbc-db2/src/test/java/io/kestra/plugin/jdbc/db2/DB2QueriesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package io.kestra.plugin.jdbc.db2; | ||
|
||
import io.kestra.core.junit.annotations.KestraTest; | ||
import io.kestra.core.models.property.Property; | ||
import io.kestra.core.runners.RunContext; | ||
import io.kestra.plugin.jdbc.AbstractJdbcQueries; | ||
import io.kestra.plugin.jdbc.AbstractRdbmsTest; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Disabled; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.FileNotFoundException; | ||
import java.io.IOException; | ||
import java.net.URISyntaxException; | ||
import java.sql.SQLException; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static io.kestra.core.models.tasks.common.FetchType.FETCH; | ||
import static io.kestra.core.models.tasks.common.FetchType.FETCH_ONE; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
import static org.hamcrest.Matchers.notNullValue; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
@KestraTest | ||
@Disabled("Disabled for CI") | ||
public class DB2QueriesTest extends AbstractRdbmsTest { | ||
|
||
@Test | ||
void testMultiSelectWithParameters() throws Exception { | ||
RunContext runContext = runContextFactory.of(Collections.emptyMap()); | ||
|
||
Map<String, Object> parameters = Map.of( | ||
"age", 40, | ||
"brand", "Apple", | ||
"cpu_frequency", 1.5 | ||
); | ||
|
||
Queries taskGet = Queries.builder() | ||
.url(getUrl()) | ||
.username(getUsername()) | ||
.password(getPassword()) | ||
.fetchType(FETCH) | ||
.timeZoneId("Europe/Paris") | ||
.sql(""" | ||
SELECT firstName, lastName, age FROM employee where age > :age and age < :age + 10; | ||
SELECT brand, model FROM laptop where brand = :brand and cpu_frequency > :cpu_frequency; | ||
SELECT * FROM employee; | ||
""") | ||
.parameters(Property.of(parameters)) | ||
.build(); | ||
|
||
AbstractJdbcQueries.MultiQueryOutput runOutput = taskGet.run(runContext); | ||
assertThat(runOutput.getOutputs().size(), is(3)); | ||
|
||
List<Map<String, Object>> employees = runOutput.getOutputs().getFirst().getRows(); | ||
assertThat("employees", employees, notNullValue()); | ||
assertThat("employees", employees.size(), is(1)); | ||
assertThat("employee selected", employees.getFirst().get("AGE"), is(45)); | ||
assertThat("employee selected", employees.getFirst().get("FIRSTNAME"), is("John")); | ||
assertThat("employee selected", employees.getFirst().get("LASTNAME"), is("Doe")); | ||
|
||
List<Map<String, Object>> laptops = runOutput.getOutputs().get(1).getRows(); | ||
assertThat("laptops", laptops, notNullValue()); | ||
assertThat("laptops", laptops.size(), is(1)); | ||
assertThat("selected laptop", laptops.getFirst().get("BRAND"), is("Apple")); | ||
|
||
List<Map<String, Object>> allEmployees = runOutput.getOutputs().getLast().getRows(); | ||
assertThat("All employees", allEmployees, notNullValue()); | ||
assertThat("All employees size", allEmployees.size(), is(4)); | ||
} | ||
|
||
@Test | ||
void testRollback() throws Exception { | ||
RunContext runContext = runContextFactory.of(Collections.emptyMap()); | ||
|
||
//Queries should pass in a transaction | ||
Queries queriesPass = Queries.builder() | ||
.url(getUrl()) | ||
.username(getUsername()) | ||
.password(getPassword()) | ||
.fetchType(FETCH_ONE) | ||
.timeZoneId("Europe/Paris") | ||
.sql(""" | ||
DROP TABLE IF EXISTS DB2INST1.test_transaction; | ||
CREATE TABLE DB2INST1.test_transaction(id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name VARCHAR(230)); | ||
INSERT INTO DB2INST1.test_transaction (name) VALUES ('test_insert_1'); | ||
SELECT COUNT(id) as TRANSACTION_COUNT FROM DB2INST1.test_transaction; | ||
""") | ||
.build(); | ||
|
||
AbstractJdbcQueries.MultiQueryOutput runOutput = queriesPass.run(runContext); | ||
assertThat(runOutput.getOutputs().size(), is(1)); | ||
assertThat(runOutput.getOutputs().getFirst().getRow().get("TRANSACTION_COUNT"), is(1)); | ||
|
||
//Queries should fail due to bad sql | ||
Queries insertsFail = Queries.builder() | ||
.url(getUrl()) | ||
.username(getUsername()) | ||
.password(getPassword()) | ||
.fetchType(FETCH_ONE) | ||
.timeZoneId("Europe/Paris") | ||
.sql(""" | ||
INSERT INTO DB2INST1.test_transaction (name) VALUES ('test_insert_2'); | ||
INSERT INTO DB2INST1.test_transaction (name) VALUES (3f); | ||
""") //Try inserting before failing | ||
.build(); | ||
|
||
assertThrows(Exception.class, () -> insertsFail.run(runContext)); | ||
|
||
//Final query to verify the amount of updated rows | ||
Queries verifyQuery = Queries.builder() | ||
.url(getUrl()) | ||
.username(getUsername()) | ||
.password(getPassword()) | ||
.fetchType(FETCH_ONE) | ||
.timeZoneId("Europe/Paris") | ||
.sql(""" | ||
SELECT COUNT(id) as TRANSACTION_COUNT FROM DB2INST1.test_transaction; | ||
""") //Try inserting before failing | ||
.build(); | ||
|
||
AbstractJdbcQueries.MultiQueryOutput verifyOutput = verifyQuery.run(runContext); | ||
assertThat(verifyOutput.getOutputs().size(), is(1)); | ||
assertThat(verifyOutput.getOutputs().getFirst().getRow().get("TRANSACTION_COUNT"), is(1)); | ||
} | ||
|
||
@Test | ||
void testNonTransactionalShouldNotRollback() throws Exception { | ||
RunContext runContext = runContextFactory.of(Collections.emptyMap()); | ||
|
||
//Queries should pass in a transaction | ||
Queries insertOneAndFail = Queries.builder() | ||
.url(getUrl()) | ||
.username(getUsername()) | ||
.password(getPassword()) | ||
.fetchType(FETCH_ONE) | ||
.transaction(Property.of(false)) | ||
.timeZoneId("Europe/Paris") | ||
.sql(""" | ||
DROP TABLE IF EXISTS DB2INST1.test_transaction; | ||
CREATE TABLE DB2INST1.test_transaction(id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name VARCHAR(230)); | ||
INSERT INTO DB2INST1.test_transaction (name) VALUES ('test_insert_1'); | ||
INSERT INTO DB2INST1.test_transaction (id) VALUES (1f); | ||
INSERT INTO DB2INST1.test_transaction (id) VALUES ('test_insert_2); | ||
""") | ||
.build(); | ||
|
||
assertThrows(Exception.class, () -> insertOneAndFail.run(runContext)); | ||
|
||
//Final query to verify the amount of updated rows | ||
Queries verifyQuery = Queries.builder() | ||
.url(getUrl()) | ||
.username(getUsername()) | ||
.password(getPassword()) | ||
.fetchType(FETCH_ONE) | ||
.timeZoneId("Europe/Paris") | ||
.sql(""" | ||
SELECT COUNT(id) as TRANSACTION_COUNT FROM DB2INST1.test_transaction; | ||
""") //Try inserting before failing | ||
.build(); | ||
|
||
AbstractJdbcQueries.MultiQueryOutput verifyOutput = verifyQuery.run(runContext); | ||
assertThat(verifyOutput.getOutputs().size(), is(1)); | ||
assertThat(verifyOutput.getOutputs().getFirst().getRow().get("TRANSACTION_COUNT"), is(1)); | ||
} | ||
|
||
@Override | ||
protected String getUrl() { | ||
return "jdbc:db2://localhost:5023/testdb"; | ||
} | ||
|
||
@Override | ||
protected String getUsername() { | ||
return "db2inst1"; | ||
} | ||
|
||
@Override | ||
protected String getPassword() { | ||
return "password"; | ||
} | ||
|
||
@Override | ||
protected void initDatabase() throws SQLException, FileNotFoundException, URISyntaxException { | ||
executeSqlScript("scripts/db2_queries.sql"); | ||
} | ||
|
||
@Override | ||
@BeforeEach | ||
public void init() throws IOException, URISyntaxException, SQLException { | ||
initDatabase(); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
plugin-jdbc-db2/src/test/resources/scripts/db2_queries.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
-- Create table employee | ||
DROP TABLE IF EXISTS DB2INST1.employee; | ||
|
||
CREATE TABLE DB2INST1.employee ( | ||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | ||
firstName VARCHAR(200), | ||
lastName VARCHAR(200), | ||
age INT | ||
); | ||
|
||
INSERT INTO DB2INST1.employee (firstName, lastName, age) | ||
VALUES | ||
('John', 'Doe', 45), | ||
('Bryan', 'Grant', 33), | ||
('Jude', 'Philips', 25), | ||
('Michael', 'Page', 62); | ||
|
||
|
||
-- Create table laptop | ||
DROP TABLE IF EXISTS DB2INST1.laptop; | ||
|
||
CREATE TABLE DB2INST1.laptop | ||
( | ||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | ||
brand VARCHAR(200), | ||
model VARCHAR(200), | ||
cpu_frequency DOUBLE | ||
); | ||
|
||
INSERT INTO DB2INST1.laptop (brand, model, cpu_frequency) | ||
VALUES | ||
('Apple', 'MacBookPro M1 13', 2.2), | ||
('Apple', 'MacBookPro M3 16', 1.5), | ||
('LG', 'Gram', 1.95), | ||
('Lenovo', 'ThinkPad', 1.05); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters