killbill-uncached

util: populate account_record_id in DefaultTagUserApi Signed-off-by:

10/5/2012 2:45:27 PM

Details

diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextSqlDao.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextSqlDao.java
index 5580273..eb05589 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextSqlDao.java
@@ -26,5 +26,4 @@ public interface CallContextSqlDao {
 
     @SqlQuery("select record_id from accounts where id = :accountId;")
     public Long getAccountRecordId(@Bind("accountId") final String accountId);
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
index cbadb35..17637d5 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
@@ -16,16 +16,22 @@
 
 package com.ning.billing.util.callcontext;
 
+import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
 
+import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.dao.TableName;
 
 public class InternalCallContextFactory {
 
@@ -34,11 +40,13 @@ public class InternalCallContextFactory {
     public static final UUID INTERNAL_TENANT_ID = new UUID(0L, 0L);
     public static final long INTERNAL_TENANT_RECORD_ID = 0L;
 
+    private final IDBI dbi;
     private final CallContextSqlDao callContextSqlDao;
     private final Clock clock;
 
     @Inject
     public InternalCallContextFactory(final IDBI dbi, final Clock clock) {
+        this.dbi = dbi;
         this.callContextSqlDao = dbi.onDemand(CallContextSqlDao.class);
         this.clock = clock;
     }
@@ -63,6 +71,49 @@ public class InternalCallContextFactory {
         return createInternalCallContext(INTERNAL_TENANT_RECORD_ID, null, new DefaultCallContext(INTERNAL_TENANT_ID, userName, callOrigin, userType, userToken, clock));
     }
 
+    /**
+     * Crate an internal call context from a call context, and retrieving the account_record_id from another table
+     *
+     * @param objectId   the id of the row in the table pointed by object type where to look for account_record_id
+     * @param objectType the object type pointed by this objectId
+     * @param context    original call context
+     * @return internal call context from context, with a non null account_record_id (if found)
+     */
+    public InternalCallContext createInternalCallContext(final UUID objectId, final ObjectType objectType, final CallContext context) {
+        final Long accountRecordId;
+
+        final TableName tableName = TableName.fromObjectType(objectType);
+        if (tableName != null) {
+            accountRecordId = dbi.withHandle(new HandleCallback<Long>() {
+                @Override
+                public Long withHandle(final Handle handle) throws Exception {
+                    final String columnName;
+                    if (TableName.TAG_DEFINITIONS.equals(tableName) || TableName.TAG_DEFINITION_HISTORY.equals(tableName)) {
+                        // Not tied to an account
+                        return null;
+                    } else if (TableName.ACCOUNT.equals(tableName) || TableName.ACCOUNT_HISTORY.equals(tableName)) {
+                        // Lookup the record_id directly
+                        columnName = "record_id";
+                    } else {
+                        // The table should have an account_record_id column
+                        columnName = "account_record_id";
+                    }
+
+                    final List<Map<String, Object>> values = handle.select(String.format("select %s from %s where id = ?;", columnName, tableName.getTableName()), objectId.toString());
+                    if (values.size() == 0) {
+                        return null;
+                    } else {
+                        return (Long) values.get(0).get(columnName);
+                    }
+                }
+            });
+        } else {
+            accountRecordId = null;
+        }
+
+        return createInternalCallContext(getTenantRecordId(context), accountRecordId, context);
+    }
+
     // Used for r/o or update/delete operations - we don't need the account id in that case
     // TODO - more work is needed for this statement to hold (especially for junction, overdue, custom fields and tags)
     public InternalCallContext createInternalCallContext(final CallContext context) {
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 84e9710..e958798 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -39,7 +39,8 @@ public enum TableName {
     SUBSCRIPTION_EVENTS("subscription_events", ObjectType.SUBSCRIPTION_EVENT),
     REFUND_HISTORY("refund_history"),
     REFUNDS("refunds", ObjectType.REFUND, REFUND_HISTORY),
-    TAG_DEFINITIONS("tag_definitions", ObjectType.TAG_DEFINITION),
+    TAG_DEFINITION_HISTORY("tag_definition_history"),
+    TAG_DEFINITIONS("tag_definitions", ObjectType.TAG_DEFINITION, TAG_DEFINITION_HISTORY),
     TAG_HISTORY("tag_history"),
     TENANT("tenants", ObjectType.TENANT),
     TAG("tags", TAG_HISTORY);
@@ -66,6 +67,15 @@ public enum TableName {
         this(tableName, null, null);
     }
 
+    public static TableName fromObjectType(final ObjectType objectType) {
+        for (final TableName tableName : values()) {
+            if (tableName.getObjectType() != null && tableName.getObjectType().equals(objectType)) {
+                return tableName;
+            }
+        }
+        return null;
+    }
+
     public String getTableName() {
         return tableName;
     }
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index 87eba08..71b8274 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -79,29 +79,25 @@ public class DefaultTagUserApi implements TagUserApi {
     public void addTags(final UUID objectId, final ObjectType objectType, final Collection<UUID> tagDefinitionIds, final CallContext context) throws TagApiException {
         // TODO: consider making this batch
         for (final UUID tagDefinitionId : tagDefinitionIds) {
-            // TODO accountId?
-            tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(context));
+            tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
         }
     }
 
     @Override
     public void addTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final CallContext context) throws TagApiException {
-        // TODO accountId?
-        tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(context));
+        tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
     }
 
     @Override
     public void removeTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final CallContext context) throws TagApiException {
-        // TODO accountId?
-        tagDao.deleteTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(context));
+        tagDao.deleteTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
     }
 
     @Override
     public void removeTags(final UUID objectId, final ObjectType objectType, final Collection<UUID> tagDefinitionIds, final CallContext context) throws TagApiException {
         // TODO: consider making this batch
         for (final UUID tagDefinitionId : tagDefinitionIds) {
-            // TODO accountId?
-            tagDao.deleteTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(context));
+            tagDao.deleteTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
         }
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java b/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
new file mode 100644
index 0000000..0ac9552
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010-2012 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.util.callcontext;
+
+import java.util.UUID;
+
+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.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.dao.ObjectType;
+
+public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB {
+
+    private InternalCallContextFactory internalCallContextFactory;
+
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        internalCallContextFactory = new InternalCallContextFactory(getMysqlTestingHelper().getDBI(), new ClockMock());
+    }
+
+    @Test(groups = "slow")
+    public void testCreateInternalCallContextWithAccountRecordIdFromSimpleObjectType() throws Exception {
+        final UUID invoiceId = UUID.randomUUID();
+        final Long accountRecordId = 19384012L;
+
+        getMysqlTestingHelper().getDBI().withHandle(new HandleCallback<Void>() {
+            @Override
+            public Void withHandle(final Handle handle) throws Exception {
+                handle.execute("DROP TABLE IF EXISTS invoices;\n" +
+                               "CREATE TABLE invoices (\n" +
+                               "    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,\n" +
+                               "    id char(36) NOT NULL,\n" +
+                               "    account_id char(36) NOT NULL,\n" +
+                               "    invoice_date date NOT NULL,\n" +
+                               "    target_date date NOT NULL,\n" +
+                               "    currency char(3) NOT NULL,\n" +
+                               "    migrated bool NOT NULL,\n" +
+                               "    created_by varchar(50) NOT NULL,\n" +
+                               "    created_date datetime NOT NULL,\n" +
+                               "    account_record_id int(11) unsigned default null,\n" +
+                               "    tenant_record_id int(11) unsigned default null,\n" +
+                               "    PRIMARY KEY(record_id)\n" +
+                               ") ENGINE=innodb;");
+                handle.execute("insert into invoices (id, account_id, invoice_date, target_date, currency, migrated, created_by, created_date, account_record_id) values " +
+                               "(?, ?, now(), now(), 'USD', 0, 'test', now(), ?)", invoiceId.toString(), UUID.randomUUID().toString(), accountRecordId);
+                return null;
+            }
+        });
+
+        final InternalCallContext context = internalCallContextFactory.createInternalCallContext(invoiceId, ObjectType.INVOICE, callContext);
+        // The account record id should have been looked up in the invoices table
+        Assert.assertEquals(context.getAccountRecordId(), accountRecordId);
+        verifyInternalCallContext(context);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateInternalCallContextWithAccountRecordIdFromAccountObjectType() throws Exception {
+        final UUID accountId = UUID.randomUUID();
+        final Long accountRecordId = 19384012L;
+
+        getMysqlTestingHelper().getDBI().withHandle(new HandleCallback<Void>() {
+            @Override
+            public Void withHandle(final Handle handle) throws Exception {
+                // Note: we always create an accounts table, see MysqlTestingHelper
+                handle.execute("insert into accounts (record_id, id) values (?, ?)", accountRecordId, accountId.toString());
+                return null;
+            }
+        });
+
+        final InternalCallContext context = internalCallContextFactory.createInternalCallContext(accountId, ObjectType.ACCOUNT, callContext);
+        // The account record id should have been looked up in the accounts table
+        Assert.assertEquals(context.getAccountRecordId(), accountRecordId);
+        verifyInternalCallContext(context);
+    }
+
+    private void verifyInternalCallContext(final InternalCallContext context) {
+        Assert.assertEquals(context.getCallOrigin(), callContext.getCallOrigin());
+        Assert.assertEquals(context.getComment(), callContext.getComment());
+        Assert.assertEquals(context.getCreatedDate(), callContext.getCreatedDate());
+        Assert.assertEquals(context.getReasonCode(), callContext.getReasonCode());
+        Assert.assertEquals(context.getUpdatedDate(), callContext.getUpdatedDate());
+        Assert.assertEquals(context.getUserName(), callContext.getUserName());
+        Assert.assertEquals(context.getUserToken(), callContext.getUserToken());
+        Assert.assertEquals(context.getUserType(), callContext.getUserType());
+        // Our test callContext doesn't have a tenant id
+        Assert.assertEquals(context.getTenantRecordId(), (Long) InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID);
+    }
+}