killbill-uncached

util: Make DefaultCustomFieldUserApi#addCustomFields

11/3/2017 11:45:35 PM

Details

diff --git a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
index 23acc3e..163eeea 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
@@ -17,14 +17,11 @@
 package org.killbill.billing.util.customfield.api;
 
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
@@ -34,6 +31,7 @@ import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.customfield.StringCustomField;
 import org.killbill.billing.util.customfield.dao.CustomFieldDao;
 import org.killbill.billing.util.customfield.dao.CustomFieldModelDao;
+import org.killbill.billing.util.customfield.dao.DefaultCustomFieldDao;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
 
@@ -89,37 +87,15 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
 
     @Override
     public void addCustomFields(final List<CustomField> customFields, final CallContext context) throws CustomFieldApiException {
-        // TODO make it transactional
-
-        final Map<UUID, ObjectType> mapping = new HashMap<UUID, ObjectType>();
-        for (final CustomField cur : customFields) {
-            mapping.put(cur.getObjectId(), cur.getObjectType());
-        }
-
-        final List<CustomFieldModelDao> all = new LinkedList<CustomFieldModelDao>();
-        for (UUID cur : mapping.keySet()) {
-            final ObjectType type = mapping.get(cur);
-            all.addAll(customFieldDao.getCustomFieldsForObject(cur, type, internalCallContextFactory.createInternalCallContext(cur, type, context)));
-        }
-        final List<CustomField> toBeInserted = new LinkedList<CustomField>();
-        for (final CustomField cur : customFields) {
-            final CustomFieldModelDao match = Iterables.tryFind(all, new com.google.common.base.Predicate<CustomFieldModelDao>() {
+        if (!customFields.isEmpty()) {
+            final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(customFields.get(0).getObjectId(), customFields.get(0).getObjectType(), context);
+            final Iterable<CustomFieldModelDao> transformed = Iterables.transform(customFields, new Function<CustomField, CustomFieldModelDao>() {
                 @Override
-                public boolean apply(final CustomFieldModelDao input) {
-                    return input.getObjectId().equals(cur.getObjectId()) &&
-                           input.getObjectType() == cur.getObjectType() &&
-                           input.getFieldName().equals(cur.getFieldName());
-
+                public CustomFieldModelDao apply(final CustomField input) {
+                    return new CustomFieldModelDao(context.getCreatedDate(), input.getFieldName(), input.getFieldValue(), input.getObjectId(), input.getObjectType());
                 }
-            }).orNull();
-            if (match != null) {
-                throw new CustomFieldApiException(ErrorCode.CUSTOM_FIELD_ALREADY_EXISTS, match.getId());
-            }
-            toBeInserted.add(cur);
-        }
-
-        for (CustomField cur : toBeInserted) {
-            customFieldDao.create(new CustomFieldModelDao(context.getCreatedDate(), cur.getFieldName(), cur.getFieldValue(), cur.getObjectId(), cur.getObjectType()), internalCallContextFactory.createInternalCallContext(cur.getObjectId(), cur.getObjectType(), context));
+            });
+            ((DefaultCustomFieldDao) customFieldDao).create(transformed, internalCallContext);
         }
     }
 
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java
index 9f4371b..6147e25 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java
@@ -25,6 +25,7 @@ import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
+import org.killbill.billing.util.tag.dao.TagModelDao;
 
 public class CustomFieldModelDao extends EntityModelDaoBase implements EntityModelDao<CustomField> {
 
@@ -34,7 +35,6 @@ public class CustomFieldModelDao extends EntityModelDaoBase implements EntityMod
     private ObjectType objectType;
     private Boolean isActive;
 
-
     public CustomFieldModelDao() {  /* For the DAO mapper */ }
 
     public CustomFieldModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String fieldName,
@@ -137,6 +137,27 @@ public class CustomFieldModelDao extends EntityModelDaoBase implements EntityMod
         return true;
     }
 
+    public boolean isSame(final CustomFieldModelDao that) {
+        if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null) {
+            return false;
+        }
+        if (fieldValue != null ? !fieldValue.equals(that.fieldValue) : that.fieldValue != null) {
+            return false;
+        }
+        if (objectId != null ? !objectId.equals(that.objectId) : that.objectId != null) {
+            return false;
+        }
+        if (objectType != that.objectType) {
+            return false;
+        }
+        if (isActive != null ? !isActive.equals(that.isActive) : that.isActive != null) {
+            return false;
+        }
+        return true;
+    }
+
+
+
     @Override
     public int hashCode() {
         int result = super.hashCode();
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
index 742bf5d..28677b9 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
@@ -24,37 +24,38 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Ordering;
-import org.skife.jdbi.v2.IDBI;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.BillingExceptionBase;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
-import org.killbill.bus.api.PersistentBus;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.clock.Clock;
 import org.killbill.billing.events.BusInternalEvent;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.customfield.api.DefaultCustomFieldCreationEvent;
 import org.killbill.billing.util.customfield.api.DefaultCustomFieldDeletionEvent;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Pagination;
+import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Ordering;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
 import org.killbill.billing.util.entity.dao.EntityDaoBase;
+import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.clock.Clock;
+import org.skife.jdbi.v2.IDBI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, CustomField, CustomFieldApiException> implements CustomFieldDao {
@@ -126,6 +127,18 @@ public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, Cu
     }
 
     @Override
+    protected boolean checkEntityAlreadyExists(final EntitySqlDao<CustomFieldModelDao, CustomField> transactional, final CustomFieldModelDao entity, final InternalCallContext context) {
+        return Iterables.find(transactional.getByAccountRecordId(context),
+                              new Predicate<CustomFieldModelDao>() {
+                                  @Override
+                                  public boolean apply(final CustomFieldModelDao existingCustomField) {
+                                      return entity.isSame(existingCustomField);
+                                  }
+                              },
+                              null) != null;
+    }
+
+    @Override
     protected void postBusEventFromTransaction(final CustomFieldModelDao customField, final CustomFieldModelDao savedCustomField, final ChangeType changeType,
                                                final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context)
             throws BillingExceptionBase {
diff --git a/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java b/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
index fcddf41..4004e9a 100644
--- a/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
+++ b/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
@@ -23,11 +23,13 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
+import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.customfield.StringCustomField;
 import org.killbill.billing.util.entity.Pagination;
@@ -35,16 +37,20 @@ import org.mockito.Mockito;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 
 public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
 
-    @Test(groups = "slow")
-    public void testSaveCustomFieldWithAccountRecordId() throws Exception {
-        final UUID accountId = UUID.randomUUID();
-        final Long accountRecordId = 19384012L;
+    final UUID accountId = UUID.randomUUID();
+    final Long accountRecordId = 19384012L;
+
+    @Override
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
 
         final ImmutableAccountData immutableAccountData = Mockito.mock(ImmutableAccountData.class);
         Mockito.when(immutableAccountInternalApi.getImmutableAccountDataByRecordId(Mockito.<Long>eq(accountRecordId), Mockito.<InternalTenantContext>any())).thenReturn(immutableAccountData);
@@ -59,6 +65,39 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
                 return null;
             }
         });
+    }
+
+    @Test(groups = "slow")
+    public void testCustomFieldBasic() throws Exception {
+
+        final CustomField customField1 = new StringCustomField("some123", "some 456", ObjectType.ACCOUNT, accountId, callContext.getCreatedDate());
+        final CustomField customField2 = new StringCustomField("other123", "other 456", ObjectType.ACCOUNT, accountId, callContext.getCreatedDate());
+        eventsListener.pushExpectedEvents(NextEvent.CUSTOM_FIELD, NextEvent.CUSTOM_FIELD);
+        customFieldUserApi.addCustomFields(ImmutableList.<CustomField>of(customField1, customField2), callContext);
+        assertListenerStatus();
+
+        // Verify operation is indeed transaction, and nothing was inserted
+        final CustomField customField3 = new StringCustomField("qrqrq123", "qrqrq 456", ObjectType.ACCOUNT, accountId, callContext.getCreatedDate());
+        try {
+            customFieldUserApi.addCustomFields(ImmutableList.<CustomField>of(customField3, customField1), callContext);
+        } catch (CustomFieldApiException e) {
+            Assert.assertEquals(e.getCode(), ErrorCode.CUSTOM_FIELD_ALREADY_EXISTS.getCode());
+        }
+
+        List<CustomField> all = customFieldUserApi.getCustomFieldsForAccount(accountId, callContext);
+        Assert.assertEquals(all.size(), 2);
+
+        eventsListener.pushExpectedEvent(NextEvent.CUSTOM_FIELD);
+        customFieldUserApi.addCustomFields(ImmutableList.<CustomField>of(customField3), callContext);
+        assertListenerStatus();
+
+        all = customFieldUserApi.getCustomFieldsForAccount(accountId, callContext);
+        Assert.assertEquals(all.size(), 3);
+    }
+
+
+        @Test(groups = "slow")
+    public void testSaveCustomFieldWithAccountRecordId() throws Exception {
 
         checkPagination(0);
 
diff --git a/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java b/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java
index e2b016d..f3d5cf7 100644
--- a/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java
+++ b/util/src/test/java/org/killbill/billing/util/customfield/dao/MockCustomFieldDao.java
@@ -52,6 +52,7 @@ public class MockCustomFieldDao extends MockEntityDaoBase<CustomFieldModelDao, C
         throw new UnsupportedOperationException();
     }
 
+
     @Override
     public Pagination<CustomFieldModelDao> searchCustomFields(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
         throw new UnsupportedOperationException();