@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 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; }
public void setDefaultPaymentMethod(final Account account, final UUID paymentMethodId, final InternalCallContext context) throws PaymentApiException { final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT); new WithAccountLock<Void>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Void>() { @Override public Void doOperation() throws PaymentApiException { final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context); if (paymentMethodModel == null) { throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId); } try { final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context); pluginApi.setDefaultPaymentMethod(account.getId(), paymentMethodId, context.toCallContext(tenantId)); accountInternalApi.updatePaymentMethod(account.getId(), paymentMethodId, context); return null; } catch (PaymentPluginApiException e) { throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, account.getId(), e.getErrorMessage()); } catch (AccountApiException e) { throw new PaymentApiException(e); } } }); }
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); } }
private void verifyInternalCallContext(final InternalCallContext context) { Assert.assertEquals(context.getCallOrigin(), callContext.getCallOrigin()); Assert.assertEquals(context.getComments(), callContext.getComments()); Assert.assertEquals(context.getCreatedDate(), callContext.getCreatedDate()); Assert.assertEquals(context.getReasonCode(), callContext.getReasonCode()); Assert.assertEquals(context.getUpdatedDate(), callContext.getUpdatedDate()); Assert.assertEquals(context.getCreatedBy(), callContext.getUserName()); Assert.assertEquals(context.getUserToken(), callContext.getUserToken()); Assert.assertEquals(context.getContextUserType(), callContext.getUserType()); // Our test callcontext doesn't have a tenant id Assert.assertEquals(context.getTenantRecordId(), (Long) InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID); } }
public InternalCallContext createInternalCallContext(final Long accountRecordId, final InternalCallContext context) { return new InternalCallContext(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(), context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(), context.getUpdatedDate()); }
private void postBlockingTransitionEvent(final UUID blockingStateId, final DateTime effectiveDate, final UUID blockableId, final BlockingStateType type, final String serviceName, final BlockingAggregator previousState, final BlockingAggregator currentState, final InternalCallContext context) { final boolean isTransitionToBlockedBilling = !previousState.isBlockBilling() && currentState.isBlockBilling(); final boolean isTransitionToUnblockedBilling = previousState.isBlockBilling() && !currentState.isBlockBilling(); final boolean isTransitionToBlockedEntitlement = !previousState.isBlockEntitlement() && currentState.isBlockEntitlement(); final boolean isTransitionToUnblockedEntitlement = previousState.isBlockEntitlement() && !currentState.isBlockEntitlement(); if (effectiveDate.compareTo(clock.getUTCNow()) > 0) { // Add notification entry to send the bus event at the effective date final NotificationEvent notificationEvent = new BlockingTransitionNotificationKey(blockingStateId, blockableId, type, isTransitionToBlockedBilling, isTransitionToUnblockedBilling, isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement); recordFutureNotification(effectiveDate, notificationEvent, context); } else { // TODO Do we want to send a DefaultEffectiveEntitlementEvent for entitlement specific blocking states? // Don't post if nothing has changed for entitlement-service if (! serviceName.equals(EntitlementService.ENTITLEMENT_SERVICE_NAME) || ! previousState.equals(currentState)) { final BusEvent event = new DefaultBlockingTransitionInternalEvent(blockableId, type, isTransitionToBlockedBilling, isTransitionToUnblockedBilling, isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()); postBusEvent(event); } else { System.out.println("********** SKIPPING EVENT "); } } }
@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; } });
@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); } }
@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); } }