killbill-uncached
Changes
account/pom.xml 2(+1 -1)
account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java 66(+35 -31)
analytics/pom.xml 2(+1 -1)
analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java 22(+8 -14)
api/pom.xml 2(+1 -1)
api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java 73(+73 -0)
api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java 38(+38 -0)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java 209(+209 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java 80(+80 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java 188(+188 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java 20(+11 -9)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java 126(+70 -56)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg 6(+6 -0)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg 6(+6 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java 371(+371 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java 55(+55 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java 56(+56 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java 12(+8 -4)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java 42(+27 -15)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java 19(+12 -7)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java 18(+12 -6)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java 11(+6 -5)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java 15(+10 -5)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java 62(+61 -1)
invoice/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index 01b61f9..2107a34 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index 1c7a397..b953707 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -133,7 +133,9 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
@Override
public void addTags(List<Tag> tags) {
- this.tags.add(tags);
+ if (tags != null) {
+ this.tags.add(tags);
+ }
}
@Override
@@ -160,4 +162,4 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
public BigDecimal getBalance() {
return balance;
}
-}
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
index 6bcc634..178100f 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
@@ -50,40 +50,44 @@ public class DefaultAccountChangeNotification implements AccountChangeNotificati
}
private List<ChangedField> calculateChangedFields(Account oldData, Account newData) {
- List<ChangedField> changedFields = new ArrayList<ChangedField>();
- if (!newData.getExternalKey().equals(oldData.getExternalKey())) {
- changedFields.add(new DefaultChangedField("externalKey", oldData.getExternalKey(), newData.getExternalKey()));
- }
- if (!newData.getEmail().equals(oldData.getEmail())) {
- changedFields.add(new DefaultChangedField("email", oldData.getEmail(), newData.getEmail()));
- }
- if (!newData.getName().equals(oldData.getName())) {
- changedFields.add(new DefaultChangedField("firstName", oldData.getName(), newData.getName()));
- }
- if (!newData.getPhone().equals(oldData.getPhone())) {
- changedFields.add(new DefaultChangedField("phone", oldData.getPhone(), newData.getPhone()));
- }
- if (!newData.getCurrency().equals(oldData.getCurrency())) {
- changedFields.add(new DefaultChangedField("currency", oldData.getCurrency().toString(), newData.getCurrency().toString()));
- }
- if (newData.getBillCycleDay() != oldData.getBillCycleDay()) {
- changedFields.add(new DefaultChangedField("billCycleDay", Integer.toString(oldData.getBillCycleDay()),
- Integer.toString(newData.getBillCycleDay())));
- }
+ List<ChangedField> tmpChangedFields = new ArrayList<ChangedField>();
- String oldProviderName = oldData.getPaymentProviderName();
- String newProviderName = newData.getPaymentProviderName();
+ addIfValueChanged(tmpChangedFields, "externalKey",
+ oldData.getExternalKey(), newData.getExternalKey());
- if ((newProviderName == null) && (oldProviderName == null)) {
- } else if ((newProviderName == null) && (oldProviderName != null)) {
- changedFields.add((new DefaultChangedField("paymentProviderName", oldProviderName, newProviderName)));
- } else if ((newProviderName != null) && (oldProviderName == null)) {
- changedFields.add((new DefaultChangedField("paymentProviderName", oldProviderName, newProviderName)));
- } else if (!newProviderName.equals(oldProviderName)) {
- changedFields.add((new DefaultChangedField("paymentProviderName", oldProviderName, newProviderName)));
- }
+ addIfValueChanged(tmpChangedFields, "email",
+ oldData.getEmail(), newData.getEmail());
- return changedFields;
+ addIfValueChanged(tmpChangedFields, "firstName",
+ oldData.getName(), newData.getName());
+
+ addIfValueChanged(tmpChangedFields, "phone",
+ oldData.getPhone(), newData.getPhone());
+
+ addIfValueChanged(tmpChangedFields, "currency",
+ (oldData.getCurrency() != null) ? oldData.getCurrency().toString() : null,
+ (newData.getCurrency() != null) ? newData.getCurrency().toString() : null);
+
+ addIfValueChanged(tmpChangedFields,
+ "billCycleDay",
+ Integer.toString(oldData.getBillCycleDay()), Integer.toString(newData.getBillCycleDay()));
+
+ addIfValueChanged(tmpChangedFields,"paymentProviderName",
+ oldData.getPaymentProviderName(), newData.getPaymentProviderName());
+ return tmpChangedFields;
+ }
+
+ private void addIfValueChanged(List<ChangedField> inputList, String key, String oldData, String newData) {
+ // If both null => no changes
+ if (newData == null && oldData == null) {
+ return;
+ // If only one is null
+ } else if (newData == null || oldData == null) {
+ inputList.add(new DefaultChangedField(key, oldData, newData));
+ // If non are null we can safely compare values
+ } else if (!newData.equals(oldData)) {
+ inputList.add(new DefaultChangedField(key, oldData, newData));
+ }
}
}
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index a20a519..d6bf995 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -19,33 +19,47 @@ package com.ning.billing.account.api.user;
import java.util.List;
import java.util.UUID;
import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountData;
import com.ning.billing.account.api.DefaultAccount;
import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
private final AccountDao dao;
@Inject
- public DefaultAccountUserApi(AccountDao dao) {
+ public DefaultAccountUserApi(final AccountDao dao) {
this.dao = dao;
}
@Override
- public Account createAccount(AccountData data) {
- Account account = new DefaultAccount(data);
- dao.save(account);
- return account;
+ public Account createAccount(final AccountData data, final List<CustomField> fields, List<Tag> tags) throws AccountApiException {
+ String key = data.getExternalKey();
+ Account existingAccount = dao.getAccountByKey(key);
+
+ if (existingAccount == null) {
+ Account account = new DefaultAccount(data);
+ account.addFields(fields);
+ account.addTags(tags);
+
+ dao.create(account);
+ return account;
+ } else {
+ throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
+ }
}
@Override
- public Account getAccountByKey(String key) {
+ public Account getAccountByKey(final String key) {
return dao.getAccountByKey(key);
}
@Override
- public Account getAccountById(UUID id) {
+ public Account getAccountById(final UUID id) {
return dao.getById(id.toString());
}
@@ -55,12 +69,12 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
}
@Override
- public UUID getIdFromKey(String externalKey) {
+ public UUID getIdFromKey(final String externalKey) {
return dao.getIdFromKey(externalKey);
}
@Override
- public void saveAccount(Account account) {
- dao.save(account);
+ public void updateAccount(final Account account) {
+ dao.update(account);
}
}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index 20eb90b..21122e2 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -17,6 +17,7 @@
package com.ning.billing.account.dao;
import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.user.AccountBuilder;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.UuidMapper;
@@ -56,7 +57,11 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
@Override
@SqlUpdate
- public void save(@AccountBinder Account account);
+ public void create(@AccountBinder Account account);
+
+ @Override
+ @SqlUpdate
+ public void update(@AccountBinder Account account);
public static class AccountMapper implements ResultSetMapper<Account> {
@Override
diff --git a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
index c108bb4..5b4ed6e 100644
--- a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -124,7 +124,7 @@ public class DefaultAccountDao implements AccountDao {
}
@Override
- public void save(final Account account) {
+ public void create(final Account account) {
final String accountId = account.getId().toString();
final String objectType = DefaultAccount.OBJECT_TYPE;
@@ -132,7 +132,7 @@ public class DefaultAccountDao implements AccountDao {
@Override
public Void inTransaction(AccountSqlDao accountDao, TransactionStatus status) throws Exception {
Account currentAccount = accountDao.getById(accountId);
- accountDao.save(account);
+ accountDao.create(account);
FieldStoreDao fieldStoreDao = accountDao.become(FieldStoreDao.class);
fieldStoreDao.save(accountId, objectType, account.getFieldList());
@@ -140,14 +140,37 @@ public class DefaultAccountDao implements AccountDao {
TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
tagStoreDao.save(accountId, objectType, account.getTagList());
- if (currentAccount == null) {
- AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
- eventBus.post(creationEvent);
- } else {
- AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
- if (changeEvent.hasChanges()) {
- eventBus.post(changeEvent);
- }
+ AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
+ eventBus.post(creationEvent);
+
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void update(final Account account) {
+ final String accountId = account.getId().toString();
+ final String objectType = DefaultAccount.OBJECT_TYPE;
+
+ accountDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+ @Override
+ public Void inTransaction(AccountSqlDao accountDao, TransactionStatus status) throws Exception {
+ Account currentAccount = accountDao.getById(accountId);
+
+ accountDao.update(account);
+
+ FieldStoreDao fieldStoreDao = accountDao.become(FieldStoreDao.class);
+ fieldStoreDao.clear(accountId, objectType);
+ fieldStoreDao.save(accountId, objectType, account.getFieldList());
+
+ TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
+ tagStoreDao.clear(accountId, objectType);
+ tagStoreDao.save(accountId, objectType, account.getTagList());
+
+ AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
+ if (changeEvent.hasChanges()) {
+ eventBus.post(changeEvent);
}
return null;
diff --git a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
index 275d4c9..345e2a1 100644
--- a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
@@ -17,7 +17,9 @@
package com.ning.billing.account.glue;
import com.google.inject.AbstractModule;
+import com.ning.billing.account.api.AccountService;
import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.DefaultAccountService;
import com.ning.billing.account.api.user.DefaultAccountUserApi;
import com.ning.billing.account.dao.AccountDao;
import com.ning.billing.account.dao.AccountSqlDao;
@@ -31,9 +33,6 @@ public class AccountModule extends AbstractModule {
bind(AccountConfig.class).toInstance(config);
}
- private void installAccountCore() {
- }
-
private void installAccountDao() {
bind(AccountDao.class).to(DefaultAccountDao.class).asEagerSingleton();
}
@@ -42,11 +41,15 @@ public class AccountModule extends AbstractModule {
bind(AccountUserApi.class).to(DefaultAccountUserApi.class).asEagerSingleton();
}
+ private void installAccountService() {
+ bind(AccountService.class).to(DefaultAccountService.class).asEagerSingleton();
+ }
+
@Override
protected void configure() {
installConfig();
- installAccountCore();
installAccountDao();
+ installAccountService();
installAccountUserApi();
}
}
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 3e0ebdd..4f6a9f9 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -1,13 +1,17 @@
group AccountDaoSql;
-save() ::= <<
+create() ::= <<
INSERT INTO accounts
(id, external_key, email, name, first_name_length, phone, currency, billing_cycle_day, payment_provider_name)
VALUES
- (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName)
- ON DUPLICATE KEY UPDATE
- external_key = :externalKey, email = :email, name = :name, first_name_length = :firstNameLength,
- phone = :phone, currency = :currency, payment_provider_name = :paymentProviderName;
+ (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName);
+>>
+
+update() ::= <<
+ UPDATE accounts
+ SET email = :email, name = :name, first_name_length = :firstNameLength, phone = :phone,
+ currency = :currency, billing_cycle_day = :billingCycleDay, payment_provider_name = :paymentProviderName
+ WHERE id = :id AND external_key = :externalKey;
>>
getAccountByKey() ::= <<
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index acca205..ccfed72 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -12,42 +12,4 @@ CREATE TABLE accounts (
PRIMARY KEY(id)
) ENGINE=innodb;
CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
-CREATE UNIQUE INDEX accounts_email ON accounts(email);
-
-DROP TABLE IF EXISTS custom_fields;
-CREATE TABLE custom_fields (
- id char(36) NOT NULL,
- object_id char(36) NOT NULL,
- object_type varchar(30) NOT NULL,
- field_name varchar(30) NOT NULL,
- field_value varchar(255) NOT NULL,
- PRIMARY KEY(id)
-) ENGINE=innodb;
-CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
-CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type, field_name);
-
-DROP TABLE IF EXISTS tag_descriptions;
-CREATE TABLE tag_descriptions (
- id char(36) NOT NULL,
- name varchar(20) NOT NULL,
- created_by varchar(50) NOT NULL,
- creation_date datetime NOT NULL,
- description varchar(200) NOT NULL,
- generate_invoice boolean DEFAULT false,
- process_payment boolean DEFAULT false,
- PRIMARY KEY(id)
-) ENGINE=innodb;
-CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
-
-DROP TABLE IF EXISTS tags;
-CREATE TABLE tags (
- id char(36) NOT NULL,
- tag_description_id char(36) NOT NULL,
- object_id char(36) NOT NULL,
- object_type varchar(30) NOT NULL,
- date_added datetime NOT NULL,
- added_by varchar(50) NOT NULL,
- PRIMARY KEY(id)
-) ENGINE = innodb;
-CREATE INDEX tags_by_object ON tags(object_id);
-CREATE UNIQUE INDEX tags_unique ON tags(tag_description_id, object_id);
\ No newline at end of file
+CREATE UNIQUE INDEX accounts_email ON accounts(email);
\ No newline at end of file
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index 77efe00..b18a41b 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -38,14 +38,17 @@ public abstract class AccountDaoTestBase {
@BeforeClass(alwaysRun = true)
protected void setup() throws IOException {
- // Healthcheck test to make sure MySQL is setup properly
+ // Health check test to make sure MySQL is setup properly
try {
module = new AccountModuleMock();
final String accountDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
final String invoiceDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+ final String utilDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
module.startDb();
module.initDb(accountDdl);
module.initDb(invoiceDdl);
+ module.initDb(utilDdl);
final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
dbi = injector.getInstance(IDBI.class);
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index 7a82f98..a4b0b98 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -21,6 +21,7 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountData;
import com.ning.billing.account.api.DefaultAccount;
import com.ning.billing.util.tag.DefaultTagDescription;
@@ -30,7 +31,6 @@ import com.ning.billing.account.api.user.AccountBuilder;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.clock.DefaultClock;
import com.ning.billing.util.tag.dao.TagDescriptionDao;
-import com.ning.billing.util.tag.dao.TagDescriptionDao;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -57,7 +57,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
public void testBasic() {
Account a = createTestAccount();
- accountDao.save(a);
+ accountDao.create(a);
String key = a.getExternalKey();
Account r = accountDao.getAccountByKey(key);
@@ -74,14 +74,14 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
}
@Test
- public void testGetById() {
+ public void testGetById() throws AccountApiException {
Account account = createTestAccount();
UUID id = account.getId();
String key = account.getExternalKey();
String name = account.getName();
int firstNameLength = account.getFirstNameLength();
- accountDao.save(account);
+ accountDao.create(account);
account = accountDao.getById(id.toString());
assertNotNull(account);
@@ -99,7 +99,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
String fieldValue = "testField1_value";
account.setFieldValue(fieldName, fieldValue);
- accountDao.save(account);
+ accountDao.create(account);
Account thisAccount = accountDao.getAccountByKey(account.getExternalKey());
assertNotNull(thisAccount);
@@ -112,13 +112,13 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
Account account = createTestAccount();
TagDescription description = new DefaultTagDescription("Test Tag", "For testing only", true, true, "Test System", new DateTime());
TagDescriptionDao tagDescriptionDao = dbi.onDemand(TagDescriptionDao.class);
- tagDescriptionDao.save(description);
+ tagDescriptionDao.create(description);
String addedBy = "testTags()";
DateTime dateAdded = new DefaultClock().getUTCNow();
account.addTag(description, addedBy, dateAdded);
assertEquals(account.getTagList().size(), 1);
- accountDao.save(account);
+ accountDao.create(account);
Account thisAccount = accountDao.getById(account.getId().toString());
List<Tag> tagList = thisAccount.getTagList();
@@ -135,16 +135,16 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
@Test
public void testGetIdFromKey() {
Account account = createTestAccount();
- accountDao.save(account);
+ accountDao.create(account);
UUID accountId = accountDao.getIdFromKey(account.getExternalKey());
assertEquals(accountId, account.getId());
}
@Test
- public void testUpdate() {
+ public void testUpdate() throws AccountApiException {
final Account account = createTestAccount();
- accountDao.save(account);
+ accountDao.create(account);
AccountData accountData = new AccountData() {
@Override
@@ -182,7 +182,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
};
Account updatedAccount = new DefaultAccount(account.getId(), accountData);
- accountDao.save(updatedAccount);
+ accountDao.update(updatedAccount);
Account savedAccount = accountDao.getAccountByKey(account.getExternalKey());
analytics/pom.xml 2(+1 -1)
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 692fded..03e7d72 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-analytics</artifactId>
diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index cce74e7..d623338 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountChangeNotification;
import com.ning.billing.account.api.AccountCreationNotification;
import com.ning.billing.entitlement.api.user.SubscriptionTransition;
@@ -35,12 +36,14 @@ public class AnalyticsListener
}
@Subscribe
- public void handleSubscriptionTransitionChange(final SubscriptionTransition event)
- {
+ public void handleSubscriptionTransitionChange(final SubscriptionTransition event) throws AccountApiException {
switch (event.getTransitionType()) {
+ case MIGRATE_ENTITLEMENT:
+ // TODO do nothing for now
+ break;
case CREATE:
bstRecorder.subscriptionCreated(event);
- break;
+ break;
case CANCEL:
bstRecorder.subscriptionCancelled(event);
break;
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
index 33b8f4d..0384fab 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics;
import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
import com.ning.billing.catalog.api.Currency;
@@ -46,44 +47,37 @@ public class BusinessSubscriptionTransitionRecorder
this.accountApi = accountApi;
}
- public void subscriptionCreated(final SubscriptionTransition created)
- {
+ public void subscriptionCreated(final SubscriptionTransition created) throws AccountApiException {
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
recordTransition(event, created);
}
- public void subscriptionCancelled(final SubscriptionTransition cancelled)
- {
+ public void subscriptionCancelled(final SubscriptionTransition cancelled) throws AccountApiException {
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
recordTransition(event, cancelled);
}
- public void subscriptionChanged(final SubscriptionTransition changed)
- {
+ public void subscriptionChanged(final SubscriptionTransition changed) throws AccountApiException {
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
recordTransition(event, changed);
}
- public void subscriptionPaused(final SubscriptionTransition paused)
- {
+ public void subscriptionPaused(final SubscriptionTransition paused) throws AccountApiException {
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
recordTransition(event, paused);
}
- public void subscriptionResumed(final SubscriptionTransition resumed)
- {
+ public void subscriptionResumed(final SubscriptionTransition resumed) throws AccountApiException {
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
recordTransition(event, resumed);
}
- public void subscriptionPhaseChanged(final SubscriptionTransition phaseChanged)
- {
+ public void subscriptionPhaseChanged(final SubscriptionTransition phaseChanged) throws AccountApiException {
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
recordTransition(event, phaseChanged);
}
- public void recordTransition(final BusinessSubscriptionEvent event, final SubscriptionTransition transition)
- {
+ public void recordTransition(final BusinessSubscriptionEvent event, final SubscriptionTransition transition) throws AccountApiException {
Currency currency = null;
String transitionKey = null;
String accountKey = null;
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 03a27d0..c146de7 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -48,7 +48,9 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.user.ApiEventType;
import com.ning.billing.util.eventbus.EventBus;
+import com.ning.billing.util.tag.DefaultTag;
import com.ning.billing.util.tag.DefaultTagDescription;
+import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.dao.TagDescriptionDao;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
@@ -61,13 +63,17 @@ import org.testng.annotations.Test;
import java.io.IOException;
import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
+import static org.testng.Assert.fail;
+
@Guice(modules = AnalyticsTestModule.class)
public class TestAnalyticsService
{
- private static final String KEY = "1234";
- private static final String ACCOUNT_KEY = "pierre-1234";
+ private static final String KEY = "12345";
+ private static final String ACCOUNT_KEY = "pierre-12345";
private static final DefaultTagDescription TAG_ONE = new DefaultTagDescription("batch20", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
private static final DefaultTagDescription TAG_TWO = new DefaultTagDescription("awesome", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
@@ -106,18 +112,23 @@ public class TestAnalyticsService
// Killbill generic setup
setupBusAndMySQL();
- tagDao.save(TAG_ONE);
- tagDao.save(TAG_TWO);
+ tagDao.create(TAG_ONE);
+ tagDao.create(TAG_TWO);
final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, Currency.USD);
- final Account storedAccount = accountApi.createAccount(account);
- storedAccount.addTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC));
- storedAccount.addTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC));
- accountApi.saveAccount(storedAccount);
-
- // Create events for the bus and expected results
- createSubscriptionTransitionEvent(storedAccount);
- createAccountCreationEvent(storedAccount);
+ try {
+ List<Tag> tags = new ArrayList<Tag>();
+ tags.add(new DefaultTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC)));
+ tags.add(new DefaultTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC)));
+
+ final Account storedAccount = accountApi.createAccount(account, null, tags);
+
+ // Create events for the bus and expected results
+ createSubscriptionTransitionEvent(storedAccount);
+ createAccountCreationEvent(storedAccount);
+ } catch (Throwable t) {
+ fail("Initializing accounts failed.", t);
+ }
}
private void setupBusAndMySQL() throws IOException
@@ -127,11 +138,13 @@ public class TestAnalyticsService
final String analyticsDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
final String accountDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
final String entitlementDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+ final String utilDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
helper.startMysql();
helper.initDb(analyticsDdl);
helper.initDb(accountDdl);
helper.initDb(entitlementDdl);
+ helper.initDb(utilDdl);
}
private void createSubscriptionTransitionEvent(final Account account) throws EntitlementUserApiException
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
index faf4dc8..3b38c8c 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -109,6 +109,11 @@ public class MockAccount implements Account
}
@Override
+ public void addFields(List<CustomField> fields) {
+ throw new NotImplementedException();
+ }
+
+ @Override
public void clearFields() {
throw new NotImplementedException();
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
index 4c39456..791d191 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -21,10 +21,13 @@ import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.util.List;
import java.util.UUID;
import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountData;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.account.api.DefaultAccount;
import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
public class MockIAccountUserApi implements AccountUserApi
{
@@ -38,14 +41,14 @@ public class MockIAccountUserApi implements AccountUserApi
}
@Override
- public Account createAccount(final AccountData data)
+ public Account createAccount(final AccountData data, final List<CustomField> fields, final List<Tag> tags)
{
throw new UnsupportedOperationException();
}
@Override
- public void saveAccount(Account account) {
- throw new NotImplementedException();
+ public void updateAccount(final Account account) {
+ throw new UnsupportedOperationException();
}
@Override
@@ -55,8 +58,7 @@ public class MockIAccountUserApi implements AccountUserApi
}
@Override
- public Account getAccountById(final UUID uid)
- {
+ public Account getAccountById(final UUID uid) {
return new DefaultAccount(account);
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
index 3017413..12b6f77 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
@@ -24,7 +24,9 @@ import org.joda.time.DateTime;
import com.ning.billing.account.api.AccountData;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -104,9 +106,13 @@ public class MockIEntitlementUserApi implements EntitlementUserApi
}
@Override
- public Subscription createSubscription(UUID bundleId, String productName,
- BillingPeriod term, String priceList, PhaseType initialPhase,
+ public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec,
DateTime requestedDate) throws EntitlementUserApiException {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException();
}
+
+ @Override
+ public SubscriptionBundle getBundleForKey(String bundleKey) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
index cff4ad7..5982bcd 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -127,4 +127,9 @@ public class MockSubscription implements Subscription
public List<SubscriptionTransition> getActiveTransitions() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public SubscriptionTransition getPendingTransition() {
+ throw new UnsupportedOperationException();
+ }
}
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index 013feb9..a998c67 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountApiException.java b/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
new file mode 100644
index 0000000..d9761b6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.account.api;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+
+public class AccountApiException extends BillingExceptionBase {
+ public AccountApiException(Throwable cause, int code, final String msg) {
+ super(cause, code, msg);
+ }
+
+ public AccountApiException(Throwable cause, ErrorCode code, final Object... args) {
+ super(cause, code, args);
+ }
+
+ public AccountApiException(ErrorCode code, final Object... args) {
+ super(code, args);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index b34d9a3..a535ada 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -18,12 +18,14 @@ package com.ning.billing.account.api;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
public interface AccountUserApi {
- public Account createAccount(AccountData data);
+ public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
- public void saveAccount(Account account);
+ public void updateAccount(Account account);
public Account getAccountByKey(String key);
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java b/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
index b325d6b..53854d1 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
@@ -17,13 +17,13 @@
package com.ning.billing.entitlement.api;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
import com.ning.billing.entitlement.api.test.EntitlementTestApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.lifecycle.KillbillService;
public interface EntitlementService extends KillbillService {
-
@Override
public String getName();
@@ -33,5 +33,5 @@ public interface EntitlementService extends KillbillService {
public EntitlementTestApi getTestApi();
-
+ public EntitlementMigrationApi getMigrationApi();
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
new file mode 100644
index 0000000..9498c6f
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+
+public interface EntitlementMigrationApi {
+
+
+ public interface EntitlementAccountMigration {
+ public UUID getAccountKey();
+ public EntitlementBundleMigration [] getBundles();
+ }
+
+ public interface EntitlementBundleMigration {
+ public String getBundleKey();
+ public EntitlementSubscriptionMigration [] getSubscriptions();
+ }
+
+ public interface EntitlementSubscriptionMigration {
+ public ProductCategory getCategory();
+ public EntitlementSubscriptionMigrationCase [] getSubscriptionCases();
+ }
+
+ /**
+ *
+ * Each case is either a PHASE or a different PlanSpecifer
+ */
+ public interface EntitlementSubscriptionMigrationCase {
+ public PlanPhaseSpecifier getPlanPhaseSpecifer();
+ public DateTime getEffectiveDate();
+ public DateTime getCancelledDate();
+ }
+
+
+ /**
+ * Migrate all the existing entitlements associated with that account.
+ * The semantics is 'all or nothing' (atomic operation)
+ *
+ * @param toBeMigrated all the bundles and associated subscription that should be migrated for the account
+ *
+ */
+ public void migrate(EntitlementAccountMigration toBeMigrated)
+ throws EntitlementMigrationApiException;
+
+ /**
+ * Remove all the data pertaining to that acount
+ *
+ * @param accountKey
+ */
+ public void undoMigration(UUID accountKey)
+ throws EntitlementMigrationApiException;
+
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java
new file mode 100644
index 0000000..55835fd
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+public class EntitlementMigrationApiException extends Exception {
+
+ private static final long serialVersionUID = 7623133L;
+
+ public EntitlementMigrationApiException() {
+ super();
+ }
+
+ public EntitlementMigrationApiException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EntitlementMigrationApiException(String message) {
+ super(message);
+ }
+
+ public EntitlementMigrationApiException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 1f431ed..22b9830 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -19,9 +19,7 @@ package com.ning.billing.entitlement.api.user;
import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
-import com.ning.billing.account.api.AccountData;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
public interface EntitlementUserApi {
@@ -30,6 +28,8 @@ public interface EntitlementUserApi {
public Subscription getSubscriptionFromId(UUID id);
+ public SubscriptionBundle getBundleForKey(String bundleKey);
+
public List<SubscriptionBundle> getBundlesForAccount(UUID accountId);
public List<Subscription> getSubscriptionsForBundle(UUID bundleId);
@@ -39,6 +39,7 @@ public interface EntitlementUserApi {
public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey)
throws EntitlementUserApiException;
- public Subscription createSubscription(UUID bundleId, String productName, BillingPeriod term, String priceList, PhaseType initialPhase, DateTime requestedDate)
+ public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate)
throws EntitlementUserApiException;
+
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 8f13e06..4b56301 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -67,4 +67,6 @@ public interface Subscription {
public List<SubscriptionTransition> getActiveTransitions();
+ public SubscriptionTransition getPendingTransition();
+
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index 517a7b8..26ce81f 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -27,6 +27,7 @@ import java.util.UUID;
public interface SubscriptionTransition extends EventBusNotification {
public enum SubscriptionTransitionType {
+ MIGRATE_ENTITLEMENT,
CREATE,
CHANGE,
PAUSE,
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 8336ddb..8e3352c 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -82,7 +82,15 @@ public enum ErrorCode {
/*
* Phases
*/
- CAT_NO_SUCH_PHASE(2040,"Could not find any phases named '%s'")
+ CAT_NO_SUCH_PHASE(2040,"Could not find any phases named '%s'"),
+
+ /*
+ *
+ * Range 3000 : ACCOUNT
+ *
+ */
+ ACCOUNT_ALREADY_EXISTS(3000, "Account already exists for key %s"),
+ ACCOUNT_INVALID_NAME(3001, "An invalid name was specified when creating or updating an account.")
;
private int code;
diff --git a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
index 7b8f37a..e21fd83 100644
--- a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
+++ b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
@@ -26,6 +26,8 @@ public interface CustomizableEntity extends Entity {
public List<CustomField> getFieldList();
+ public void addFields(List<CustomField> fields);
+
public void clearFields();
public String getObjectName();
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 07e2a57..ec79685 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index ba9a714..8d5c9c1 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java
index 1a3b0b5..b38c1f8 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java
@@ -31,8 +31,8 @@ public abstract class CasePhase<T> extends CaseStandardNaming<T> {
private PhaseType phaseType;
public T getResult(PlanPhaseSpecifier specifier, StandaloneCatalog c) throws CatalogApiException {
- if (
- (phaseType == null || specifier.getPhaseType() == null || specifier.getPhaseType() == phaseType) &&
+ if ((phaseType == null ||
+ specifier.getPhaseType() == null || specifier.getPhaseType() == phaseType) &&
satisfiesCase(new PlanSpecifier(specifier), c)
) {
return getResult();
diff --git a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
index 88a7dd4..bb48b9b 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
@@ -15,19 +15,45 @@
*/
package com.ning.billing.catalog;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
import com.google.inject.Inject;
-import com.ning.billing.catalog.api.*;
+import com.ning.billing.catalog.api.ActionPolicy;
+import com.ning.billing.catalog.api.BillingAlignment;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanAlignmentChange;
+import com.ning.billing.catalog.api.PlanAlignmentCreate;
+import com.ning.billing.catalog.api.PlanChangeResult;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.Product;
import com.ning.billing.util.config.ValidatingConfig;
import com.ning.billing.util.config.ValidationErrors;
-import java.net.URI;
-import java.util.*;
+@XmlRootElement(name="catalog")
+@XmlAccessorType(XmlAccessType.NONE)
public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implements Catalog {
private StandaloneCatalog currentCatalog;
+ @XmlElement(name="catalogVersion", required=true)
private final List<StandaloneCatalog> versions = new ArrayList<StandaloneCatalog>();
@Inject
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 93853ef..7e97852 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
new file mode 100644
index 0000000..4290f73
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.alignment;
+
+
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApiException;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigrationCase;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.util.clock.DefaultClock;
+
+public class MigrationPlanAligner {
+
+ private final CatalogService catalogService;
+
+ @Inject
+ public MigrationPlanAligner(CatalogService catalogService) {
+ this.catalogService = catalogService;
+ }
+
+
+ public TimedMigration [] getEventsMigration(EntitlementSubscriptionMigrationCase [] input, DateTime now)
+ throws EntitlementMigrationApiException {
+
+ try {
+ TimedMigration [] events = null;
+ Plan plan0 = catalogService.getCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
+ input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName());
+
+ Plan plan1 = (input.length > 1) ? catalogService.getCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
+ input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName()) :
+ null;
+
+ DateTime migrationStartDate = now;
+
+ if (isRegularMigratedSubscription(input)) {
+
+ events = getEventsOnRegularMigration(plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now);
+
+ } else if (isRegularFutureCancelledMigratedSubscription(input)) {
+
+ events = getEventsOnFuturePlanCancelMigration(plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now,
+ input[0].getCancelledDate());
+
+ } else if (isPhaseChangeMigratedSubscription(input)) {
+
+ PhaseType curPhaseType = input[0].getPlanPhaseSpecifer().getPhaseType();
+ Duration curPhaseDuration = null;
+ for (PlanPhase cur : plan0.getAllPhases()) {
+ if (cur.getPhaseType() == curPhaseType) {
+ curPhaseDuration = cur.getDuration();
+ break;
+ }
+ }
+ if (curPhaseDuration == null) {
+ throw new EntitlementMigrationApiException(String.format("Failed to compute current phase duration for plan %s and phase %s",
+ plan0.getName(), curPhaseType));
+ }
+
+ migrationStartDate = DefaultClock.removeDuration(input[1].getEffectiveDate(), curPhaseDuration);
+ events = getEventsOnFuturePhaseChangeMigration(plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ migrationStartDate,
+ input[1].getEffectiveDate());
+
+ } else if (isPlanChangeMigratedSubscription(input)) {
+
+ events = getEventsOnFuturePlanChangeMigration(plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ plan1,
+ getPlanPhase(plan1, input[1].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now,
+ input[1].getEffectiveDate());
+
+ } else {
+ throw new EntitlementMigrationApiException("Unknown migration type");
+ }
+
+ return events;
+ } catch (CatalogApiException e) {
+ throw new EntitlementMigrationApiException(e);
+ }
+ }
+
+ private TimedMigration [] getEventsOnRegularMigration(Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate) {
+ TimedMigration [] result = new TimedMigration[1];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
+ return result;
+ }
+
+ private TimedMigration [] getEventsOnFuturePhaseChangeMigration(Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForNextPhase)
+ throws EntitlementMigrationApiException {
+
+ TimedMigration [] result = new TimedMigration[2];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
+ boolean foundCurrent = false;
+ PlanPhase nextPhase = null;
+ for (PlanPhase cur : plan.getAllPhases()) {
+ if (cur == initialPhase) {
+ foundCurrent = true;
+ continue;
+ }
+ if (foundCurrent) {
+ nextPhase = cur;
+ }
+ }
+ if (nextPhase == null) {
+ throw new EntitlementMigrationApiException(String.format("Cannot find next phase for Plan %s and current Phase %s",
+ plan.getName(), initialPhase.getName()));
+ }
+ result[1] = new TimedMigration(effectiveDateForNextPhase, EventType.PHASE, null, plan, nextPhase, priceList);
+ return result;
+ }
+
+ private TimedMigration [] getEventsOnFuturePlanChangeMigration(Plan currentPlan, PlanPhase currentPhase, Plan newPlan, PlanPhase newPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForChangePlan) {
+ TimedMigration [] result = new TimedMigration[2];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, currentPlan, currentPhase, priceList);
+ result[1] = new TimedMigration(effectiveDateForChangePlan, EventType.API_USER, ApiEventType.CHANGE, newPlan, newPhase, priceList);
+ return result;
+ }
+
+ private TimedMigration [] getEventsOnFuturePlanCancelMigration(Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForCancellation) {
+ TimedMigration [] result = new TimedMigration[2];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
+ result[1] = new TimedMigration(effectiveDateForCancellation, EventType.API_USER, ApiEventType.CANCEL, null, null, null);
+ return result;
+ }
+
+
+ // STEPH should be in catalog
+ private PlanPhase getPlanPhase(Plan plan, PhaseType phaseType) throws EntitlementMigrationApiException {
+ for (PlanPhase cur: plan.getAllPhases()) {
+ if (cur.getPhaseType() == phaseType) {
+ return cur;
+ }
+ }
+ throw new EntitlementMigrationApiException(String.format("Cannot find PlanPhase from Plan %s and type %s", plan.getName(), phaseType));
+ }
+
+ private boolean isRegularMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ return (input.length == 1 && input[0].getCancelledDate() == null);
+ }
+
+ private boolean isRegularFutureCancelledMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ return (input.length == 1 && input[0].getCancelledDate() != null);
+ }
+
+ private boolean isPhaseChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ if (input.length != 2) {
+ return false;
+ }
+ return (isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer()) &&
+ !isSamePhase(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer()));
+ }
+
+ private boolean isPlanChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ if (input.length != 2) {
+ return false;
+ }
+ return ! isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
+ }
+
+ private boolean isSamePlan(PlanPhaseSpecifier plan0, PlanPhaseSpecifier plan1) {
+ if (plan0.getPriceListName().equals(plan1.getPriceListName()) &&
+ plan0.getProductName().equals(plan1.getProductName())) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isSamePhase(PlanPhaseSpecifier plan0, PlanPhaseSpecifier plan1) {
+ if (plan0.getPhaseType() == plan1.getPhaseType()) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/TimedMigration.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/TimedMigration.java
new file mode 100644
index 0000000..e1ad564
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/TimedMigration.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.alignment;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+
+public class TimedMigration {
+
+ private final DateTime eventTime;
+ private final EventType eventType;
+ private final ApiEventType apiEventType;
+
+ private final Plan plan;
+ private final PlanPhase phase;
+ private final String priceList;
+
+
+ public TimedMigration(DateTime eventTime, EventType eventType,
+ ApiEventType apiEventType, Plan plan, PlanPhase phase, String priceList) {
+ super();
+ this.eventTime = eventTime;
+ this.eventType = eventType;
+ this.apiEventType = apiEventType;
+ this.plan = plan;
+ this.phase = phase;
+ this.priceList = priceList;
+ }
+
+ public DateTime getEventTime() {
+ return eventTime;
+ }
+
+ public EventType getEventType() {
+ return eventType;
+ }
+
+
+ public ApiEventType getApiEventType() {
+ return apiEventType;
+ }
+
+ public Plan getPlan() {
+ return plan;
+ }
+
+ public PlanPhase getPhase() {
+ return phase;
+ }
+
+ public String getPriceList() {
+ return priceList;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
new file mode 100644
index 0000000..7396136
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+import java.util.List;
+
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public class AccountMigrationData {
+
+ private final List<BundleMigrationData> data;
+
+ public AccountMigrationData(List<BundleMigrationData> data) {
+ super();
+ this.data = data;
+ }
+
+ public List<BundleMigrationData> getData() {
+ return data;
+ }
+
+ public static class BundleMigrationData {
+
+ private final SubscriptionBundleData data;
+ private final List<SubscriptionMigrationData> subscriptions;
+
+ public BundleMigrationData(SubscriptionBundleData data,
+ List<SubscriptionMigrationData> subscriptions) {
+ super();
+ this.data = data;
+ this.subscriptions = subscriptions;
+ }
+
+ public SubscriptionBundleData getData() {
+ return data;
+ }
+
+ public List<SubscriptionMigrationData> getSubscriptions() {
+ return subscriptions;
+ }
+ }
+
+ public static class SubscriptionMigrationData {
+
+ private final SubscriptionData data;
+ private final List<EntitlementEvent> initialEvents;
+
+ public SubscriptionMigrationData(SubscriptionData data,
+
+ List<EntitlementEvent> initialEvents) {
+ super();
+ this.data = data;
+ this.initialEvents = initialEvents;
+ }
+
+ public SubscriptionData getData() {
+ return data;
+ }
+
+ public List<EntitlementEvent> getInitialEvents() {
+ return initialEvents;
+ }
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
new file mode 100644
index 0000000..d8a0100
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
+import com.ning.billing.entitlement.alignment.TimedMigration;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.phase.PhaseEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEventData;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+import com.ning.billing.entitlement.events.user.ApiEventChange;
+import com.ning.billing.entitlement.events.user.ApiEventMigrate;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.DefaultClock;
+
+public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
+
+
+ private final EntitlementDao dao;
+ private final MigrationPlanAligner migrationAligner;
+ private final SubscriptionFactory factory;
+ private final CatalogService catalogService;
+ private final Clock clock;
+
+ @Inject
+ public DefaultEntitlementMigrationApi(MigrationPlanAligner migrationAligner,
+ SubscriptionFactory factory,
+ CatalogService catalogService,
+ EntitlementDao dao,
+ Clock clock) {
+ this.dao = dao;
+ this.migrationAligner = migrationAligner;
+ this.factory = factory;
+ this.catalogService = catalogService;
+ this.clock = clock;
+ }
+
+ @Override
+ public void migrate(EntitlementAccountMigration toBeMigrated)
+ throws EntitlementMigrationApiException {
+ AccountMigrationData accountMigrationData = createAccountMigrationData(toBeMigrated);
+ dao.migrate(toBeMigrated.getAccountKey(), accountMigrationData);
+ }
+
+ @Override
+ public void undoMigration(UUID accountId) {
+ dao.undoMigration(accountId);
+ }
+
+ private AccountMigrationData createAccountMigrationData(EntitlementAccountMigration toBeMigrated)
+ throws EntitlementMigrationApiException {
+
+ final UUID accountId = toBeMigrated.getAccountKey();
+ final DateTime now = clock.getUTCNow();
+
+ List<BundleMigrationData> accountBundleData = new LinkedList<BundleMigrationData>();
+
+ for (final EntitlementBundleMigration curBundle : toBeMigrated.getBundles()) {
+
+ SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId);
+ List<SubscriptionMigrationData> bundleSubscriptionData = new LinkedList<AccountMigrationData.SubscriptionMigrationData>();
+
+ for (EntitlementSubscriptionMigration curSub : curBundle.getSubscriptions()) {
+ SubscriptionMigrationData data = null;
+ switch (curSub.getCategory()) {
+ case BASE:
+ data = createBaseSubscriptionMigrationData(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now);
+ break;
+ case ADD_ON:
+ // Not implemented yet
+ break;
+ case STANDALONE:
+ // Not implemented yet
+ break;
+ default:
+ throw new EntitlementMigrationApiException(String.format("Unkown product type ", curSub.getCategory()));
+ }
+ if (data != null) {
+ bundleSubscriptionData.add(data);
+ }
+ }
+ BundleMigrationData bundleMigrationData = new BundleMigrationData(bundleData, bundleSubscriptionData);
+ accountBundleData.add(bundleMigrationData);
+ }
+ AccountMigrationData accountMigrationData = new AccountMigrationData(accountBundleData);
+ return accountMigrationData;
+ }
+
+ private SubscriptionMigrationData createBaseSubscriptionMigrationData(UUID bundleId, ProductCategory productCategory,
+ EntitlementSubscriptionMigrationCase [] input, DateTime now)
+ throws EntitlementMigrationApiException {
+
+ TimedMigration [] events = migrationAligner.getEventsMigration(input, now);
+ DateTime migrationStartDate= events[0].getEventTime();
+ List<EntitlementEvent> emptyEvents = Collections.emptyList();
+ SubscriptionData subscriptionData = factory.createSubscription(new SubscriptionBuilder()
+ .setId(UUID.randomUUID())
+ .setBundleId(bundleId)
+ .setCategory(productCategory)
+ .setBundleStartDate(migrationStartDate)
+ .setStartDate(migrationStartDate),
+ emptyEvents);
+ return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
+ }
+
+ private List<EntitlementEvent> toEvents(SubscriptionData subscriptionData, DateTime now, TimedMigration [] migrationEvents) {
+
+ List<EntitlementEvent> events = new ArrayList<EntitlementEvent>(migrationEvents.length);
+ for (TimedMigration cur : migrationEvents) {
+
+ if (cur.getEventType() == EventType.PHASE) {
+ PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(cur.getPhase().getName(), subscriptionData, now, cur.getEventTime());
+ events.add(nextPhaseEvent);
+
+ } else if (cur.getEventType() == EventType.API_USER) {
+
+ ApiEventBuilder builder = new ApiEventBuilder()
+ .setSubscriptionId(subscriptionData.getId())
+ .setEventPlan((cur.getPlan() != null) ? cur.getPlan().getName() : null)
+ .setEventPlanPhase((cur.getPhase() != null) ? cur.getPhase().getName() : null)
+ .setEventPriceList(cur.getPriceList())
+ .setActiveVersion(subscriptionData.getActiveVersion())
+ .setEffectiveDate(cur.getEventTime())
+ .setProcessedDate(now)
+ .setRequestedDate(now);
+
+ switch(cur.getApiEventType()) {
+ case MIGRATE_ENTITLEMENT:
+ events.add(new ApiEventMigrate(builder));
+ break;
+
+ case CHANGE:
+ events.add(new ApiEventChange(builder));
+ break;
+ case CANCEL:
+ events.add(new ApiEventCancel(builder));
+ break;
+ default:
+ throw new EntitlementError(String.format("Unexpected type of api migration event %s", cur.getApiEventType()));
+ }
+ } else {
+ throw new EntitlementError(String.format("Unexpected type of migration event %s", cur.getEventType()));
+ }
+ }
+ return events;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index 2cf23bc..8b6d59a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -21,13 +21,11 @@ import java.util.UUID;
import org.joda.time.DateTime;
import com.google.inject.Inject;
import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.AccountData;
-import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
@@ -62,6 +60,11 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
@Override
+ public SubscriptionBundle getBundleForKey(String bundleKey) {
+ return dao.getSubscriptionBundleFromKey(bundleKey);
+ }
+
+ @Override
public List<SubscriptionBundle> getBundlesForAccount(UUID accountId) {
return dao.getSubscriptionBundleForAccount(accountId);
}
@@ -85,11 +88,10 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
@Override
- public Subscription createSubscription(UUID bundleId, String productName,
- BillingPeriod term, String priceList, PhaseType initialPhase, DateTime requestedDate) throws EntitlementUserApiException {
+ public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate) throws EntitlementUserApiException {
try {
- String realPriceList = (priceList == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : priceList;
+ String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
DateTime now = clock.getUTCNow();
requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : now;
if (requestedDate != null && requestedDate.isAfter(now)) {
@@ -98,13 +100,13 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
requestedDate = (requestedDate == null) ? now : requestedDate;
DateTime effectiveDate = requestedDate;
- Plan plan = catalogService.getCatalog().findPlan(productName, term, realPriceList);
+ Plan plan = catalogService.getCatalog().findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList);
PlanPhase phase = (plan.getInitialPhases() != null) ? plan.getInitialPhases()[0] : plan.getFinalPhase();
if (phase == null) {
throw new EntitlementError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
- productName, term.toString(), realPriceList));
+ spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
}
SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
@@ -139,7 +141,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
.setCategory(plan.getProduct().getCategory())
.setBundleStartDate(bundleStartDate)
.setStartDate(effectiveDate),
- plan, initialPhase, realPriceList, requestedDate, effectiveDate, now);
+ plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now);
return subscription;
} catch (CatalogApiException e) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index 3b3ff73..3aebac0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -71,7 +71,10 @@ public class SubscriptionApiService {
.setEffectiveDate(effectiveDate)
.setRequestedDate(requestedDate));
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(curAndNextPhases[1], subscription, processedDate);
+ TimedPhase nextTimedPhase = curAndNextPhases[1];
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, processedDate, nextTimedPhase.getStartPhase()) :
+ null;
List<EntitlementEvent> events = new ArrayList<EntitlementEvent>();
events.add(creationEvent);
if (nextPhaseEvent != null) {
@@ -95,7 +98,8 @@ public class SubscriptionApiService {
}
DateTime now = clock.getUTCNow();
- requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : null;
+ requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : now;
+ // STEPH needs to check if requestedDate is before last 'erasable event'?
if (requestedDate != null && requestedDate.isAfter(now)) {
throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
}
@@ -109,7 +113,7 @@ public class SubscriptionApiService {
ActionPolicy policy = null;
policy = catalogService.getCatalog().planCancelPolicy(planPhase);
- DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, now);
+ DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(subscription.getId())
@@ -146,7 +150,9 @@ public class SubscriptionApiService {
DateTime planStartDate = subscription.getCurrentPlanStart();
TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, planStartDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+ null;
if (nextPhaseEvent != null) {
uncancelEvents.add(nextPhaseEvent);
}
@@ -159,66 +165,74 @@ public class SubscriptionApiService {
throws EntitlementUserApiException {
try {
- requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : null;
- String currentPriceList = subscription.getCurrentPriceList();
- SubscriptionState currentState = subscription.getState();
- if (currentState != SubscriptionState.ACTIVE) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_NON_ACTIVE, subscription.getId(), currentState);
- }
- if (subscription.isSubscriptionFutureCancelled()) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_FUTURE_CANCELLED, subscription.getId());
- }
+ DateTime now = clock.getUTCNow();
+ requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : now;
+ // STEPH needs to check if requestedDate is before last 'erasable event'?
+ if (requestedDate != null && requestedDate.isAfter(now)) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
+ }
- DateTime now = clock.getUTCNow();
- PlanChangeResult planChangeResult = null;
- try {
+ String currentPriceList = subscription.getCurrentPriceList();
- Product destProduct = catalogService.getCatalog().findProduct(productName);
- Plan currentPlan = subscription.getCurrentPlan();
- PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
- currentPlan.getProduct().getCategory(),
- currentPlan.getBillingPeriod(),
- currentPriceList, subscription.getCurrentPhase().getPhaseType());
- PlanSpecifier toPlanPhase = new PlanSpecifier(productName,
- destProduct.getCategory(),
- term,
- priceList);
-
- planChangeResult = catalogService.getCatalog().planChange(fromPlanPhase, toPlanPhase);
- } catch (CatalogApiException e) {
- throw new EntitlementUserApiException(e);
- }
+ SubscriptionState currentState = subscription.getState();
+ if (currentState != SubscriptionState.ACTIVE) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_NON_ACTIVE, subscription.getId(), currentState);
+ }
- ActionPolicy policy = planChangeResult.getPolicy();
- PriceList newPriceList = planChangeResult.getNewPriceList();
+ if (subscription.isSubscriptionFutureCancelled()) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_FUTURE_CANCELLED, subscription.getId());
+ }
+ PlanChangeResult planChangeResult = null;
+ try {
+
+ Product destProduct = catalogService.getCatalog().findProduct(productName);
+ Plan currentPlan = subscription.getCurrentPlan();
+ PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
+ currentPlan.getProduct().getCategory(),
+ currentPlan.getBillingPeriod(),
+ currentPriceList, subscription.getCurrentPhase().getPhaseType());
+ PlanSpecifier toPlanPhase = new PlanSpecifier(productName,
+ destProduct.getCategory(),
+ term,
+ priceList);
+
+ planChangeResult = catalogService.getCatalog().planChange(fromPlanPhase, toPlanPhase);
+ } catch (CatalogApiException e) {
+ throw new EntitlementUserApiException(e);
+ }
- Plan newPlan = catalogService.getCatalog().findPlan(productName, term, newPriceList.getName());
- DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, now);
+ ActionPolicy policy = planChangeResult.getPolicy();
+ PriceList newPriceList = planChangeResult.getNewPriceList();
- TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+ Plan newPlan = catalogService.getCatalog().findPlan(productName, term, newPriceList.getName());
+ DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, now);
- EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
- .setSubscriptionId(subscription.getId())
- .setEventPlan(newPlan.getName())
- .setEventPlanPhase(currentTimedPhase.getPhase().getName())
- .setEventPriceList(newPriceList.getName())
- .setActiveVersion(subscription.getActiveVersion())
- .setProcessedDate(now)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(now));
-
- TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
- List<EntitlementEvent> changeEvents = new ArrayList<EntitlementEvent>();
- // Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
- if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
- changeEvents.add(nextPhaseEvent);
- }
- changeEvents.add(changeEvent);
- dao.changePlan(subscription.getId(), changeEvents);
- subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+ TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+
+ EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
+ .setSubscriptionId(subscription.getId())
+ .setEventPlan(newPlan.getName())
+ .setEventPlanPhase(currentTimedPhase.getPhase().getName())
+ .setEventPriceList(newPriceList.getName())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(now));
+
+ TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+ null;
+ List<EntitlementEvent> changeEvents = new ArrayList<EntitlementEvent>();
+ // Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
+ if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
+ changeEvents.add(nextPhaseEvent);
+ }
+ changeEvents.add(changeEvent);
+ dao.changePlan(subscription.getId(), changeEvents);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
} catch (CatalogApiException e) {
throw new EntitlementUserApiException(e);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 669c77d..3e8ede5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -170,6 +170,18 @@ public class SubscriptionData implements Subscription {
return activeTransitions;
}
+ @Override
+ public SubscriptionTransition getPendingTransition() {
+ if (transitions == null) {
+ return null;
+ }
+ for (SubscriptionTransition cur : transitions) {
+ if (cur.getEffectiveTransitionTime().isAfter(clock.getUTCNow())) {
+ return cur;
+ }
+ }
+ return null;
+ }
public SubscriptionTransition getLatestTranstion() {
@@ -265,10 +277,10 @@ public class SubscriptionData implements Subscription {
}
- public DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime now) {
+ public DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime requestedDate) {
if (policy == ActionPolicy.IMMEDIATE) {
- return now;
+ return requestedDate;
}
if (policy != ActionPolicy.END_OF_TERM) {
throw new EntitlementError(String.format("Unexpected policy type %s", policy.toString()));
@@ -342,6 +354,7 @@ public class SubscriptionData implements Subscription {
ApiEvent userEV = (ApiEvent) cur;
apiEventType = userEV.getEventType();
switch(apiEventType) {
+ case MIGRATE_ENTITLEMENT:
case CREATE:
nextState = SubscriptionState.ACTIVE;
nextPlanName = userEV.getEventPlan();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
index 59d7c6e..207b4b2 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
@@ -43,12 +43,13 @@ public class SubscriptionFactory {
public SubscriptionData createSubscription(SubscriptionBuilder builder, List<EntitlementEvent> events) {
SubscriptionData subscription = new SubscriptionData(builder, apiService, clock);
- subscription.rebuildTransitions(events, catalogService.getCatalog());
+ if (events.size() > 0) {
+ subscription.rebuildTransitions(events, catalogService.getCatalog());
+ }
return subscription;
}
-
public static class SubscriptionBuilder {
private UUID id;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index aed7937..3eb5dd3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -16,6 +16,10 @@
package com.ning.billing.entitlement.engine.core;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.google.inject.Inject;
import com.ning.billing.config.EntitlementConfig;
import com.ning.billing.entitlement.alignment.PlanAligner;
@@ -23,6 +27,8 @@ import com.ning.billing.entitlement.alignment.TimedPhase;
import com.ning.billing.entitlement.api.EntitlementService;
import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
import com.ning.billing.entitlement.api.test.DefaultEntitlementTestApi;
import com.ning.billing.entitlement.api.test.EntitlementTestApi;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
@@ -39,9 +45,6 @@ import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.eventbus.EventBus;
import com.ning.billing.util.eventbus.EventBus.EventBusException;
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class Engine implements EventListener, EntitlementService {
@@ -60,6 +63,7 @@ public class Engine implements EventListener, EntitlementService {
private final EntitlementUserApi userApi;
private final EntitlementBillingApi billingApi;
private final EntitlementTestApi testApi;
+ private final EntitlementMigrationApi migrationApi;
private final EventBus eventBus;
private boolean startedNotificationThread;
@@ -67,7 +71,8 @@ public class Engine implements EventListener, EntitlementService {
@Inject
public Engine(Clock clock, EntitlementDao dao, EventNotifier apiEventProcessor,
PlanAligner planAligner, EntitlementConfig config, DefaultEntitlementUserApi userApi,
- DefaultEntitlementBillingApi billingApi, DefaultEntitlementTestApi testApi, EventBus eventBus) {
+ DefaultEntitlementBillingApi billingApi, DefaultEntitlementTestApi testApi,
+ DefaultEntitlementMigrationApi migrationApi, EventBus eventBus) {
super();
this.clock = clock;
this.dao = dao;
@@ -76,6 +81,7 @@ public class Engine implements EventListener, EntitlementService {
this.userApi = userApi;
this.testApi = testApi;
this.billingApi = billingApi;
+ this.migrationApi = migrationApi;
this.eventBus = eventBus;
this.startedNotificationThread = false;
@@ -120,6 +126,12 @@ public class Engine implements EventListener, EntitlementService {
}
@Override
+ public EntitlementMigrationApi getMigrationApi() {
+ return migrationApi;
+ }
+
+
+ @Override
public void processEventReady(EntitlementEvent event) {
SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(event.getSubscriptionId());
if (subscription == null) {
@@ -176,7 +188,9 @@ public class Engine implements EventListener, EntitlementService {
try {
DateTime now = clock.getUTCNow();
TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, subscription.getCurrentPlanStart());
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+ null;
if (nextPhaseEvent != null) {
dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent);
}
@@ -184,4 +198,5 @@ public class Engine implements EventListener, EntitlementService {
log.error(String.format("Failed to insert next phase for subscription %s", subscription.getId()), e);
}
}
+
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index 8ce78bb..d2b9f11 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -46,6 +46,9 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
@SqlUpdate
public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle);
+ @SqlUpdate
+ public void removeBundle(@Bind("id") String id);
+
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
public SubscriptionBundle getBundleFromId(@Bind("id") String id);
@@ -58,7 +61,6 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
@Mapper(ISubscriptionBundleSqlMapper.class)
public List<SubscriptionBundle> getBundleFromAccount(@Bind("account_id") String accountId);
-
public static class SubscriptionBundleBinder implements Binder<Bind, SubscriptionBundleData> {
private Date getDate(DateTime dateTime) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index 6c87a40..b118110 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -16,12 +16,12 @@
package com.ning.billing.entitlement.engine.dao;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.events.EntitlementEvent;
-
import java.util.Collection;
import java.util.List;
import java.util.UUID;
@@ -32,6 +32,8 @@ public interface EntitlementDao {
// Bundle apis
public List<SubscriptionBundle> getSubscriptionBundleForAccount(UUID accountId);
+ public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey);
+
public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId);
public SubscriptionBundle createSubscriptionBundle(SubscriptionBundleData bundle);
@@ -68,4 +70,8 @@ public interface EntitlementDao {
public void uncancelSubscription(UUID subscriptionId, List<EntitlementEvent> uncancelEvents);
public void changePlan(UUID subscriptionId, List<EntitlementEvent> changeEvents);
+
+ public void migrate(UUID acountId, AccountMigrationData data);
+
+ public void undoMigration(UUID accountId);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index e676af4..016f005 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -19,7 +19,14 @@ package com.ning.billing.entitlement.engine.dao;
import com.google.inject.Inject;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.config.EntitlementConfig;
-import com.ning.billing.entitlement.api.user.*;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory;
import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -60,6 +67,11 @@ public class EntitlementSqlDao implements EntitlementDao {
}
@Override
+ public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+ return bundlesDao.getBundleFromKey(bundleKey);
+ }
+
+ @Override
public List<SubscriptionBundle> getSubscriptionBundleForAccount(
UUID accountId) {
return bundlesDao.getBundleFromAccount(accountId.toString());
@@ -334,4 +346,64 @@ public class EntitlementSqlDao implements EntitlementDao {
}
return result;
}
+
+ @Override
+ public void migrate(final UUID accountId, final AccountMigrationData accountData) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao transEventDao,
+ TransactionStatus status) throws Exception {
+
+ SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
+ BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+
+ // First get rid of any data from account
+ undoMigrationFromTransaction(accountId, transEventDao, transBundleDao, transSubDao);
+
+ for (BundleMigrationData curBundle : accountData.getData()) {
+ SubscriptionBundleData bundleData = curBundle.getData();
+ for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+ SubscriptionData subData = curSubscription.getData();
+ for (EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+ transEventDao.insertEvent(curEvent);
+ }
+ transSubDao.insertSubscription(subData);
+ }
+ transBundleDao.insertBundle(bundleData);
+ }
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void undoMigration(final UUID accountId) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao transEventDao,
+ TransactionStatus status) throws Exception {
+
+ SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
+ BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+ undoMigrationFromTransaction(accountId, transEventDao, transBundleDao, transSubDao);
+ return null;
+ }
+ });
+ }
+
+ private void undoMigrationFromTransaction(final UUID accountId, EventSqlDao transEventDao, BundleSqlDao transBundleDao, SubscriptionSqlDao transSubDao) {
+ final List<SubscriptionBundle> bundles = transBundleDao.getBundleFromAccount(accountId.toString());
+ for (SubscriptionBundle curBundle : bundles) {
+ List<Subscription> subscriptions = transSubDao.getSubscriptionsFromBundleId(curBundle.getId().toString());
+ for (Subscription cur : subscriptions) {
+ eventsDao.removeEvents(cur.getId().toString());
+ transSubDao.removeSubscription(cur.getId().toString());
+ }
+ transBundleDao.removeBundle(curBundle.getId().toString());
+ }
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 704d722..de61217 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -39,7 +39,6 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
import org.skife.jdbi.v2.tweak.ResultSetMapper;
-
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
@@ -61,6 +60,9 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
public int claimEvent(@Bind("owner") String owner, @Bind("next_available") Date nextAvailable, @Bind("event_id") String eventId, @Bind("now") Date now);
@SqlUpdate
+ public void removeEvents(@Bind("subscription_id") String subscriptionId);
+
+ @SqlUpdate
public void clearEvent(@Bind("event_id") String eventId, @Bind("owner") String owner);
@SqlUpdate
@@ -154,12 +156,9 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
EntitlementEvent result = null;
if (eventType == EventType.PHASE) {
- EventBaseBuilder<PhaseEventBuilder> realBase = (EventBaseBuilder<PhaseEventBuilder>) base;
- result = new PhaseEventData(new PhaseEventBuilder(realBase).setPhaseName(phaseName));
+ result = new PhaseEventData(new PhaseEventBuilder(base).setPhaseName(phaseName));
} else if (eventType == EventType.API_USER) {
-
- EventBaseBuilder<ApiEventBuilder> realBase = (EventBaseBuilder<ApiEventBuilder>) base;
- ApiEventBuilder builder = new ApiEventBuilder(realBase)
+ ApiEventBuilder builder = new ApiEventBuilder(base)
.setEventPlan(planName)
.setEventPlanPhase(phaseName)
.setEventPriceList(priceListName)
@@ -167,6 +166,8 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
if (userType == ApiEventType.CREATE) {
result = new ApiEventCreate(builder);
+ } else if (userType == ApiEventType.MIGRATE_ENTITLEMENT) {
+ result = new ApiEventMigrate(builder);
} else if (userType == ApiEventType.CHANGE) {
result = new ApiEventChange(builder);
} else if (userType == ApiEventType.CANCEL) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 02f651d..d896ebe 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -49,6 +49,9 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
@SqlUpdate
public void insertSubscription(@Bind(binder = ISubscriptionDaoBinder.class) SubscriptionData sub);
+ @SqlUpdate
+ public void removeSubscription(@Bind("id") String id);
+
@SqlQuery
@Mapper(ISubscriptionDaoSqlMapper.class)
public Subscription getSubscriptionFromId(@Bind("id") String id);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
index d0db679..93dc9e1 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
@@ -37,7 +37,7 @@ public abstract class EventBase implements EntitlementEvent {
private DateTime nextAvailableProcessingTime;
private EventLifecycleState processingState;
- public EventBase(EventBaseBuilder builder) {
+ public EventBase(EventBaseBuilder<?> builder) {
this.uuid = builder.getUuid();
this.subscriptionId = builder.getSubscriptionId();
this.requestedDate = builder.getRequestedDate();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventBuilder.java
index 00ac8e2..5847970 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventBuilder.java
@@ -26,7 +26,7 @@ public class PhaseEventBuilder extends EventBaseBuilder<PhaseEventBuilder> {
super();
}
- public PhaseEventBuilder(EventBaseBuilder<PhaseEventBuilder> base) {
+ public PhaseEventBuilder(EventBaseBuilder<?> base) {
super(base);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
index be0ab6c..9744363 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
@@ -56,15 +56,15 @@ public class PhaseEventData extends EventBase implements PhaseEvent {
+ ", isActive()=" + isActive() + "]\n";
}
- public static final PhaseEvent getNextPhaseEvent(TimedPhase nextTimedPhase, SubscriptionData subscription, DateTime now) {
- return (nextTimedPhase == null) ?
+ public static final PhaseEvent getNextPhaseEvent(String phaseName, SubscriptionData subscription, DateTime now, DateTime effectiveDate) {
+ return (phaseName == null) ?
null :
new PhaseEventData(new PhaseEventBuilder()
.setSubscriptionId(subscription.getId())
.setRequestedDate(now)
- .setEffectiveDate(nextTimedPhase.getStartPhase())
+ .setEffectiveDate(effectiveDate)
.setProcessedDate(now)
.setActiveVersion(subscription.getActiveVersion())
- .setPhaseName(nextTimedPhase.getPhase().getName()));
+ .setPhaseName(phaseName));
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
index cd3857e..2ff026b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
@@ -29,7 +29,7 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
super();
}
- public ApiEventBuilder(EventBaseBuilder<ApiEventBuilder> base) {
+ public ApiEventBuilder(EventBaseBuilder<?> base) {
super(base);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventMigrate.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventMigrate.java
new file mode 100644
index 0000000..636a940
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventMigrate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.events.user;
+
+public class ApiEventMigrate extends ApiEventBase {
+
+ public ApiEventMigrate(ApiEventBuilder builder) {
+ super(builder.setEventType(ApiEventType.MIGRATE_ENTITLEMENT));
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index c28941e..b994899 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -20,6 +20,10 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition.Subscription
public enum ApiEventType {
+ MIGRATE_ENTITLEMENT {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.MIGRATE_ENTITLEMENT; }
+ },
CREATE {
@Override
public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CREATE; }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index 1a9fffc..ab04700 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -18,10 +18,13 @@ package com.ning.billing.entitlement.glue;
import com.google.inject.AbstractModule;
import com.ning.billing.config.EntitlementConfig;
+import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.api.EntitlementService;
import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
import com.ning.billing.entitlement.api.test.DefaultEntitlementTestApi;
import com.ning.billing.entitlement.api.test.EntitlementTestApi;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
@@ -63,9 +66,11 @@ public class EntitlementModule extends AbstractModule {
bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
bind(Engine.class).asEagerSingleton();
bind(PlanAligner.class).asEagerSingleton();
+ bind(MigrationPlanAligner.class).asEagerSingleton();
bind(EntitlementTestApi.class).to(DefaultEntitlementTestApi.class).asEagerSingleton();
bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
+ bind(EntitlementMigrationApi.class).to(DefaultEntitlementMigrationApi.class).asEagerSingleton();
}
@Override
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index f79666e..4540ad8 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -3,7 +3,7 @@ CREATE TABLE events (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
event_id char(36) NOT NULL,
event_type varchar(9) NOT NULL,
- user_type varchar(10) DEFAULT NULL,
+ user_type varchar(25) DEFAULT NULL,
created_dt datetime NOT NULL,
updated_dt datetime NOT NULL,
requested_dt datetime NOT NULL,
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index cb15e9b..d9f26c2 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -14,6 +14,12 @@ insertBundle() ::= <<
);
>>
+removeBundle(id) ::= <<
+ delete from bundles
+ where
+ id = :id
+ ;
+>>
getBundleFromId(id) ::= <<
select
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index a3b677a..31e9eaf 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -96,6 +96,13 @@ insertEvent() ::= <<
);
>>
+removeEvents(subscription_id) ::= <<
+ delete from events
+ where
+ subscription_id = :subscription_id
+ ;
+>>
+
insertClaimedHistory(sequence_id, owner_id, hostname, claimed_dt, event_id) ::= <<
insert into claimed_events (
sequence_id
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index 094bdf1..bb7400b 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -22,6 +22,12 @@ insertSubscription() ::= <<
);
>>
+removeSubscription(id) ::= <<
+ delete from subscriptions
+ where id = :id
+ ;
+>>
+
getSubscriptionFromId(id) ::= <<
select
id
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
index f76f775..6bee471 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
@@ -36,6 +36,7 @@ public class ApiTestListener {
private volatile boolean completed;
public enum NextEvent {
+ MIGRATE_ENTITLEMENT,
CREATE,
CHANGE,
CANCEL,
@@ -52,6 +53,9 @@ public class ApiTestListener {
@Subscribe
public void handleEntitlementEvent(SubscriptionTransition event) {
switch (event.getTransitionType()) {
+ case MIGRATE_ENTITLEMENT:
+ subscriptionMigrated(event);
+ break;
case CREATE:
subscriptionCreated(event);
break;
@@ -135,6 +139,12 @@ public class ApiTestListener {
}
+ public void subscriptionMigrated(SubscriptionTransition migrated) {
+ log.debug("-> Got event MIGRATED");
+ assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
+ notifyIfStackEmpty();
+ }
+
public void subscriptionCreated(SubscriptionTransition created) {
log.debug("-> Got event CREATED");
assertEqualsNicely(NextEvent.CREATE);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
new file mode 100644
index 0000000..271de3c
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApiException;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementAccountMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementBundleMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigrationCase;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public abstract class TestMigration extends TestApiBase {
+
+
+ public void testSingleBasePlan() {
+
+ try {
+ DateTime beforeMigration = clock.getUTCNow();
+ EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlan();
+ DateTime afterMigration = clock.getUTCNow();
+
+ testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ migrationApi.migrate(toBeMigrated);
+ assertTrue(testListener.isCompleted(5000));
+
+ List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+ assertEquals(bundles.size(), 1);
+ SubscriptionBundle bundle = bundles.get(0);
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+ assertEquals(subscriptions.size(), 1);
+ Subscription subscription = subscriptions.get(0);
+ assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+ assertEquals(subscription.getEndDate(), null);
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
+ } catch (EntitlementMigrationApiException e) {
+ Assert.fail("", e);
+ }
+ }
+
+
+ public void testSingleBasePlanFutureCancelled() {
+
+ try {
+
+ DateTime beforeMigration = clock.getUTCNow();
+ EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanFutreCancelled();
+ DateTime afterMigration = clock.getUTCNow();
+
+ testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ migrationApi.migrate(toBeMigrated);
+ assertTrue(testListener.isCompleted(5000));
+
+ List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+ assertEquals(bundles.size(), 1);
+ SubscriptionBundle bundle = bundles.get(0);
+ //assertEquals(bundle.getStartDate(), effectiveDate);
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+ assertEquals(subscriptions.size(), 1);
+ Subscription subscription = subscriptions.get(0);
+ assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
+
+ testListener.pushExpectedEvent(NextEvent.CANCEL);
+ Duration oneYear = getDurationYear(1);
+ clock.setDeltaFromReality(oneYear, 0);
+ assertTrue(testListener.isCompleted(5000));
+
+ assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+ assertNotNull(subscription.getEndDate());
+ assertTrue(subscription.getEndDate().isAfterNow());
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(subscription.getCurrentPhase(), null);
+ assertEquals(subscription.getState(), SubscriptionState.CANCELLED);
+ assertNull(subscription.getCurrentPlan());
+
+ } catch (EntitlementMigrationApiException e) {
+ Assert.fail("", e);
+ }
+ }
+
+ public void testSingleBasePlanWithPendingPhase() {
+
+ try {
+ DateTime beforeMigration = clock.getUTCNow();
+ final DateTime trialDate = clock.getUTCNow().minusDays(10);
+ EntitlementAccountMigration toBeMigrated = createAccountFuturePendingPhase(trialDate);
+ DateTime afterMigration = clock.getUTCNow();
+
+ testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ migrationApi.migrate(toBeMigrated);
+ assertTrue(testListener.isCompleted(5000));
+
+ List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+ assertEquals(bundles.size(), 1);
+ SubscriptionBundle bundle = bundles.get(0);
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+ assertEquals(subscriptions.size(), 1);
+ Subscription subscription = subscriptions.get(0);
+
+ assertEquals(subscription.getStartDate(), trialDate);
+ assertEquals(subscription.getEndDate(), null);
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.TRIAL);
+ assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
+
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ Duration thirtyDays = getDurationDay(30);
+ clock.setDeltaFromReality(thirtyDays, 0);
+ assertTrue(testListener.isCompleted(5000));
+
+ assertEquals(subscription.getStartDate(), trialDate);
+ assertEquals(subscription.getEndDate(), null);
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
+ assertEquals(subscription.getCurrentPhase().getName(), "assault-rifle-monthly-evergreen");
+
+ } catch (EntitlementMigrationApiException e) {
+ Assert.fail("", e);
+ }
+ }
+
+
+ public void testSingleBasePlanWithPendingChange() {
+
+ try {
+ DateTime beforeMigration = clock.getUTCNow();
+ EntitlementAccountMigration toBeMigrated = createAccountFuturePendingChange();
+ DateTime afterMigration = clock.getUTCNow();
+
+ testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ migrationApi.migrate(toBeMigrated);
+ assertTrue(testListener.isCompleted(5000));
+
+ List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+ assertEquals(bundles.size(), 1);
+ SubscriptionBundle bundle = bundles.get(0);
+
+ List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+ assertEquals(subscriptions.size(), 1);
+ Subscription subscription = subscriptions.get(0);
+ assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+ assertEquals(subscription.getEndDate(), null);
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+ assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
+
+ testListener.pushExpectedEvent(NextEvent.CHANGE);
+ Duration oneMonth = getDurationMonth(1);
+ clock.setDeltaFromReality(oneMonth, 0);
+ assertTrue(testListener.isCompleted(5000));
+
+ assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+ assertEquals(subscription.getEndDate(), null);
+ assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(subscription.getCurrentPlan().getName(), "shotgun-annual");
+
+ } catch (EntitlementMigrationApiException e) {
+ Assert.fail("", e);
+ }
+ }
+
+
+ private EntitlementAccountMigration createAccountWithSingleBasePlan(final List<EntitlementSubscriptionMigrationCase> cases) {
+
+ return new EntitlementAccountMigration() {
+
+ private final UUID accountId = UUID.randomUUID();
+
+ @Override
+ public EntitlementBundleMigration[] getBundles() {
+ List<EntitlementBundleMigration> bundles = new ArrayList<EntitlementBundleMigration>();
+ EntitlementBundleMigration bundle0 = new EntitlementBundleMigration() {
+
+ @Override
+ public EntitlementSubscriptionMigration[] getSubscriptions() {
+ EntitlementSubscriptionMigration subscription = new EntitlementSubscriptionMigration() {
+ @Override
+ public EntitlementSubscriptionMigrationCase[] getSubscriptionCases() {
+ return cases.toArray(new EntitlementSubscriptionMigrationCase[cases.size()]);
+ }
+ @Override
+ public ProductCategory getCategory() {
+ return ProductCategory.BASE;
+ }
+ };
+ EntitlementSubscriptionMigration[] result = new EntitlementSubscriptionMigration[1];
+ result[0] = subscription;
+ return result;
+ }
+ @Override
+ public String getBundleKey() {
+ return "12345";
+ }
+ };
+ bundles.add(bundle0);
+ return bundles.toArray(new EntitlementBundleMigration[bundles.size()]);
+ }
+
+ @Override
+ public UUID getAccountKey() {
+ return accountId;
+ }
+ };
+ }
+
+ private EntitlementAccountMigration createAccountWithRegularBasePlan() {
+ List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+ cases.add(new EntitlementSubscriptionMigrationCase() {
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+ return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return clock.getUTCNow().minusMonths(3);
+ }
+ @Override
+ public DateTime getCancelledDate() {
+ return null;
+ }
+ });
+ return createAccountWithSingleBasePlan(cases);
+ }
+
+ private EntitlementAccountMigration createAccountWithRegularBasePlanFutreCancelled() {
+ List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+ final DateTime effectiveDate = clock.getUTCNow().minusMonths(3);
+ cases.add(new EntitlementSubscriptionMigrationCase() {
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+ return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+ @Override
+ public DateTime getCancelledDate() {
+ return effectiveDate.plusYears(1);
+ }
+ });
+ return createAccountWithSingleBasePlan(cases);
+ }
+
+
+ private EntitlementAccountMigration createAccountFuturePendingPhase(final DateTime trialDate) {
+ List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+ cases.add(new EntitlementSubscriptionMigrationCase() {
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+ return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return trialDate;
+ }
+ @Override
+ public DateTime getCancelledDate() {
+ return trialDate.plusDays(30);
+ }
+ });
+ cases.add(new EntitlementSubscriptionMigrationCase() {
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+ return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return trialDate.plusDays(30);
+ }
+ @Override
+ public DateTime getCancelledDate() {
+ return null;
+ }
+ });
+ return createAccountWithSingleBasePlan(cases);
+ }
+
+ private EntitlementAccountMigration createAccountFuturePendingChange() {
+ List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+ final DateTime effectiveDate = clock.getUTCNow().minusDays(10);
+ cases.add(new EntitlementSubscriptionMigrationCase() {
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+ return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+ @Override
+ public DateTime getCancelledDate() {
+ return effectiveDate.plusMonths(1);
+ }
+ });
+ cases.add(new EntitlementSubscriptionMigrationCase() {
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+ return new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDate.plusMonths(1).plusDays(1);
+ }
+ @Override
+ public DateTime getCancelledDate() {
+ return null;
+ }
+ });
+ return createAccountWithSingleBasePlan(cases);
+ }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java
new file mode 100644
index 0000000..05d6fb5
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
+
+public class TestMigrationMemory extends TestMigration {
+ @Override
+ protected Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleMemory());
+ }
+
+ @Override
+ @Test(enabled=true, groups="fast")
+ public void testSingleBasePlan() {
+ super.testSingleBasePlan();
+ }
+
+ @Override
+ @Test(enabled=true, groups="fast")
+ public void testSingleBasePlanFutureCancelled() {
+ super.testSingleBasePlanFutureCancelled();
+ }
+
+ @Override
+ @Test(enabled=true, groups="fast")
+ public void testSingleBasePlanWithPendingPhase() {
+ super.testSingleBasePlanWithPendingPhase();
+ }
+
+ @Override
+ @Test(enabled=true, groups="fast")
+ public void testSingleBasePlanWithPendingChange() {
+ super.testSingleBasePlanWithPendingChange();
+ }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
new file mode 100644
index 0000000..b3eb168
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you 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 com.ning.billing.entitlement.api.migration;
+
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestMigrationSql extends TestMigration {
+
+ @Override
+ protected Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+ }
+
+ @Override
+ @Test(enabled=true, groups="sql")
+ public void testSingleBasePlan() {
+ super.testSingleBasePlan();
+ }
+
+ @Override
+ @Test(enabled=true, groups="sql")
+ public void testSingleBasePlanFutureCancelled() {
+ super.testSingleBasePlanFutureCancelled();
+ }
+
+ @Override
+ @Test(enabled=true, groups="sql")
+ public void testSingleBasePlanWithPendingPhase() {
+ super.testSingleBasePlanWithPendingPhase();
+ }
+
+ @Override
+ @Test(enabled=true, groups="sql")
+ public void testSingleBasePlanWithPendingChange() {
+ super.testSingleBasePlanWithPendingChange();
+ }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
index 72e5556..3326bc8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
@@ -16,19 +16,29 @@
package com.ning.billing.entitlement.api.user;
-import com.ning.billing.catalog.api.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.util.clock.DefaultClock;
import org.joda.time.DateTime;
import org.testng.Assert;
-import java.util.List;
-import static org.testng.Assert.*;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.entitlement.api.TestApiBase;
+import java.util.List;
-public abstract class TestUserApiCancel extends TestUserApiBase {
+public abstract class TestUserApiCancel extends TestApiBase {
- protected void testCancelSubscriptionIMMReal() {
+ protected void testCancelSubscriptionIMM() {
log.info("Starting testCancelSubscriptionIMM");
@@ -70,7 +80,7 @@ public abstract class TestUserApiCancel extends TestUserApiBase {
}
- protected void testCancelSubscriptionEOTWithChargeThroughDateReal() {
+ protected void testCancelSubscriptionEOTWithChargeThroughDate() {
log.info("Starting testCancelSubscriptionEOTWithChargeThroughDate");
try {
@@ -122,7 +132,7 @@ public abstract class TestUserApiCancel extends TestUserApiBase {
}
- protected void testCancelSubscriptionEOTWithNoChargeThroughDateReal() {
+ protected void testCancelSubscriptionEOTWithNoChargeThroughDate() {
log.info("Starting testCancelSubscriptionEOTWithNoChargeThroughDate");
@@ -166,7 +176,7 @@ public abstract class TestUserApiCancel extends TestUserApiBase {
// Similar test to testCancelSubscriptionEOTWithChargeThroughDate except we uncancel and check things
// are as they used to be and we can move forward without hitting cancellation
//
- protected void testUncancelReal() {
+ protected void testUncancel() {
log.info("Starting testUncancel");
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
index 962a6ef..630d925 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
@@ -30,23 +30,27 @@ public class TestUserApiCancelMemory extends TestUserApiCancel {
return Guice.createInjector(Stage.PRODUCTION, new MockEngineModuleMemory());
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testCancelSubscriptionIMM() {
- invokeRealMethod(this);
+ super.testCancelSubscriptionIMM();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testCancelSubscriptionEOTWithChargeThroughDate() {
- invokeRealMethod(this);
+ super.testCancelSubscriptionEOTWithChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testCancelSubscriptionEOTWithNoChargeThroughDate() {
- invokeRealMethod(this);
+ super.testCancelSubscriptionEOTWithNoChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testUncancel() {
- invokeRealMethod(this);
+ super.testUncancel();
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
index 682fa06..187c080 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
@@ -47,24 +47,28 @@ public class TestUserApiCancelSql extends TestUserApiCancel {
}
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testCancelSubscriptionIMM() {
- invokeRealMethod(this);
+ super.testCancelSubscriptionIMM();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testCancelSubscriptionEOTWithChargeThroughDate() {
- invokeRealMethod(this);
+ super.testCancelSubscriptionEOTWithChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testCancelSubscriptionEOTWithNoChargeThroughDate() {
- invokeRealMethod(this);
+ super.testCancelSubscriptionEOTWithNoChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testUncancel() {
- invokeRealMethod(this);
+ super.testUncancel();
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 76f6d44..dc6567f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -16,20 +16,32 @@
package com.ning.billing.entitlement.api.user;
-import com.ning.billing.catalog.api.*;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
-import com.ning.billing.entitlement.events.EntitlementEvent;
-import com.ning.billing.entitlement.events.user.ApiEvent;
-import com.ning.billing.util.clock.DefaultClock;
-import org.joda.time.DateTime;
-import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
-import static org.testng.Assert.*;
+import org.joda.time.DateTime;
+import org.testng.Assert;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.TestApiBase;
+
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.user.ApiEvent;
+import com.ning.billing.util.clock.DefaultClock;
-public abstract class TestUserApiChangePlan extends TestUserApiBase {
+public abstract class TestUserApiChangePlan extends TestApiBase {
@@ -49,7 +61,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
- protected void testChangePlanBundleAlignEOTWithNoChargeThroughDateReal() {
+ protected void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
tChangePlanBundleAlignEOTWithNoChargeThroughDate("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
}
@@ -88,7 +100,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
}
- protected void testChangePlanBundleAlignEOTWithChargeThroughDateReal() {
+ protected void testChangePlanBundleAlignEOTWithChargeThroughDate() {
testChangePlanBundleAlignEOTWithChargeThroughDate("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", "Pistol", BillingPeriod.ANNUAL, "gunclubDiscount");
}
@@ -153,7 +165,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
}
- protected void testChangePlanBundleAlignIMMReal() {
+ protected void testChangePlanBundleAlignIMM() {
tChangePlanBundleAlignIMM("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
}
@@ -195,7 +207,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
}
- protected void testChangePlanChangePlanAlignEOTWithChargeThroughDateReal() {
+ protected void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
tChangePlanChangePlanAlignEOTWithChargeThroughDate("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, "Assault-Rifle", BillingPeriod.ANNUAL, "rescue");
}
@@ -276,7 +288,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
}
}
- protected void testMultipleChangeLastIMMReal() {
+ protected void testMultipleChangeLastIMM() {
try {
SubscriptionData subscription = createSubscription("Assault-Rifle", BillingPeriod.MONTHLY, "gunclubDiscount");
@@ -323,7 +335,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
}
}
- protected void testMultipleChangeLastEOTReal() {
+ protected void testMultipleChangeLastEOT() {
try {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index 173239c..aecaaac 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -30,35 +30,40 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
- invokeRealMethod(this);
+ super.testChangePlanBundleAlignEOTWithNoChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testChangePlanBundleAlignEOTWithChargeThroughDate() {
- invokeRealMethod(this);
+ super.testChangePlanBundleAlignEOTWithChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testChangePlanBundleAlignIMM() {
- invokeRealMethod(this);
+ super.testChangePlanBundleAlignIMM();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testMultipleChangeLastIMM() {
- invokeRealMethod(this);
+ super.testMultipleChangeLastIMM();
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testMultipleChangeLastEOT() {
- invokeRealMethod(this);
+ super.testMultipleChangeLastEOT();
}
// Set to false until we implement rescue example.
+ @Override
@Test(enabled=false, groups={"fast"})
public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
- invokeRealMethod(this);
+ super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
}
-
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index 255e894..f5ad803 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -52,35 +52,41 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
}
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
- invokeRealMethod(this);
+ super.testChangePlanBundleAlignEOTWithNoChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testChangePlanBundleAlignEOTWithChargeThroughDate() {
- invokeRealMethod(this);
+ super.testChangePlanBundleAlignEOTWithChargeThroughDate();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testChangePlanBundleAlignIMM() {
- invokeRealMethod(this);
+ super.testChangePlanBundleAlignIMM();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testMultipleChangeLastIMM() {
- invokeRealMethod(this);
+ super.testMultipleChangeLastIMM();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testMultipleChangeLastEOT() {
- invokeRealMethod(this);
+ super.testMultipleChangeLastEOT();
}
// rescue not implemented yet
+ @Override
@Test(enabled=false, groups={"sql"})
public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
- invokeRealMethod(this);
+ super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index 365374e..ff55ca0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -16,23 +16,31 @@
package com.ning.billing.entitlement.api.user;
-import com.ning.billing.catalog.api.*;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import org.joda.time.DateTime;
+import org.testng.Assert;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.TestApiBase;
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.phase.PhaseEvent;
import com.ning.billing.util.clock.DefaultClock;
-import org.joda.time.DateTime;
-import org.testng.Assert;
-
-import java.util.List;
-import static org.testng.Assert.*;
+public abstract class TestUserApiCreate extends TestApiBase {
-public abstract class TestUserApiCreate extends TestUserApiBase {
-
-
- protected void testCreateWithRequestedDateReal() {
+ public void testCreateWithRequestedDate() {
log.info("Starting testCreateWithRequestedDate");
try {
@@ -48,7 +56,8 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
testListener.pushExpectedEvent(NextEvent.CREATE);
- SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(), productName, term, planSetName, null, requestedDate);
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+ getProductSpecifier(productName, planSetName, term, null), requestedDate);
assertNotNull(subscription);
assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
@@ -63,7 +72,7 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
}
}
- protected void testCreateWithInitialPhaseReal() {
+ protected void testCreateWithInitialPhase() {
log.info("Starting testCreateWithInitialPhase");
try {
@@ -76,7 +85,8 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
testListener.pushExpectedEvent(NextEvent.CREATE);
- SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(), productName, term, planSetName, PhaseType.EVERGREEN, clock.getUTCNow());
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+ getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), clock.getUTCNow());
assertNotNull(subscription);
assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
@@ -102,7 +112,7 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
}
}
- protected void testSimpleCreateSubscriptionReal() {
+ protected void testSimpleCreateSubscription() {
log.info("Starting testSimpleCreateSubscription");
try {
@@ -115,7 +125,9 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
testListener.pushExpectedEvent(NextEvent.CREATE);
- SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(), productName, term, planSetName, null, clock.getUTCNow());
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+ getProductSpecifier(productName, planSetName, term, null),
+ clock.getUTCNow());
assertNotNull(subscription);
assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
@@ -166,7 +178,7 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
}
- protected void testSimpleSubscriptionThroughPhasesReal() {
+ protected void testSimpleSubscriptionThroughPhases() {
log.info("Starting testSimpleSubscriptionThroughPhases");
try {
@@ -180,7 +192,8 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
testListener.pushExpectedEvent(NextEvent.CREATE);
// CREATE SUBSCRIPTION
- SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(), productName, term, planSetName, null, clock.getUTCNow());
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+ getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow());
assertNotNull(subscription);
PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -216,7 +229,7 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
}
}
- protected void testSubscriptionWithAddOnReal() {
+ protected void testSubscriptionWithAddOn() {
log.info("Starting testSubscriptionWithAddOn");
try {
@@ -227,7 +240,8 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
testListener.pushExpectedEvent(NextEvent.CREATE);
- SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(), productName, term, planSetName, null, clock.getUTCNow());
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+ getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow());
assertNotNull(subscription);
} catch (EntitlementUserApiException e) {
@@ -236,4 +250,5 @@ public abstract class TestUserApiCreate extends TestUserApiBase {
}
+
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
index bff20db..f4474f9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
@@ -30,29 +30,30 @@ public class TestUserApiCreateMemory extends TestUserApiCreate {
return Guice.createInjector(Stage.PRODUCTION, new MockEngineModuleMemory());
}
+ @Override
@Test(enabled=true, groups={"fast"})
public void testCreateWithRequestedDate() {
- invokeRealMethod(this);
+ super.testCreateWithRequestedDate();
}
@Test(enabled=true, groups={"fast"})
public void testCreateWithInitialPhase() {
- invokeRealMethod(this);
+ super.testSimpleSubscriptionThroughPhases();
}
@Test(enabled=true, groups={"fast"})
public void testSimpleCreateSubscription() {
- invokeRealMethod(this);
+ super.testSimpleCreateSubscription();
}
@Test(enabled=true, groups={"fast"})
protected void testSimpleSubscriptionThroughPhases() {
- invokeRealMethod(this);
+ super.testSimpleSubscriptionThroughPhases();
}
@Test(enabled=false, groups={"fast"})
protected void testSubscriptionWithAddOn() {
- invokeRealMethod(this);
+ super.testSubscriptionWithAddOn();
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java
index 25996e2..8170b6d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateSql.java
@@ -29,29 +29,34 @@ public class TestUserApiCreateSql extends TestUserApiCreate {
return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testCreateWithRequestedDate() {
- invokeRealMethod(this);
+ super.testCreateWithRequestedDate();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testCreateWithInitialPhase() {
- invokeRealMethod(this);
+ super.testCreateWithInitialPhase();
}
+ @Override
@Test(enabled=true, groups={"sql"})
public void testSimpleCreateSubscription() {
- invokeRealMethod(this);
+ super.testSimpleCreateSubscription();
}
+ @Override
@Test(enabled=true, groups={"sql"})
protected void testSimpleSubscriptionThroughPhases() {
- invokeRealMethod(this);
+ super.testSimpleSubscriptionThroughPhases();
}
+ @Override
@Test(enabled=false, groups={"sql"})
protected void testSubscriptionWithAddOn() {
- invokeRealMethod(this);
+ super.testSubscriptionWithAddOn();
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
index 91ac96f..4a8b4b1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
@@ -19,7 +19,15 @@ package com.ning.billing.entitlement.api.user;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
-import com.ning.billing.catalog.api.*;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.TestApiBase;
+
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.entitlement.glue.MockEngineModuleSql;
import com.ning.billing.util.clock.DefaultClock;
@@ -33,7 +41,7 @@ import java.util.UUID;
import static org.testng.Assert.*;
-public class TestUserApiDemos extends TestUserApiBase {
+public class TestUserApiDemos extends TestApiBase {
@Override
protected Injector getInjector() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index d683244..c32b654 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -24,6 +24,7 @@ import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Duration;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.entitlement.api.TestApiBase;
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
import com.ning.billing.util.clock.DefaultClock;
@@ -36,7 +37,7 @@ import java.util.UUID;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
-public class TestUserApiError extends TestUserApiBase {
+public class TestUserApiError extends TestApiBase {
@Override
@@ -86,7 +87,9 @@ public class TestUserApiError extends TestUserApiBase {
private void tCreateSubscriptionInternal(UUID bundleId, String productName,
BillingPeriod term, String planSet, ErrorCode expected) {
try {
- entitlementApi.createSubscription(bundleId, productName, term, planSet, null, clock.getUTCNow());
+ entitlementApi.createSubscription(bundleId,
+ getProductSpecifier(productName, planSet, term, null),
+ clock.getUTCNow());
assertFalse(true);
} catch (EntitlementUserApiException e) {
assertEquals(e.getCode(), expected.getCode());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiPriceList.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiPriceList.java
index d6a2b12..a1a0dc7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiPriceList.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiPriceList.java
@@ -17,8 +17,9 @@
package com.ning.billing.entitlement.api.user;
import com.google.inject.Injector;
+import com.ning.billing.entitlement.api.TestApiBase;
-public class TestUserApiPriceList extends TestUserApiBase {
+public class TestUserApiPriceList extends TestApiBase {
@Override
protected Injector getInjector() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
index 7b600dd..84d1031 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
@@ -23,7 +23,9 @@ import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Duration;
import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.PlanPhase;
+
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.TestApiBase;
import com.ning.billing.entitlement.glue.MockEngineModuleSql;
import com.ning.billing.util.clock.DefaultClock;
import org.joda.time.DateTime;
@@ -32,7 +34,7 @@ import org.testng.annotations.Test;
import static org.testng.Assert.*;
-public class TestUserApiScenarios extends TestUserApiBase {
+public class TestUserApiScenarios extends TestApiBase {
@Override
protected Injector getInjector() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index e9ff1bd..7fc8342 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -20,7 +20,16 @@ import com.google.inject.Inject;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.catalog.api.TimeUnit;
import com.ning.billing.config.EntitlementConfig;
-import com.ning.billing.entitlement.api.user.*;
+
+import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+
import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -86,6 +95,17 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
}
@Override
+ public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+ for (SubscriptionBundle cur : bundles) {
+ if (cur.getKey().equals(bundleKey)) {
+ return cur;
+ }
+ }
+ return null;
+ }
+
+
+ @Override
public SubscriptionBundle createSubscriptionBundle(SubscriptionBundleData bundle) {
bundles.add(bundle);
return getSubscriptionBundleFromId(bundle.getId());
@@ -333,4 +353,44 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
}
}
+
+ @Override
+ public void migrate(final UUID accountId, final AccountMigrationData accountData) {
+ synchronized(events) {
+
+ undoMigration(accountId);
+
+ for (BundleMigrationData curBundle : accountData.getData()) {
+ SubscriptionBundleData bundleData = curBundle.getData();
+ for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+ SubscriptionData subData = curSubscription.getData();
+ for (EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+ events.add(curEvent);
+ }
+ subscriptions.add(subData);
+ }
+ bundles.add(bundleData);
+ }
+ }
+ }
+
+ @Override
+ public void undoMigration(UUID accountId) {
+ synchronized(events) {
+
+ List<SubscriptionBundle> allBundles = getSubscriptionBundleForAccount(accountId);
+ for (SubscriptionBundle bundle : allBundles) {
+ List<Subscription> allSubscriptions = getSubscriptions(bundle.getId());
+ for (Subscription subscription : allSubscriptions) {
+ List<EntitlementEvent> allEvents = getEventsForSubscription(subscription.getId());
+ for (EntitlementEvent event : allEvents) {
+ events.remove(event);
+ }
+ subscriptions.remove(subscription);
+ }
+ bundles.remove(bundle);
+ }
+ }
+
+ }
}
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 8428ef0..3d7dc27 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 1186456..20f33c7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -16,6 +16,8 @@
package com.ning.billing.invoice.dao;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
import com.google.inject.Inject;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceCreationNotification;
@@ -40,7 +42,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
private final static Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
@Inject
- public DefaultInvoiceDao(IDBI dbi, EventBus eventBus) {
+ public DefaultInvoiceDao(final IDBI dbi, final EventBus eventBus) {
this.invoiceDao = dbi.onDemand(InvoiceSqlDao.class);
this.eventBus = eventBus;
}
@@ -54,7 +56,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
public List<Invoice> get() {
return invoiceDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
@Override
- public List<Invoice> inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+ public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
List<Invoice> invoices = invoiceDao.get();
InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
@@ -72,7 +74,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
public Invoice getById(final String invoiceId) {
return invoiceDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
@Override
- public Invoice inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+ public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
Invoice invoice = invoiceDao.getById(invoiceId);
if (invoice != null) {
@@ -87,25 +89,24 @@ public class DefaultInvoiceDao implements InvoiceDao {
}
@Override
- public void save(final Invoice invoice) {
+ public void create(final Invoice invoice) {
invoiceDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
@Override
- public Void inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+ public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
Invoice currentInvoice = invoiceDao.getById(invoice.getId().toString());
- invoiceDao.save(invoice);
-
- List<InvoiceItem> invoiceItems = invoice.getItems();
- InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
- invoiceItemDao.save(invoiceItems);
if (currentInvoice == null) {
+ invoiceDao.create(invoice);
+
+ List<InvoiceItem> invoiceItems = invoice.getItems();
+ InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
+ invoiceItemDao.create(invoiceItems);
+
InvoiceCreationNotification event;
event = new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
invoice.getAmountOutstanding(), invoice.getCurrency(),
invoice.getInvoiceDate());
eventBus.post(event);
- } else {
-
}
return null;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index b59e4ed..e9306b2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -23,7 +23,7 @@ import java.util.UUID;
import com.ning.billing.invoice.api.Invoice;
public interface InvoiceDao {
- void save(Invoice invoice);
+ void create(Invoice invoice);
Invoice getById(final String id);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
index a89749e..6f4e47b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -50,10 +50,14 @@ public interface InvoiceItemSqlDao extends EntityDao<InvoiceItem> {
@Override
@SqlUpdate
- void save(@InvoiceItemBinder final InvoiceItem invoiceItem);
+ void create(@InvoiceItemBinder final InvoiceItem invoiceItem);
+
+ @Override
+ @SqlUpdate
+ void update(@InvoiceItemBinder final InvoiceItem invoiceItem);
@SqlBatch
- void save(@InvoiceItemBinder final List<InvoiceItem> items);
+ void create(@InvoiceItemBinder final List<InvoiceItem> items);
@BindingAnnotation(InvoiceItemBinder.InvoiceItemBinderFactory.class)
@Retention(RetentionPolicy.RUNTIME)
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index aa0051a..9ede145 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -53,7 +53,11 @@ import java.util.UUID;
public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
@Override
@SqlUpdate
- void save(@InvoiceBinder Invoice invoice);
+ void create(@InvoiceBinder Invoice invoice);
+
+ @Override
+ @SqlUpdate
+ void update(@InvoiceBinder Invoice invoice);
@SqlQuery
List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 797f587..97f82e0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
@@ -126,6 +126,10 @@ public class DefaultInvoice implements Invoice {
@Override
public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
+ if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+ return false;
+ }
+
if (lastPaymentAttempt == null) {
return true;
}
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 490d075..4dc783f 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -25,12 +25,16 @@ getInvoiceItemsBySubscription() ::= <<
WHERE subscription_id = :subscriptionId;
>>
-save() ::= <<
+create() ::= <<
INSERT INTO invoice_items(id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency)
- VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency)
- ON DUPLICATE KEY UPDATE
- start_date = :startDate, end_date = :endDate, description = :description,
- amount = :amount, rate = :rate, currency = :currency''
+ VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency);
+>>
+
+update() ::= <<
+ UPDATE invoice_items
+ SET invoice_id = :invoiceId, subscription_id = :subscriptionId, start_date = :startDate, end_date = :endDate,
+ description = :description, amount = :amount, rate = :rate, currency = :currency
+ WHERE id = :id;
>>
test() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 0323040..8766e18 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -52,11 +52,15 @@ getById() ::= <<
GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency;
>>
-save() ::= <<
+create() ::= <<
INSERT INTO invoices(id, account_id, invoice_date, target_date, currency)
- VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency)
- ON DUPLICATE KEY UPDATE
- invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency;
+ VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency);
+>>
+
+update() ::= <<
+ UPDATE invoices
+ SET account_id = :accountId, invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency
+ WHERE id = :id;
>>
notifySuccessfulPayment() ::= <<
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 0666ada..3fe72ed 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -45,7 +45,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice invoice = new DefaultInvoice(accountId, new DefaultClock().getUTCNow(), Currency.USD);
DateTime invoiceDate = invoice.getInvoiceDate();
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString());
assertNotNull(invoices);
@@ -68,7 +68,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
DateTime endDate = new DateTime(2010, 4, 1, 0, 0, 0, 0);
InvoiceItem invoiceItem = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
invoice.add(invoiceItem);
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
Invoice savedInvoice = invoiceDao.getById(invoiceId.toString());
assertNotNull(savedInvoice);
@@ -105,12 +105,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
BigDecimal paymentAmount = new BigDecimal("14.0");
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
invoiceDao.notifySuccessfulPayment(invoice.getId().toString(), paymentAmount, Currency.USD.toString(), paymentId, paymentAttemptDate.toDate());
invoice = invoiceDao.getById(invoice.getId().toString());
-// assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
-// assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+ assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
+ assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
}
@Test
@@ -121,11 +121,11 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), paymentAttemptDate.toDate());
invoice = invoiceDao.getById(invoice.getId().toString());
-// assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+ assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
}
@Test
@@ -140,7 +140,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
UUID accountId = UUID.randomUUID();
Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
assertEquals(invoices.size(), existingInvoiceCount);
}
@@ -163,7 +163,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, targetDate, endDate, "test", amount, rate, Currency.USD);
invoice.add(item);
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
// ensure that the number of invoices for payment has increased by 1
int count;
@@ -248,7 +248,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
// create invoice 1 (subscriptions 1-4)
Invoice invoice1 = new DefaultInvoice(accountId, targetDate, Currency.USD);
- invoiceDao.save(invoice1);
+ invoiceDao.create(invoice1);
UUID invoiceId1 = invoice1.getId();
@@ -256,34 +256,34 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
DateTime endDate = startDate.plusMonths(1);
DefaultInvoiceItem item1 = new DefaultInvoiceItem(invoiceId1, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
- invoiceItemDao.save(item1);
+ invoiceItemDao.create(item1);
DefaultInvoiceItem item2 = new DefaultInvoiceItem(invoiceId1, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
- invoiceItemDao.save(item2);
+ invoiceItemDao.create(item2);
DefaultInvoiceItem item3 = new DefaultInvoiceItem(invoiceId1, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
- invoiceItemDao.save(item3);
+ invoiceItemDao.create(item3);
DefaultInvoiceItem item4 = new DefaultInvoiceItem(invoiceId1, subscriptionId4, startDate, endDate, "test D", rate4, rate4, Currency.USD);
- invoiceItemDao.save(item4);
+ invoiceItemDao.create(item4);
// create invoice 2 (subscriptions 1-3)
- DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
- invoiceDao.save(invoice);
+ DefaultInvoice invoice2 = new DefaultInvoice(accountId, targetDate, Currency.USD);
+ invoiceDao.create(invoice2);
- UUID invoiceId2 = invoice.getId();
+ UUID invoiceId2 = invoice2.getId();
startDate = endDate;
endDate = startDate.plusMonths(1);
DefaultInvoiceItem item5 = new DefaultInvoiceItem(invoiceId2, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
- invoiceItemDao.save(item5);
+ invoiceItemDao.create(item5);
DefaultInvoiceItem item6 = new DefaultInvoiceItem(invoiceId2, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
- invoiceItemDao.save(item6);
+ invoiceItemDao.create(item6);
DefaultInvoiceItem item7 = new DefaultInvoiceItem(invoiceId2, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
- invoiceItemDao.save(item7);
+ invoiceItemDao.create(item7);
// check that each subscription returns the correct number of invoices
List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1.toString());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 0f30753..fa743ff 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -46,7 +46,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
BigDecimal rate = new BigDecimal("20.00");
InvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", rate, rate, Currency.USD);
- invoiceItemDao.save(item);
+ invoiceItemDao.create(item);
InvoiceItem thisItem = invoiceItemDao.getById(item.getId().toString());
assertNotNull(thisItem);
@@ -70,7 +70,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
for (int i = 0; i < 3; i++) {
UUID invoiceId = UUID.randomUUID();
DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate.plusMonths(i), startDate.plusMonths(i + 1), "test", rate, rate, Currency.USD);
- invoiceItemDao.save(item);
+ invoiceItemDao.create(item);
}
List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsBySubscription(subscriptionId.toString());
@@ -87,7 +87,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
UUID subscriptionId = UUID.randomUUID();
BigDecimal amount = rate.multiply(new BigDecimal(i + 1));
DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", amount, amount, Currency.USD);
- invoiceItemDao.save(item);
+ invoiceItemDao.create(item);
}
List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsByInvoice(invoiceId.toString());
@@ -100,7 +100,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
DateTime targetDate = new DateTime(2011, 5, 23, 0, 0, 0, 0);
DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
- invoiceDao.save(invoice);
+ invoiceDao.create(invoice);
UUID invoiceId = invoice.getId();
DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
@@ -108,7 +108,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
UUID subscriptionId = UUID.randomUUID();
DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", rate, rate, Currency.USD);
- invoiceItemDao.save(item);
+ invoiceItemDao.create(item);
List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
assertEquals(items.size(), 1);
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index ea4716f..aca06a2 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index 86eeec4..f64b324 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
<packaging>pom</packaging>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
<url>http://github.com/ning/killbill</url>
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index ad4848e..5152ab7 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.18-SNAPSHOT</version>
+ <version>0.0.21-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java b/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java
index 5db2ac3..8280a15 100644
--- a/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java
+++ b/util/src/main/java/com/ning/billing/util/clock/DefaultClock.java
@@ -41,21 +41,21 @@ public class DefaultClock implements Clock {
return input.minus(input.getMillisOfSecond());
}
- public static DateTime addDuration(DateTime input, List<Duration> durations) {
+ public static DateTime addOrRemoveDuration(DateTime input, List<Duration> durations, boolean add) {
DateTime result = input;
for (Duration cur : durations) {
switch (cur.getUnit()) {
case DAYS:
- result = result.plusDays(cur.getNumber());
+ result = add ? result.plusDays(cur.getNumber()) : result.minusDays(cur.getNumber());
break;
case MONTHS:
- result = result.plusMonths(cur.getNumber());
+ result = add ? result.plusMonths(cur.getNumber()) : result.minusMonths(cur.getNumber());
break;
case YEARS:
- result = result.plusYears(cur.getNumber());
+ result = add ? result.plusYears(cur.getNumber()) : result.minusYears(cur.getNumber());
break;
case UNLIMITED:
default:
@@ -65,9 +65,23 @@ public class DefaultClock implements Clock {
return result;
}
+ public static DateTime addDuration(DateTime input, List<Duration> durations) {
+ return addOrRemoveDuration(input, durations, true);
+ }
+
+ public static DateTime removeDuration(DateTime input, List<Duration> durations) {
+ return addOrRemoveDuration(input, durations, false);
+ }
+
public static DateTime addDuration(DateTime input, Duration duration) {
List<Duration> list = new ArrayList<Duration>();
list.add(duration);
- return addDuration(input, list);
+ return addOrRemoveDuration(input, list, true);
+ }
+
+ public static DateTime removeDuration(DateTime input, Duration duration) {
+ List<Duration> list = new ArrayList<Duration>();
+ list.add(duration);
+ return addOrRemoveDuration(input, list, false);
}
}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
index 05d0137..ac184be 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
@@ -23,18 +23,18 @@ import com.ning.billing.util.entity.EntityBase;
public abstract class CustomizableEntityBase extends EntityBase implements CustomizableEntity {
protected final FieldStore fields;
- public CustomizableEntityBase(UUID id) {
+ public CustomizableEntityBase(final UUID id) {
super(id);
fields = DefaultFieldStore.create(getId(), getObjectName());
}
@Override
- public String getFieldValue(String fieldName) {
+ public String getFieldValue(final String fieldName) {
return fields.getValue(fieldName);
}
@Override
- public void setFieldValue(String fieldName, String fieldValue) {
+ public void setFieldValue(final String fieldName, final String fieldValue) {
fields.setValue(fieldName, fieldValue);
}
@@ -44,6 +44,13 @@ public abstract class CustomizableEntityBase extends EntityBase implements Custo
}
@Override
+ public void addFields(final List<CustomField> fields) {
+ if (fields != null) {
+ this.fields.add(fields);
+ }
+ }
+
+ @Override
public void clearFields() {
fields.clear();
}
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
index 8a7a8c5..8134203 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
@@ -31,6 +31,10 @@ public interface EntityCollectionDao<T extends Entity> {
@Bind("objectType") final String objectType,
@BindBean final List<T> entities);
+ @SqlUpdate
+ public void clear(@Bind("objectId") final String objectId,
+ @Bind("objectType") final String objectType);
+
@SqlQuery
public List<T> load(@Bind("objectId") final String objectId,
@Bind("objectType") final String objectType);
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
index 1ee4fc1..3b5dd46 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
@@ -26,7 +26,10 @@ import java.util.List;
public interface EntityDao<T extends Entity> {
@SqlUpdate
- public void save(@BindBean T entity);
+ public void create(@BindBean T entity);
+
+ @SqlUpdate
+ public void update(@BindBean T entity);
@SqlQuery
public T getById(@Bind("id") final String id);
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
index 4938d49..6bc029f 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
@@ -43,7 +43,11 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
public interface TagDescriptionDao extends EntityDao<TagDescription> {
@Override
@SqlUpdate
- public void save(@TagDescriptionBinder TagDescription entity);
+ public void create(@TagDescriptionBinder TagDescription entity);
+
+ @Override
+ @SqlUpdate
+ public void update(@TagDescriptionBinder TagDescription entity);
public class TagDescriptionMapper implements ResultSetMapper<TagDescription> {
@Override
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
index 6a3062e..5d49fb2 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
@@ -20,12 +20,12 @@ import java.util.UUID;
import com.ning.billing.util.entity.EntityCollectionBase;
public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagStore {
- public DefaultTagStore(UUID objectId, String objectType) {
+ public DefaultTagStore(final UUID objectId, final String objectType) {
super(objectId, objectType);
}
@Override
- public String getEntityKey(Tag entity) {
+ public String getEntityKey(final Tag entity) {
return entity.getName();
}
@@ -58,14 +58,14 @@ public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagSto
}
@Override
- public void remove(String tagName) {
+ public void remove(final String tagName) {
entities.remove(entities.get(tagName));
}
@Override
- public boolean containsTag(String tagName) {
+ public boolean containsTag(final String tagName) {
for (Tag tag : entities.values()) {
- if (tag.getName() == tagName) {
+ if (tag.getName().equals(tagName)) {
return true;
}
}
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
index c995694..57163a9 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
@@ -13,6 +13,11 @@ load() ::= <<
WHERE object_id = :objectId AND object_type = :objectType;
>>
+clear() ::= <<
+ DELETE FROM custom_fields
+ WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
test() ::= <<
SELECT 1 FROM custom_fields;
>>
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
new file mode 100644
index 0000000..c68b18b
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS custom_fields;
+CREATE TABLE custom_fields (
+ id char(36) NOT NULL,
+ object_id char(36) NOT NULL,
+ object_type varchar(30) NOT NULL,
+ field_name varchar(30) NOT NULL,
+ field_value varchar(255) NOT NULL,
+ PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
+CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type, field_name);
+
+DROP TABLE IF EXISTS tag_descriptions;
+CREATE TABLE tag_descriptions (
+ id char(36) NOT NULL,
+ name varchar(20) NOT NULL,
+ created_by varchar(50) NOT NULL,
+ creation_date datetime NOT NULL,
+ description varchar(200) NOT NULL,
+ generate_invoice boolean DEFAULT false,
+ process_payment boolean DEFAULT false,
+ PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
+
+DROP TABLE IF EXISTS tags;
+CREATE TABLE tags (
+ id char(36) NOT NULL,
+ tag_description_id char(36) NOT NULL,
+ object_id char(36) NOT NULL,
+ object_type varchar(30) NOT NULL,
+ date_added datetime NOT NULL,
+ added_by varchar(50) NOT NULL,
+ PRIMARY KEY(id)
+) ENGINE = innodb;
+CREATE INDEX tags_by_object ON tags(object_id);
+CREATE UNIQUE INDEX tags_unique ON tags(tag_description_id, object_id);
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
index bdde261..c71d063 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
@@ -1,12 +1,15 @@
group TagDescriptionDao;
-save() ::= <<
+create() ::= <<
INSERT INTO tag_descriptions(id, name, created_by, creation_date, description, generate_invoice, process_payment)
- VALUES(:id, :name, :createdBy, :creationDate, :description, :generateInvoice, :processPayment)
- ON DUPLICATE KEY UPDATE
- name = :name, created_by = :createdBy, creation_date = :creationDate,
- description := description, generate_invoice = :generateInvoice,
- process_payment = :processPayment
+ VALUES(:id, :name, :createdBy, :creationDate, :description, :generateInvoice, :processPayment);
+>>
+
+update() ::= <<
+ UPDATE tag_descriptions
+ SET name = :name, created_by = :createdBy, creation_date = :creationDate,
+ description = :description, generate_invoice = :generateInvoice, process_payment = :processPayment)
+ WHERE id = :id;
>>
load() ::= <<
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
index 3b53b2e..e72555d 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
@@ -17,6 +17,11 @@ load() ::= <<
WHERE t.object_id = :objectId AND t.object_type = :objectType;
>>
+clear() ::= <<
+ DELETE FROM tags
+ WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
test() ::= <<
SELECT 1 FROM tags;
>>