@VisibleForTesting <T extends OverdueCheckNotificationKey> Collection<NotificationEventWithMetadata<T>> getFutureNotificationsForAccountInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final NotificationQueue checkOverdueQueue, final UUID accountId, final Class<T> clazz, final InternalCallContext context) { final List<NotificationEventWithMetadata<T>> notifications = checkOverdueQueue.getFutureNotificationFromTransactionForSearchKey1(clazz, context.getAccountRecordId(), entitySqlDaoWrapperFactory.getSqlDao()); /* final Collection<NotificationEventWithMetadata<T>> notificationsFiltered = Collections2.filter(notifications, new Predicate<NotificationEventWithMetadata<T>>() { @Override public boolean apply(@Nullable final NotificationEventWithMetadata<T> input) { final OverdueCheckNotificationKey notificationKey = input.getEvent(); return (accountId.equals(notificationKey.getUuidKey())); } }); return notificationsFiltered; */ return notifications; }
@Override public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception { // Check if we already have notifications for that key final Class<T> clazz = (Class<T>) notificationKey.getClass(); final Collection<NotificationEventWithMetadata<T>> futureNotifications = getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, overdueQueue, accountId, clazz, context); boolean shouldInsertNewNotification = cleanupFutureNotificationsFormTransaction(entitySqlDaoWrapperFactory, futureNotifications, futureNotificationTime, overdueQueue); if (shouldInsertNewNotification) { log.debug("Queuing overdue check notification. Account id: {}, timestamp: {}", accountId.toString(), futureNotificationTime.toString()); overdueQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getSqlDao(), futureNotificationTime, notificationKey, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } else { log.debug("Skipping queuing overdue check notification. Account id: {}, timestamp: {}", accountId.toString(), futureNotificationTime.toString()); } return null; } });
private void recordFutureNotification(final DateTime effectiveDate, final NotificationEvent notificationEvent, final InternalCallContext context) { try { final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME, DefaultEntitlementService.NOTIFICATION_QUEUE_NAME); subscriptionEventQueue.recordFutureNotification(effectiveDate, notificationEvent, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } catch (NoSuchNotificationQueue e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } }
private void recordFutureNotification(final DateTime effectiveDate, final NotificationEvent notificationEvent, final InternalCallContext context) { try { final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME, DefaultEntitlementService.NOTIFICATION_QUEUE_NAME); subscriptionEventQueue.recordFutureNotification(effectiveDate, notificationEvent, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } catch (NoSuchNotificationQueue e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } }
@Override public void insertNextBillingNotification(final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) { final InternalCallContext context = createCallContext(accountId, userToken); final NotificationQueue nextBillingQueue; try { nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME, DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE); log.info("Queuing next billing date notification at {} for subscriptionId {}", futureNotificationTime.toString(), subscriptionId.toString()); nextBillingQueue.recordFutureNotification(futureNotificationTime, new NextBillingDateNotificationKey(subscriptionId), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } catch (NoSuchNotificationQueue e) { log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e); } catch (IOException e) { log.error("Failed to serialize notificationKey for subscriptionId {}", subscriptionId); } }
private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) { try { eventBus.post(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken())); } catch (EventBusException e) { log.warn("Failed to post adjustment event for invoice " + invoiceId, e); } } }
private void notifyBusOfInvoiceAdjustment(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID accountId, final UUID userToken, final InternalCallContext context) { try { eventBus.postFromTransaction(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), userToken), entitySqlDaoWrapperFactory.getSqlDao()); } catch (EventBusException e) { log.warn("Failed to post adjustment event for invoice " + invoiceId, e); } } }
@Override public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) { final InternalCallContext context = createCallContext(accountId, userToken); final NotificationQueue nextBillingQueue; try { nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME, DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE); log.info("Queuing next billing date notification at {} for subscriptionId {}", futureNotificationTime.toString(), subscriptionId.toString()); nextBillingQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getSqlDao(), futureNotificationTime, new NextBillingDateNotificationKey(subscriptionId), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } catch (NoSuchNotificationQueue e) { log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e); } catch (IOException e) { log.error("Failed to serialize notificationKey for subscriptionId {}", subscriptionId); } }
private OverdueChangeInternalEvent createOverdueEvent(final Account overdueable, final String previousOverdueStateName, final String nextOverdueStateName, final boolean isBlockedBilling, final boolean isUnblockedBilling, final InternalCallContext context) throws BlockingApiException { return new DefaultOverdueChangeEvent(overdueable.getId(), previousOverdueStateName, nextOverdueStateName, isBlockedBilling, isUnblockedBilling, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); }
private boolean isAccountTaggedWith_OVERDUE_ENFORCEMENT_OFF(final InternalCallContext context) throws OverdueException { try { final UUID accountId = accountApi.getByRecordId(context.getAccountRecordId(), context); final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context); for (Tag cur : accountTags) { if (cur.getTagDefinitionId().equals(ControlTagType.OVERDUE_ENFORCEMENT_OFF.getId())) { return true; } } return false; } catch (AccountApiException e) { throw new OverdueException(e); } } }
private boolean scheduleRetryInternal(final UUID paymentId, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) { final InternalCallContext context = createCallContextFromPaymentId(paymentId); try { final NotificationQueue retryQueue = notificationQueueService.getNotificationQueue(DefaultPaymentService.SERVICE_NAME, getQueueName()); final NotificationEvent key = new PaymentRetryNotificationKey(paymentId); if (retryQueue != null) { if (transactionalDao == null) { retryQueue.recordFutureNotification(timeOfRetry, key, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } else { retryQueue.recordFutureNotificationFromTransaction(transactionalDao.getSqlDao(), timeOfRetry, key, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } } } catch (NoSuchNotificationQueue e) { log.error(String.format("Failed to retrieve notification queue %s:%s", DefaultPaymentService.SERVICE_NAME, getQueueName())); return false; } catch (IOException e) { log.error(String.format("Failed to serialize notificationQueue event for paymentId %s", paymentId)); return false; } return true; }
@Test(groups = "slow") public void testShouldntInsertMultipleNotificationsPerOverdueable() throws Exception { final UUID accountId = UUID.randomUUID(); final Account overdueable = Mockito.mock(Account.class); Mockito.when(overdueable.getId()).thenReturn(accountId); insertOverdueCheckAndVerifyQueueContent(overdueable, 10, 10); insertOverdueCheckAndVerifyQueueContent(overdueable, 5, 5); insertOverdueCheckAndVerifyQueueContent(overdueable, 15, 5); // Verify the final content of the queue Assert.assertEquals(overdueQueue.getFutureNotificationForSearchKey1(OverdueCheckNotificationKey.class, internalCallContext.getAccountRecordId()).size(), 1); }
@Test(groups = "slow") public void testCreateInternalCallContextWithAccountRecordIdFromAccountObjectType() throws Exception { final UUID accountId = UUID.randomUUID(); final Long accountRecordId = 19384012L; dbi.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, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, false, new Date(), "i", new Date(), "j"); 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 recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao, final DateTime effectiveDate, final NotificationEvent notificationKey, final InternalCallContext context) { try { final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(DefaultSubscriptionBaseService.SUBSCRIPTION_SERVICE_NAME, DefaultSubscriptionBaseService.NOTIFICATION_QUEUE_NAME); subscriptionEventQueue.recordFutureNotificationFromTransaction(null, effectiveDate, notificationKey, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId()); } catch (NoSuchNotificationQueue e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } }
@Override protected void postBusEventFromTransaction(final CustomFieldModelDao customField, final CustomFieldModelDao savedCustomField, final ChangeType changeType, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) throws BillingExceptionBase { BusInternalEvent customFieldEvent = null; switch (changeType) { case INSERT: customFieldEvent = new DefaultCustomFieldCreationEvent(customField.getId(), customField.getObjectId(), customField.getObjectType(), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); break; case DELETE: customFieldEvent = new DefaultCustomFieldDeletionEvent(customField.getId(), customField.getObjectId(), customField.getObjectType(), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); break; default: return; } try { bus.postFromTransaction(customFieldEvent, entitySqlDaoWrapperFactory.getSqlDao()); } catch (PersistentBus.EventBusException e) { log.warn("Failed to post tag event for custom field " + customField.getId().toString(), e); } }
private void recordPauseResumeNotificationEntry(final UUID entitlementId, final UUID bundleId, final DateTime effectiveDate, final boolean isPause, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException { final NotificationEvent notificationEvent = new EntitlementNotificationKey(entitlementId, bundleId, isPause ? EntitlementNotificationKeyAction.PAUSE : EntitlementNotificationKeyAction.RESUME, effectiveDate); try { final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME, DefaultEntitlementService.NOTIFICATION_QUEUE_NAME); subscriptionEventQueue.recordFutureNotification(effectiveDate, notificationEvent, contextWithValidAccountRecordId.getUserToken(), contextWithValidAccountRecordId.getAccountRecordId(), contextWithValidAccountRecordId.getTenantRecordId()); } catch (final NoSuchNotificationQueue e) { throw new EntitlementApiException(e, ErrorCode.__UNKNOWN_ERROR_CODE); } catch (final IOException e) { throw new EntitlementApiException(e, ErrorCode.__UNKNOWN_ERROR_CODE); } }
private void processBlockingNotification(final BlockingTransitionNotificationKey key, final InternalCallContext internalCallContext) { // Check if the blocking state has been deleted since if (blockingStateDao.getById(key.getBlockingStateId(), internalCallContext) == null) { log.debug("BlockingState {} has been deleted, not sending a bus event", key.getBlockingStateId()); return; } final BusEvent event = new DefaultBlockingTransitionInternalEvent(key.getBlockableId(), key.getBlockingType(), key.isTransitionedToBlockedBilling(), key.isTransitionedToUnblockedBilling(), key.isTransitionedToBlockedEntitlement(), key.isTransitionToUnblockedEntitlement(), internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), internalCallContext.getUserToken()); try { eventBus.post(event); } catch (EventBusException e) { log.warn("Failed to post event {}", e); } }
@Override public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems, final List<InvoicePaymentModelDao> invoicePayments, final boolean isRealInvoice, final Map<UUID, DateTime> callbackDateTimePerSubscriptions, final InternalCallContext context) { synchronized (monitor) { invoices.put(invoice.getId(), invoice); for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) { items.put(invoiceItemModelDao.getId(), invoiceItemModelDao); } for (final InvoicePaymentModelDao paymentModelDao : invoicePayments) { payments.put(paymentModelDao.getId(), paymentModelDao); } accountRecordIds.put(invoice.getAccountId(), context.getAccountRecordId()); } try { eventBus.post(new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(), InvoiceModelDaoHelper.getBalance(invoice), invoice.getCurrency(), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken())); } catch (PersistentBus.EventBusException ex) { throw new RuntimeException(ex); } }
@Override public Void withHandle(final Handle handle) throws Exception { handle.execute("drop table if exists " + tableNameA); handle.execute("create table " + tableNameA + "(record_id int(11) unsigned not null auto_increment," + "a_column char default 'a'," + "account_record_id int(11) unsigned not null," + "tenant_record_id int(11) unsigned default 0," + "primary key(record_id));"); handle.execute("drop table if exists " + tableNameB); handle.execute("create table " + tableNameB + "(record_id int(11) unsigned not null auto_increment," + "b_column char default 'b'," + "account_record_id int(11) unsigned not null," + "tenant_record_id int(11) unsigned default 0," + "primary key(record_id));"); handle.execute("insert into " + tableNameA + " (account_record_id, tenant_record_id) values (?, ?)", internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId()); handle.execute("insert into " + tableNameB + " (account_record_id, tenant_record_id) values (?, ?)", internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId()); // Add row in accounts table handle.execute("insert into accounts (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) " + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength, isNotifiedForInvoices, createdDate, createdBy, updatedDate, updatedBy, internalCallContext.getTenantRecordId()); return null; } });
@Test(groups = "slow") public void testInvoiceNotifier() throws Exception { final UUID accountId = UUID.randomUUID(); final SubscriptionBase subscription = invoiceUtil.createSubscription(); final UUID subscriptionId = subscription.getId(); final DateTime now = clock.getUTCNow(); final NotificationQueue nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME, DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE); nextBillingQueue.recordFutureNotification(now, new NextBillingDateNotificationKey(subscriptionId), internalCallContext.getUserToken(), internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId()); // Move time in the future after the notification effectiveDate ((ClockMock) clock).setDeltaFromReality(3000); await().atMost(1, MINUTES).until(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return testInvoiceNotificationQListener.getEventCount() == 1; } }); Assert.assertEquals(testInvoiceNotificationQListener.getEventCount(), 1); Assert.assertEquals(testInvoiceNotificationQListener.getLatestSubscriptionId(), subscriptionId); } }