Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
name: Build
on: [pull_request]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout flamingock-java (core library)
uses: actions/checkout@v4
with:
repository: flamingock/flamingock-java
path: flamingock-java
ref: develop

- name: Checkout project
uses: actions/checkout@v4
with:
path: flamingock-java-template-sql
uses: actions/checkout@v2

- name: Set up Java
uses: graalvm/setup-graalvm@v1
Expand All @@ -26,5 +16,6 @@ jobs:
github-token: ${{ secrets.FLAMINGOCK_JRELEASER_GITHUB_TOKEN }}

- name: Unit and Integration tests
working-directory: ./flamingock-java-template-sql
run: ./gradlew clean build --include-build ../flamingock-java
run: |
./gradlew clean build \
-Psql.test.dialects=mysql
12 changes: 8 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ plugins {
id("com.diffplug.spotless") version "6.25.0"
}

val flamingockVersion = "1.2.0-beta.1"

group = "io.flamingock"
version = "1.0.0-beta.1"

Expand All @@ -17,12 +19,14 @@ repositories {
}

dependencies {
implementation("io.flamingock:flamingock-core-commons:1.1.0-rc.2")
implementation("io.flamingock:sql-util:1.1.0-rc.2")
implementation("io.flamingock:flamingock-core-commons:$flamingockVersion")
implementation("io.flamingock:flamingock-template-api:$flamingockVersion")
implementation("io.flamingock:sql-util:$flamingockVersion")
implementation("org.slf4j:slf4j-api:1.7.36")

testAnnotationProcessor("io.flamingock:flamingock-processor:1.1.0-rc.2")
testImplementation("io.flamingock:flamingock-auditstore-sql:1.1.0-rc.2")
testAnnotationProcessor("io.flamingock:flamingock-processor:$flamingockVersion")
testAnnotationProcessor(files(sourceSets.main.get().output))
testImplementation("io.flamingock:flamingock-auditstore-sql:$flamingockVersion")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
testImplementation("org.mockito:mockito-inline:4.11.0")
testImplementation("com.zaxxer:HikariCP:4.0.3")
Expand Down
20 changes: 12 additions & 8 deletions src/main/java/io/flamingock/template/sql/SqlTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*/
package io.flamingock.template.sql;

import io.flamingock.api.annotations.Apply;
import io.flamingock.api.annotations.Rollback;
import io.flamingock.api.annotations.ApplyTemplate;
import io.flamingock.api.annotations.ChangeTemplate;
import io.flamingock.api.annotations.RollbackTemplate;
import io.flamingock.api.template.AbstractChangeTemplate;
import io.flamingock.api.template.wrappers.TemplateString;
import io.flamingock.internal.util.log.FlamingockLoggerFactory;
import io.flamingock.template.sql.util.SqlSplitterFactory;
import io.flamingock.template.sql.util.SqlStatement;
Expand All @@ -30,23 +32,25 @@
import java.util.Collections;
import java.util.List;

public class SqlTemplate extends AbstractChangeTemplate<SqlTemplateConfig, String, String> {
@ChangeTemplate(name = "sql-template", rollbackPayloadRequired = false)
public class SqlTemplate extends AbstractChangeTemplate<SqlTemplateConfig, TemplateString, TemplateString> {

private static final Logger logger = FlamingockLoggerFactory.getLogger(SqlTemplate.class);

public SqlTemplate() {
super();
}

@Apply
@ApplyTemplate
public void apply(Connection connection) {
execute(connection, applyPayload);
execute(connection, applyPayload.getValue());
}

@Rollback
@RollbackTemplate
public void rollback(Connection connection) {
if (rollbackPayload != null && !rollbackPayload.trim().isEmpty()) {
execute(connection, rollbackPayload);
if (rollbackPayload != null && rollbackPayload.getValue() != null
&& !rollbackPayload.getValue().trim().isEmpty()) {
execute(connection, rollbackPayload.getValue());
}
}

Expand Down
14 changes: 13 additions & 1 deletion src/main/java/io/flamingock/template/sql/SqlTemplateConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@
*/
package io.flamingock.template.sql;

import io.flamingock.api.template.TemplateField;
import io.flamingock.api.template.TemplatePayloadValidationError;
import io.flamingock.api.template.TemplateValidationContext;

import java.util.Collections;
import java.util.List;

/**
* Configuration class for SqlTemplate.
* Allows customization of SQL splitting behavior.
*/
public class SqlTemplateConfig {
public class SqlTemplateConfig implements TemplateField {

/**
* Whether to split the SQL string into multiple statements according to its dialect.
Expand All @@ -35,4 +42,9 @@ public void setSplitStatements(boolean splitStatements) {
this.splitStatements = splitStatements;
}

@Override
public List<TemplatePayloadValidationError> validate(TemplateValidationContext context) {
return Collections.emptyList();
}

}
34 changes: 21 additions & 13 deletions src/test/java/io/flamingock/template/sql/SqlTemplateConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Arrays;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.*;

class SqlTemplateConfigTest {

Expand Down Expand Up @@ -81,23 +82,30 @@ static void tearDownAll() {
}

@Test
@DisplayName("WHEN run with splitStatements=false and multiple statements THEN should fail")
void noSplitting() {
@DisplayName("WHEN run with splitStatements=false and multiple statements THEN succeeds (H2 supports multi-statement execute)")
void noSplitting() throws Exception {
try (MockedStatic<Deserializer> mocked = Mockito.mockStatic(Deserializer.class)) {

SqlTemplateConfig config = new SqlTemplateConfig();
config.setSplitStatements(false);

mocked.when(Deserializer::readMetadataFromFile).thenReturn(new FlamingockMetadata(createPipeline(config), null, null));

assertThrows(RuntimeException.class, () -> {
SqlTargetSystem sqlTargetSystem = new SqlTargetSystem("sql", dataSource);
FlamingockFactory.getCommunityBuilder()
.setAuditStore(SqlAuditStore.from(sqlTargetSystem))
.addTargetSystem(sqlTargetSystem)
.build()
.run();
});
SqlTargetSystem sqlTargetSystem = new SqlTargetSystem("sql", dataSource);
FlamingockFactory.getCommunityBuilder()
.setAuditStore(SqlAuditStore.from(sqlTargetSystem))
.addTargetSystem(sqlTargetSystem)
.build()
.run();

// Verify table was created and data inserted (H2 2.x supports multi-statement execute())
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + TEST_TABLE)) {

assertTrue(rs.next());
assertEquals(3, rs.getInt(1));
}
}
}

Expand All @@ -108,7 +116,7 @@ private PreviewPipeline createPipeline(SqlTemplateConfig config) {
"create-test-users-table",
"0001",
"test-author",
"SqlTemplate",
"sql-template",
Collections.emptyList(),
true,
false,
Expand All @@ -126,7 +134,7 @@ private PreviewPipeline createPipeline(SqlTemplateConfig config) {
"insert-test-users",
"0002",
"test-author",
"SqlTemplate",
"sql-template",
Collections.emptyList(),
true,
false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2026 Flamingock (https://www.flamingock.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.flamingock.template.sql;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.flamingock.api.annotations.EnableFlamingock;
import io.flamingock.internal.core.builder.FlamingockFactory;
import io.flamingock.store.sql.SqlAuditStore;
import io.flamingock.targetsystem.sql.SqlTargetSystem;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@EnableFlamingock(configFile = "flamingock/pipeline.yaml")
class SqlTemplateIntegrationTest {

private static final String TEST_TABLE = "sql_template_test";

private static DataSource dataSource;

@BeforeAll
static void beforeAll() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:h2:mem:integrationdb;DB_CLOSE_DELAY=-1");
config.setUsername("sa");
config.setPassword("");
config.setDriverClassName("org.h2.Driver");

dataSource = new HikariDataSource(config);
}

@AfterEach
void tearDown() {
if (dataSource != null) {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement()) {
stmt.execute("DROP TABLE IF EXISTS " + TEST_TABLE);
stmt.execute("DROP TABLE IF EXISTS flamingockAuditLog");
stmt.execute("DROP TABLE IF EXISTS flamingockLock");
} catch (Exception e) {
// Ignore cleanup errors
}
}
}

@AfterAll
static void tearDownAll() {
if (dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close();
}
}

@Test
@DisplayName("WHEN running YAML-based SQL template changes THEN all changes are applied")
void happyPath() throws Exception {
SqlTargetSystem sqlTargetSystem = new SqlTargetSystem("sql", dataSource);
FlamingockFactory.getCommunityBuilder()
.setAuditStore(SqlAuditStore.from(sqlTargetSystem))
.addTargetSystem(sqlTargetSystem)
.build()
.run();

try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + TEST_TABLE)) {

assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
}

try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM " + TEST_TABLE + " ORDER BY id")) {

assertTrue(rs.next());
assertEquals("Admin", rs.getString(1));
assertTrue(rs.next());
assertEquals("Backup", rs.getString(1));
}
}
}
4 changes: 2 additions & 2 deletions src/test/java/io/flamingock/template/sql/SqlTemplateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ private PreviewPipeline createPipeline(Boolean apply, Boolean rollback) {
"create-test-users-table",
"0001",
"test-author",
"SqlTemplate",
"sql-template",
Collections.emptyList(),
true,
false,
Expand All @@ -215,7 +215,7 @@ private PreviewPipeline createPipeline(Boolean apply, Boolean rollback) {
"insert-test-users",
"0002",
"test-author",
"SqlTemplate",
"sql-template",
Collections.emptyList(),
true,
false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
id: create-test-table
transactional: true
template: sql-template
targetSystem:
id: "sql"
apply: "CREATE TABLE sql_template_test (id INT PRIMARY KEY, name VARCHAR(100), role VARCHAR(50))"
rollback: "DROP TABLE sql_template_test"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
id: insert-test-data
transactional: true
template: sql-template
targetSystem:
id: "sql"
apply: "INSERT INTO sql_template_test (id, name, role) VALUES (1, 'Admin', 'superuser'); INSERT INTO sql_template_test (id, name, role) VALUES (2, 'Backup', 'readonly')"
rollback: "DELETE FROM sql_template_test WHERE id IN (1, 2)"
4 changes: 4 additions & 0 deletions src/test/resources/flamingock/pipeline.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pipeline:
stages:
- description: "SQL template integration changes"
location: "io.flamingock.template.sql.changes"
Loading