Skip to content

Commit d08d5d8

Browse files
Merge pull request #36 from reshmabidikar/run-flyway
Support to automatically install database tables via flyway
2 parents 234d3b7 + 601a769 commit d08d5d8

File tree

9 files changed

+215
-9
lines changed

9 files changed

+215
-9
lines changed

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ A full end-to-end integration demo is available [here](https://github.com/killbi
1616
## Requirements
1717

1818
* An active Braintree account is required for using the plugin. A Braintree sandbox account may be used for testing purposes.
19-
* The plugin needs a database. The latest version of the schema can be found [here](https://github.com/killbill/killbill-braintree/tree/master/src/main/resources).
19+
* The plugin needs a database. The database tables are automatically created and updated at plugin startup by default. Alternatively, if you would like to manage the database schema manually, you can disable automatic migrations and use the SQL scripts provided in the (https://github.com/killbill/killbill-braintree/tree/master/src/main/resources/migration)[src/main/resources/migration] directory to create or update the database tables as needed. See the [Configuration](#configuration) section below for details about disabling automatic migrations.
20+
2021

2122
## Build
2223

@@ -73,6 +74,23 @@ Some important notes:
7374
* The plugin attempts to load the credentials either from the per-tenant configuration or the Kill Bill properties file while the unit tests require the properties to be set as environment variables.
7475
* In order to facilitate automated testing, you should disable all fraud detection within your sandbox account. These can generate gateway rejection errors when processing multiple test transactions. In particular make sure to disable [Duplicate Transaction Checking](https://articles.braintreepayments.com/control-panel/transactions/duplicate-checking#configuring-duplicate-transaction-checking).
7576

77+
In addition, the `org.killbill.billing.plugin.braintree.runMigrations` property can be used to control Flyway DB migrations at plugin startup. This property eliminates the need to manually install or update the database tables, as the plugin will handle schema setup automatically.
78+
79+
Default value:
80+
81+
```properties
82+
org.killbill.billing.plugin.braintree.runMigrations=true
83+
```
84+
85+
To skip automatic migrations (for example, if you prefer to manage the database schema manually), set:
86+
87+
```properties
88+
org.killbill.billing.plugin.braintree.runMigrations=false
89+
```
90+
91+
92+
When `org.killbill.billing.plugin.braintree.runMigrations` is disabled, ensure that the required database tables are created manually using the SQL scripts provided in the (https://github.com/killbill/killbill-braintree/tree/master/src/main/resources/migration)[src/main/resources/migration] directory.
93+
7694
## Testing
7795

7896
1. Ensure that the plugin is installed and configured as explained above.
@@ -223,6 +241,3 @@ curl -v \
223241
-H "X-Killbill-Comment: demo" \
224242
"http://127.0.0.1:8080/1.0/kb/accounts/<ACCOUNT_ID>/paymentMethods/refresh"
225243
```
226-
227-
228-

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@
112112
<artifactId>org.apache.felix.framework</artifactId>
113113
<scope>provided</scope>
114114
</dependency>
115+
<dependency>
116+
<groupId>org.flywaydb</groupId>
117+
<artifactId>flyway-core</artifactId>
118+
<version>7.7.3</version>
119+
</dependency>
115120
<dependency>
116121
<groupId>org.jooby</groupId>
117122
<artifactId>jooby</artifactId>

src/main/java/org/killbill/billing/plugin/braintree/core/BraintreeActivator.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
package org.killbill.billing.plugin.braintree.core;
1818

19+
import java.sql.SQLException;
1920
import java.util.Hashtable;
2021

2122
import javax.servlet.Servlet;
2223
import javax.servlet.http.HttpServlet;
2324

25+
import org.flywaydb.core.Flyway;
2426
import org.killbill.billing.osgi.api.Healthcheck;
2527
import org.killbill.billing.osgi.api.OSGIPluginProperties;
2628
import org.killbill.billing.osgi.libs.killbill.KillbillActivatorBase;
@@ -33,6 +35,8 @@
3335
import org.killbill.billing.plugin.core.config.PluginEnvironmentConfig;
3436
import org.killbill.billing.plugin.core.resources.jooby.PluginApp;
3537
import org.killbill.billing.plugin.core.resources.jooby.PluginAppBuilder;
38+
import org.killbill.billing.plugin.dao.PluginDao;
39+
import org.killbill.billing.plugin.dao.PluginDao.DBEngine;
3640
import org.osgi.framework.BundleContext;
3741
import org.slf4j.Logger;
3842
import org.slf4j.LoggerFactory;
@@ -49,6 +53,7 @@ public void start(final BundleContext context) throws Exception {
4953
super.start(context);
5054
final String region = PluginEnvironmentConfig.getRegion(configProperties.getProperties());
5155

56+
runMigrationsIfEnabled();
5257

5358
// Register an event listener for plugin configuration
5459
braintreeConfigurationHandler = new BraintreeConfigPropertiesConfigurationHandler(region, PLUGIN_NAME, killbillAPI);
@@ -108,4 +113,41 @@ private void registerHealthcheck(final BundleContext context, final Healthcheck
108113
props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, PLUGIN_NAME);
109114
registrar.registerService(context, Healthcheck.class, healthcheck, props);
110115
}
116+
117+
private void runMigrationsIfEnabled() {
118+
// Run Flyway migrations to create/update database tables
119+
if (BraintreeConfigProperties.shouldRunMigrations(configProperties.getProperties())) {
120+
DBEngine dbEngine;
121+
try {
122+
dbEngine = PluginDao.getDBEngine(dataSource.getDataSource());
123+
} catch (final SQLException e) {
124+
logger.warn("Unable to determine database engine, defaulting to MySQL migrations", e);
125+
dbEngine = DBEngine.MYSQL;
126+
}
127+
128+
final String locations;
129+
switch (dbEngine) {
130+
case POSTGRESQL:
131+
locations = "classpath:migration/postgresql";
132+
break;
133+
case GENERIC:
134+
case H2:
135+
case MYSQL:
136+
default:
137+
// H2 and GENERIC use MySQL-compatible migration scripts
138+
locations = "classpath:migration/mysql";
139+
break;
140+
}
141+
142+
final Flyway flyway = Flyway.configure(getClass().getClassLoader())
143+
.dataSource(dataSource.getDataSource())
144+
.locations(locations)
145+
.table("braintree_schema_history")
146+
.baselineOnMigrate(true)
147+
.load();
148+
flyway.migrate();
149+
} else {
150+
logger.info("Skipping Flyway migrations as 'runMigrations' is set to false");
151+
}
152+
}
111153
}

src/main/java/org/killbill/billing/plugin/braintree/core/BraintreeConfigProperties.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public class BraintreeConfigProperties {
4242
private static final String KEY_VALUE_DELIMITER = "#";
4343
private static final String DEFAULT_CONNECTION_TIMEOUT = "30000";
4444
private static final String DEFAULT_READ_TIMEOUT = "60000";
45-
45+
private static final String DEFAULT_RUN_MIGRATIONS = "true";
46+
4647
private final String region;
4748
private final String btEnvironment;
4849
private final String btMerchantId;
@@ -54,7 +55,8 @@ public class BraintreeConfigProperties {
5455
private final Map<String, Period> paymentMethodToExpirationPeriod = new LinkedHashMap<String, Period>();
5556
private final String chargeDescription;
5657
private final String chargeStatementDescriptor;
57-
58+
private final boolean runMigrations;
59+
5860
public BraintreeConfigProperties(final Properties properties, final String region) {
5961
this.region = region;
6062
this.btEnvironment = properties.getProperty(PROPERTY_PREFIX + "btEnvironment", "sandbox");
@@ -66,6 +68,7 @@ public BraintreeConfigProperties(final Properties properties, final String regio
6668
this.pendingPaymentExpirationPeriod = readPendingExpirationProperty(properties);
6769
this.chargeDescription = Ascii.truncate(MoreObjects.firstNonNull(properties.getProperty(PROPERTY_PREFIX + "chargeDescription"), "Kill Bill charge"), 22, "...");
6870
this.chargeStatementDescriptor = Ascii.truncate(MoreObjects.firstNonNull(properties.getProperty(PROPERTY_PREFIX + "chargeStatementDescriptor"), "Kill Bill charge"), 22, "...");
71+
this.runMigrations = Boolean.parseBoolean(properties.getProperty(PROPERTY_PREFIX + "runMigrations", DEFAULT_RUN_MIGRATIONS));
6972
}
7073

7174
public String getRegion() {
@@ -116,6 +119,14 @@ public String getChargeStatementDescriptor() {
116119
return chargeStatementDescriptor;
117120
}
118121

122+
public boolean isRunMigrations() {
123+
return runMigrations;
124+
}
125+
126+
public static boolean shouldRunMigrations(final Properties properties) {
127+
return Boolean.parseBoolean(properties.getProperty(PROPERTY_PREFIX + "runMigrations", DEFAULT_RUN_MIGRATIONS));
128+
}
129+
119130
public Period getPendingPaymentExpirationPeriod(@Nullable final String paymentMethod) {
120131
if (paymentMethod != null && paymentMethodToExpirationPeriod.get(paymentMethod.toLowerCase()) != null) {
121132
return paymentMethodToExpirationPeriod.get(paymentMethod.toLowerCase());

src/main/resources/ddl-postgresql.sql

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
*/
1717

1818
/* We cannot use timestamp in MySQL because of the implicit TimeZone conversions it does behind the scenes */
19-
CREATE DOMAIN datetime AS timestamp without time zone;
19+
DO $$ BEGIN
20+
CREATE DOMAIN datetime AS timestamp without time zone;
21+
EXCEPTION WHEN duplicate_object THEN NULL;
22+
END $$;
2023

21-
CREATE DOMAIN longtext AS text;
24+
DO $$ BEGIN
25+
CREATE DOMAIN longtext AS text;
26+
EXCEPTION WHEN duplicate_object THEN NULL;
27+
END $$;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2021-2026 The Billing Project, LLC
3+
*
4+
* Wovenware licenses this file to you under the Apache License, version 2.0
5+
* (the "License"); you may not use this file except in compliance with the
6+
* License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
create table braintree_responses (
18+
record_id serial
19+
, kb_account_id char(36) not null
20+
, kb_payment_id char(36) not null
21+
, kb_payment_transaction_id char(36) not null
22+
, transaction_type varchar(32) not null
23+
, amount numeric(15,9)
24+
, currency char(3)
25+
, braintree_id varchar(255) not null
26+
, additional_data longtext default null
27+
, created_date datetime not null
28+
, kb_tenant_id char(36) not null
29+
, primary key(record_id)
30+
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
31+
create index braintree_responses_kb_payment_id on braintree_responses(kb_payment_id);
32+
create index braintree_responses_kb_payment_transaction_id on braintree_responses(kb_payment_transaction_id);
33+
create index braintree_responses_braintree_id on braintree_responses(braintree_id);
34+
35+
create table braintree_payment_methods (
36+
record_id serial
37+
, kb_account_id char(36) not null
38+
, kb_payment_method_id char(36) not null
39+
, braintree_id varchar(255) not null
40+
, is_deleted smallint not null default 0
41+
, additional_data longtext default null
42+
, created_date datetime not null
43+
, updated_date datetime not null
44+
, kb_tenant_id char(36) not null
45+
, primary key(record_id)
46+
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
47+
create unique index braintree_payment_methods_kb_payment_id on braintree_payment_methods(kb_payment_method_id);
48+
create index braintree_payment_methods_braintree_id on braintree_payment_methods(braintree_id);

src/main/resources/migration/V20200127071214__add_is_default_to_braintree_payment_methods.sql renamed to src/main/resources/migration/mysql/V20200127071214__add_is_default_to_braintree_payment_methods.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright 2020-2020 Equinix, Inc
3-
* Copyright 2014-2020 The Billing Project, LLC
3+
* Copyright 2014-2026 The Billing Project, LLC
44
*
55
* The Billing Project licenses this file to you under the Apache License, version 2.0
66
* (the "License"); you may not use this file except in compliance with the
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2020-2020 Equinix, Inc
3+
* Copyright 2014-2026 The Billing Project, LLC
4+
*
5+
* The Billing Project licenses this file to you under the Apache License, version 2.0
6+
* (the "License"); you may not use this file except in compliance with the
7+
* License. You may obtain a copy of the License at:
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*/
17+
18+
/* We cannot use timestamp in MySQL because of the implicit TimeZone conversions it does behind the scenes */
19+
DO $$ BEGIN
20+
CREATE DOMAIN datetime AS timestamp without time zone;
21+
EXCEPTION WHEN duplicate_object THEN NULL;
22+
END $$;
23+
24+
DO $$ BEGIN
25+
CREATE DOMAIN longtext AS text;
26+
EXCEPTION WHEN duplicate_object THEN NULL;
27+
END $$;
28+
29+
create table braintree_responses (
30+
record_id serial
31+
, kb_account_id char(36) not null
32+
, kb_payment_id char(36) not null
33+
, kb_payment_transaction_id char(36) not null
34+
, transaction_type varchar(32) not null
35+
, amount numeric(15,9)
36+
, currency char(3)
37+
, braintree_id varchar(255) not null
38+
, additional_data longtext default null
39+
, created_date datetime not null
40+
, kb_tenant_id char(36) not null
41+
, primary key(record_id)
42+
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
43+
create index braintree_responses_kb_payment_id on braintree_responses(kb_payment_id);
44+
create index braintree_responses_kb_payment_transaction_id on braintree_responses(kb_payment_transaction_id);
45+
create index braintree_responses_braintree_id on braintree_responses(braintree_id);
46+
47+
create table braintree_payment_methods (
48+
record_id serial
49+
, kb_account_id char(36) not null
50+
, kb_payment_method_id char(36) not null
51+
, braintree_id varchar(255) not null
52+
, is_deleted smallint not null default 0
53+
, additional_data longtext default null
54+
, created_date datetime not null
55+
, updated_date datetime not null
56+
, kb_tenant_id char(36) not null
57+
, primary key(record_id)
58+
) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
59+
create unique index braintree_payment_methods_kb_payment_id on braintree_payment_methods(kb_payment_method_id);
60+
create index braintree_payment_methods_braintree_id on braintree_payment_methods(braintree_id);
61+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright 2020-2020 Equinix, Inc
3+
* Copyright 2014-2026 The Billing Project, LLC
4+
*
5+
* The Billing Project licenses this file to you under the Apache License, version 2.0
6+
* (the "License"); you may not use this file except in compliance with the
7+
* License. You may obtain a copy of the License at:
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*/
17+
18+
ALTER TABLE braintree_payment_methods ADD COLUMN is_default SMALLINT NOT NULL DEFAULT 0;

0 commit comments

Comments
 (0)