feat(libs/payments): Implement customerChanged

This commit is contained in:
Lisa Chan 2024-09-25 18:18:37 -04:00
Родитель ae82b7f823
Коммит c20c754e4e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 9052E177BBC5E764
22 изменённых файлов: 239 добавлений и 216 удалений

Просмотреть файл

@ -409,7 +409,6 @@ commands:
paths: paths:
- artifacts/blob-report - artifacts/blob-report
rename-reports-chromium: rename-reports-chromium:
steps: steps:
- run: - run:
@ -624,7 +623,7 @@ jobs:
build: build:
executor: default-executor executor: default-executor
resource_class: large resource_class: xlarge
steps: steps:
- git-checkout - git-checkout
- restore-workspace - restore-workspace
@ -908,7 +907,7 @@ jobs:
build-and-deploy-storybooks: build-and-deploy-storybooks:
executor: default-executor executor: default-executor
resource_class: large resource_class: xlarge
steps: steps:
- git-checkout - git-checkout
- restore-workspace - restore-workspace

Просмотреть файл

@ -93,6 +93,15 @@ CSP__PAYPAL_API='https://www.sandbox.paypal.com'
SENTRY__SERVER_NAME=fxa-payments-next-server SENTRY__SERVER_NAME=fxa-payments-next-server
SENTRY__AUTH_TOKEN= SENTRY__AUTH_TOKEN=
# NotifierSns Config
NOTIFIER_SNS_CONFIG__SNS_TOPIC_ARN=arn:aws:sns:us-west-2:123456789012:MyTopic
NOTIFIER_SNS_CONFIG__SNS_TOPIC_ENDPOINT=http://localhost:4566
# ProfileClient Config
PROFILE_CLIENT_CONFIG__URL=http://localhost:1111
PROFILE_CLIENT_CONFIG__SECRET_BEARER_TOKEN='YOU MUST CHANGE ME'
PROFILE_CLIENT_CONFIG__SERVICE_NAME='subhub'
# Other # Other
CONTENT_SERVER_URL=http://localhost:3030 CONTENT_SERVER_URL=http://localhost:3030
SUPPORT_URL=https://support.mozilla.org SUPPORT_URL=https://support.mozilla.org

Просмотреть файл

@ -89,6 +89,15 @@ CSP__PAYPAL_API='https://www.paypal.com'
SENTRY__SERVER_NAME=fxa-payments-next-server SENTRY__SERVER_NAME=fxa-payments-next-server
SENTRY__AUTH_TOKEN= SENTRY__AUTH_TOKEN=
# NotifierSns Config
NOTIFIER_SNS_CONFIG__SNS_TOPIC_ARN=arn:aws:sns:us-west-2:123456789012:MyTopic
NOTIFIER_SNS_CONFIG__SNS_TOPIC_ENDPOINT=http://localhost:4566
# ProfileClient Config
PROFILE_CLIENT_CONFIG__URL=https://profile.accounts.firefox.com
PROFILE_CLIENT_CONFIG__SECRET_BEARER_TOKEN='YOU MUST CHANGE ME'
PROFILE_CLIENT_CONFIG__SERVICE_NAME='subhub'
# Other # Other
CONTENT_SERVER_URL=https://accounts.firefox.com CONTENT_SERVER_URL=https://accounts.firefox.com
SUPPORT_URL=https://support.mozilla.org SUPPORT_URL=https://support.mozilla.org

Просмотреть файл

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { faker } from '@faker-js/faker'; import { faker } from '@faker-js/faker';
import { ConfigService } from '@nestjs/config';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { import {
@ -39,6 +38,10 @@ import {
MockStripeConfigProvider, MockStripeConfigProvider,
AccountCustomerManager, AccountCustomerManager,
} from '@fxa/payments/stripe'; } from '@fxa/payments/stripe';
import {
MockProfileClientConfigProvider,
ProfileClient,
} from '@fxa/profile/client';
import { import {
MockStrapiClientConfigProvider, MockStrapiClientConfigProvider,
ProductConfigurationManager, ProductConfigurationManager,
@ -57,8 +60,14 @@ import {
GeoDBManagerConfig, GeoDBManagerConfig,
MockGeoDBNestFactory, MockGeoDBNestFactory,
} from '@fxa/shared/geodb'; } from '@fxa/shared/geodb';
import { LOGGER_PROVIDER } from '@fxa/shared/log';
import { MockStatsDProvider } from '@fxa/shared/metrics/statsd'; import { MockStatsDProvider } from '@fxa/shared/metrics/statsd';
import { AccountManager } from '@fxa/shared/account/account'; import { AccountManager } from '@fxa/shared/account/account';
import {
MockNotifierSnsConfigProvider,
NotifierService,
NotifierSnsProvider,
} from '@fxa/shared/notifier';
import { import {
CheckoutCustomerDataFactory, CheckoutCustomerDataFactory,
FinishErrorCartFactory, FinishErrorCartFactory,
@ -89,6 +98,11 @@ describe('CartService', () => {
let invoiceManager: InvoiceManager; let invoiceManager: InvoiceManager;
let productConfigurationManager: ProductConfigurationManager; let productConfigurationManager: ProductConfigurationManager;
const mockLogger = {
error: jest.fn(),
debug: jest.fn(),
};
beforeEach(async () => { beforeEach(async () => {
const moduleRef = await Test.createTestingModule({ const moduleRef = await Test.createTestingModule({
providers: [ providers: [
@ -97,7 +111,6 @@ describe('CartService', () => {
CartManager, CartManager,
CartService, CartService,
CheckoutService, CheckoutService,
ConfigService,
CustomerManager, CustomerManager,
EligibilityManager, EligibilityManager,
EligibilityService, EligibilityService,
@ -107,10 +120,14 @@ describe('CartService', () => {
MockAccountDatabaseNestFactory, MockAccountDatabaseNestFactory,
MockFirestoreProvider, MockFirestoreProvider,
MockGeoDBNestFactory, MockGeoDBNestFactory,
MockNotifierSnsConfigProvider,
MockPaypalClientConfigProvider, MockPaypalClientConfigProvider,
MockProfileClientConfigProvider,
MockStatsDProvider, MockStatsDProvider,
MockStrapiClientConfigProvider, MockStrapiClientConfigProvider,
MockStripeConfigProvider, MockStripeConfigProvider,
NotifierService,
NotifierSnsProvider,
PaymentMethodManager, PaymentMethodManager,
PaypalBillingAgreementManager, PaypalBillingAgreementManager,
PayPalClient, PayPalClient,
@ -118,12 +135,17 @@ describe('CartService', () => {
PriceManager, PriceManager,
ProductConfigurationManager, ProductConfigurationManager,
ProductManager, ProductManager,
ProfileClient,
PromotionCodeManager, PromotionCodeManager,
StrapiClient, StrapiClient,
StripeClient, StripeClient,
SubscriptionManager, SubscriptionManager,
CurrencyManager, CurrencyManager,
MockCurrencyConfigProvider, MockCurrencyConfigProvider,
{
provide: LOGGER_PROVIDER,
useValue: mockLogger,
},
], ],
}).compile(); }).compile();
@ -142,43 +164,49 @@ describe('CartService', () => {
}); });
describe('setupCart', () => { describe('setupCart', () => {
it('calls createCart with expected parameters', async () => { const args = {
const mockCustomer = StripeResponseFactory(StripeCustomerFactory()); interval: SubplatInterval.Monthly,
const mockAccountCustomer = ResultAccountCustomerFactory({ offeringConfigId: faker.string.uuid(),
stripeCustomerId: mockCustomer.id, experiment: faker.string.uuid(),
}); promoCode: faker.word.noun(),
const mockResultCart = ResultCartFactory(); uid: faker.string.hexadecimal({
const args = { length: 32,
interval: SubplatInterval.Monthly, prefix: '',
offeringConfigId: faker.string.uuid(), casing: 'lower',
experiment: faker.string.uuid(), }),
promoCode: faker.word.noun(), ip: faker.internet.ipv4(),
uid: faker.string.hexadecimal({ };
length: 32,
prefix: '',
casing: 'lower',
}),
ip: faker.internet.ipv4(),
};
const taxAddress = TaxAddressFactory();
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory();
const mockResolvedCurrency = faker.finance.currencyCode();
jest const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
.spyOn(eligibilityService, 'checkEligibility') const mockAccountCustomer = ResultAccountCustomerFactory({
.mockResolvedValue(EligibilityStatus.CREATE); stripeCustomerId: mockCustomer.id,
jest.spyOn(geodbManager, 'getTaxAddress').mockReturnValue(taxAddress); });
const mockInvoicePreview = InvoicePreviewFactory();
const mockResultCart = ResultCartFactory();
const mockPrice = StripePriceFactory();
const taxAddress = TaxAddressFactory();
beforeEach(async () => {
jest jest
.spyOn(accountCustomerManager, 'getAccountCustomerByUid') .spyOn(accountCustomerManager, 'getAccountCustomerByUid')
.mockResolvedValue(mockAccountCustomer); .mockResolvedValue(mockAccountCustomer);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest.spyOn(geodbManager, 'getTaxAddress').mockReturnValue(taxAddress);
jest jest
.spyOn(productConfigurationManager, 'retrieveStripePrice') .spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice); .mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest jest
.spyOn(invoiceManager, 'preview') .spyOn(invoiceManager, 'preview')
.mockResolvedValue(mockInvoicePreview); .mockResolvedValue(mockInvoicePreview);
jest
.spyOn(eligibilityService, 'checkEligibility')
.mockResolvedValue(EligibilityStatus.CREATE);
});
it('calls createCart with expected parameters', async () => {
const mockResultCart = ResultCartFactory();
const mockResolvedCurrency = faker.finance.currencyCode();
jest jest
.spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice') .spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice')
.mockResolvedValue(undefined); .mockResolvedValue(undefined);
@ -206,44 +234,10 @@ describe('CartService', () => {
it('throws an error when couponCode is invalid', async () => { it('throws an error when couponCode is invalid', async () => {
const mockAccount = AccountFactory(); const mockAccount = AccountFactory();
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockAccountCustomer = ResultAccountCustomerFactory({
stripeCustomerId: mockCustomer.id,
});
const mockResultCart = ResultCartFactory();
const args = {
interval: SubplatInterval.Monthly,
offeringConfigId: faker.string.uuid(),
experiment: faker.string.uuid(),
promoCode: faker.word.noun(),
uid: faker.string.hexadecimal({
length: 32,
prefix: '',
casing: 'lower',
}),
ip: faker.internet.ipv4(),
};
const taxAddress = TaxAddressFactory();
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory();
jest jest
.spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice') .spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice')
.mockRejectedValue(undefined); .mockRejectedValue(undefined);
jest
.spyOn(eligibilityService, 'checkEligibility')
.mockResolvedValue(EligibilityStatus.CREATE);
jest.spyOn(geodbManager, 'getTaxAddress').mockReturnValue(taxAddress);
jest
.spyOn(accountCustomerManager, 'getAccountCustomerByUid')
.mockResolvedValue(mockAccountCustomer);
jest
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest
.spyOn(invoiceManager, 'preview')
.mockResolvedValue(mockInvoicePreview);
jest.spyOn(cartManager, 'createCart').mockResolvedValue(mockResultCart); jest.spyOn(cartManager, 'createCart').mockResolvedValue(mockResultCart);
jest jest
.spyOn(accountManager, 'getAccounts') .spyOn(accountManager, 'getAccounts')
@ -258,44 +252,7 @@ describe('CartService', () => {
it('throws an error when country to currency result is invalid', async () => { it('throws an error when country to currency result is invalid', async () => {
const mockAccount = AccountFactory(); const mockAccount = AccountFactory();
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockAccountCustomer = ResultAccountCustomerFactory({
stripeCustomerId: mockCustomer.id,
});
const mockResultCart = ResultCartFactory();
const args = {
interval: SubplatInterval.Monthly,
offeringConfigId: faker.string.uuid(),
experiment: faker.string.uuid(),
promoCode: faker.word.noun(),
uid: faker.string.hexadecimal({
length: 32,
prefix: '',
casing: 'lower',
}),
ip: faker.internet.ipv4(),
};
const taxAddress = TaxAddressFactory();
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory();
jest
.spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice')
.mockRejectedValue(undefined);
jest
.spyOn(eligibilityService, 'checkEligibility')
.mockResolvedValue(EligibilityStatus.CREATE);
jest.spyOn(geodbManager, 'getTaxAddress').mockReturnValue(taxAddress);
jest
.spyOn(accountCustomerManager, 'getAccountCustomerByUid')
.mockResolvedValue(mockAccountCustomer);
jest
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest
.spyOn(invoiceManager, 'preview')
.mockResolvedValue(mockInvoicePreview);
jest jest
.spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice') .spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice')
.mockResolvedValue(undefined); .mockResolvedValue(undefined);
@ -316,21 +273,24 @@ describe('CartService', () => {
}); });
describe('restartCart', () => { describe('restartCart', () => {
it('fetches old cart and creates new cart with same details', async () => { const mockOldCart = ResultCartFactory({
const mockOldCart = ResultCartFactory({ couponCode: faker.word.noun(),
couponCode: faker.word.noun(), });
}); const mockNewCart = ResultCartFactory();
const mockNewCart = ResultCartFactory(); const mockPrice = StripePriceFactory();
const mockPrice = StripePriceFactory();
beforeEach(async () => {
jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockOldCart); jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockOldCart);
jest.spyOn(cartManager, 'createCart').mockResolvedValue(mockNewCart);
jest jest
.spyOn(productConfigurationManager, 'retrieveStripePrice') .spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice); .mockResolvedValue(mockPrice);
});
it('fetches old cart and creates new cart with same details', async () => {
jest jest
.spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice') .spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice')
.mockResolvedValue(undefined); .mockResolvedValue(undefined);
jest.spyOn(cartManager, 'createCart').mockResolvedValue(mockNewCart);
const result = await cartService.restartCart(mockOldCart.id); const result = await cartService.restartCart(mockOldCart.id);
@ -351,16 +311,10 @@ describe('CartService', () => {
}); });
it('throws an error when couponCode is invalid', async () => { it('throws an error when couponCode is invalid', async () => {
const mockOldCart = ResultCartFactory({
couponCode: faker.word.noun(),
});
const mockNewCart = ResultCartFactory();
jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockOldCart);
jest.spyOn(cartManager, 'createCart').mockResolvedValue(mockNewCart);
jest jest
.spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice') .spyOn(promotionCodeManager, 'assertValidPromotionCodeNameForPrice')
.mockRejectedValue(undefined); .mockRejectedValue(undefined);
jest.spyOn(cartManager, 'createCart').mockResolvedValue(mockNewCart);
await expect(() => await expect(() =>
cartService.restartCart(mockOldCart.id) cartService.restartCart(mockOldCart.id)

Просмотреть файл

@ -161,7 +161,7 @@ export class CartService {
oldCart.interval as SubplatInterval oldCart.interval as SubplatInterval
); );
this.promotionCodeManager.assertValidPromotionCodeNameForPrice( await this.promotionCodeManager.assertValidPromotionCodeNameForPrice(
oldCart.couponCode, oldCart.couponCode,
price price
); );

Просмотреть файл

@ -51,6 +51,10 @@ import {
MockStripeConfigProvider, MockStripeConfigProvider,
AccountCustomerManager, AccountCustomerManager,
} from '@fxa/payments/stripe'; } from '@fxa/payments/stripe';
import {
MockProfileClientConfigProvider,
ProfileClient,
} from '@fxa/profile/client';
import { AccountManager } from '@fxa/shared/account/account'; import { AccountManager } from '@fxa/shared/account/account';
import { import {
MockStrapiClientConfigProvider, MockStrapiClientConfigProvider,
@ -62,7 +66,13 @@ import {
CartEligibilityStatus, CartEligibilityStatus,
MockAccountDatabaseNestFactory, MockAccountDatabaseNestFactory,
} from '@fxa/shared/db/mysql/account'; } from '@fxa/shared/db/mysql/account';
import { LOGGER_PROVIDER } from '@fxa/shared/log';
import { MockStatsDProvider, StatsDService } from '@fxa/shared/metrics/statsd'; import { MockStatsDProvider, StatsDService } from '@fxa/shared/metrics/statsd';
import {
MockNotifierSnsConfigProvider,
NotifierService,
NotifierSnsProvider,
} from '@fxa/shared/notifier';
import { import {
CartEligibilityMismatchError, CartEligibilityMismatchError,
CartTotalMismatchError, CartTotalMismatchError,
@ -80,14 +90,21 @@ describe('CheckoutService', () => {
let customerManager: CustomerManager; let customerManager: CustomerManager;
let eligibilityService: EligibilityService; let eligibilityService: EligibilityService;
let invoiceManager: InvoiceManager; let invoiceManager: InvoiceManager;
let mockStatsd: StatsD;
let paymentMethodManager: PaymentMethodManager; let paymentMethodManager: PaymentMethodManager;
let paypalBillingAgreementManager: PaypalBillingAgreementManager; let paypalBillingAgreementManager: PaypalBillingAgreementManager;
let paypalCustomerManager: PaypalCustomerManager; let paypalCustomerManager: PaypalCustomerManager;
let privateMethod: any;
let productConfigurationManager: ProductConfigurationManager; let productConfigurationManager: ProductConfigurationManager;
let profileClient: ProfileClient;
let promotionCodeManager: PromotionCodeManager; let promotionCodeManager: PromotionCodeManager;
let statsd: StatsD;
let subscriptionManager: SubscriptionManager; let subscriptionManager: SubscriptionManager;
const mockLogger = {
error: jest.fn(),
debug: jest.fn(),
};
beforeEach(async () => { beforeEach(async () => {
const moduleRef = await Test.createTestingModule({ const moduleRef = await Test.createTestingModule({
providers: [ providers: [
@ -101,9 +118,13 @@ describe('CheckoutService', () => {
InvoiceManager, InvoiceManager,
MockAccountDatabaseNestFactory, MockAccountDatabaseNestFactory,
MockFirestoreProvider, MockFirestoreProvider,
MockNotifierSnsConfigProvider,
MockProfileClientConfigProvider,
MockStatsDProvider, MockStatsDProvider,
MockStrapiClientConfigProvider, MockStrapiClientConfigProvider,
MockStripeConfigProvider, MockStripeConfigProvider,
NotifierService,
NotifierSnsProvider,
PaymentMethodManager, PaymentMethodManager,
PaypalBillingAgreementManager, PaypalBillingAgreementManager,
PayPalClient, PayPalClient,
@ -112,11 +133,16 @@ describe('CheckoutService', () => {
PriceManager, PriceManager,
ProductConfigurationManager, ProductConfigurationManager,
ProductManager, ProductManager,
ProfileClient,
PromotionCodeManager, PromotionCodeManager,
StrapiClient, StrapiClient,
StripeClient, StripeClient,
StripeConfig, StripeConfig,
SubscriptionManager, SubscriptionManager,
{
provide: LOGGER_PROVIDER,
useValue: mockLogger,
},
], ],
}).compile(); }).compile();
@ -127,14 +153,18 @@ describe('CheckoutService', () => {
customerManager = moduleRef.get(CustomerManager); customerManager = moduleRef.get(CustomerManager);
eligibilityService = moduleRef.get(EligibilityService); eligibilityService = moduleRef.get(EligibilityService);
invoiceManager = moduleRef.get(InvoiceManager); invoiceManager = moduleRef.get(InvoiceManager);
mockStatsd = moduleRef.get(StatsDService);
paymentMethodManager = moduleRef.get(PaymentMethodManager); paymentMethodManager = moduleRef.get(PaymentMethodManager);
paypalBillingAgreementManager = moduleRef.get( paypalBillingAgreementManager = moduleRef.get(
PaypalBillingAgreementManager PaypalBillingAgreementManager
); );
paypalCustomerManager = moduleRef.get(PaypalCustomerManager); paypalCustomerManager = moduleRef.get(PaypalCustomerManager);
privateMethod = jest
.spyOn(checkoutService as any, 'customerChanged')
.mockResolvedValue({});
profileClient = moduleRef.get(ProfileClient);
productConfigurationManager = moduleRef.get(ProductConfigurationManager); productConfigurationManager = moduleRef.get(ProductConfigurationManager);
promotionCodeManager = moduleRef.get(PromotionCodeManager); promotionCodeManager = moduleRef.get(PromotionCodeManager);
statsd = moduleRef.get(StatsDService);
subscriptionManager = moduleRef.get(SubscriptionManager); subscriptionManager = moduleRef.get(SubscriptionManager);
}); });
@ -415,29 +445,31 @@ describe('CheckoutService', () => {
}); });
describe('postPaySteps', () => { describe('postPaySteps', () => {
const mockUid = faker.string.uuid();
const mockSubscription = StripeResponseFactory(StripeSubscriptionFactory());
beforeEach(async () => {
jest.spyOn(customerManager, 'setTaxId').mockResolvedValue();
jest.spyOn(profileClient, 'deleteCache').mockResolvedValue('test');
});
it('success', async () => { it('success', async () => {
const mockCart = ResultCartFactory(); const mockCart = ResultCartFactory();
const mockSubscription = StripeResponseFactory(
StripeSubscriptionFactory()
);
jest.spyOn(customerManager, 'setTaxId').mockResolvedValue(); await checkoutService.postPaySteps(mockCart, mockSubscription, mockUid);
await checkoutService.postPaySteps(mockCart, mockSubscription);
expect(customerManager.setTaxId).toHaveBeenCalledWith( expect(customerManager.setTaxId).toHaveBeenCalledWith(
mockSubscription.customer, mockSubscription.customer,
mockSubscription.currency mockSubscription.currency
); );
expect(privateMethod).toHaveBeenCalled();
}); });
it('success - adds coupon code to subscription metadata if it exists', async () => { it('success - adds coupon code to subscription metadata if it exists', async () => {
const mockCart = ResultCartFactory({ const mockCart = ResultCartFactory({
couponCode: faker.string.uuid(), couponCode: faker.string.uuid(),
}); });
const mockSubscription = StripeResponseFactory(
StripeSubscriptionFactory()
);
const mockUpdatedSubscription = StripeResponseFactory( const mockUpdatedSubscription = StripeResponseFactory(
StripeSubscriptionFactory({ StripeSubscriptionFactory({
metadata: { metadata: {
@ -447,17 +479,17 @@ describe('CheckoutService', () => {
}) })
); );
jest.spyOn(customerManager, 'setTaxId').mockResolvedValue();
jest jest
.spyOn(subscriptionManager, 'update') .spyOn(subscriptionManager, 'update')
.mockResolvedValue(mockUpdatedSubscription); .mockResolvedValue(mockUpdatedSubscription);
await checkoutService.postPaySteps(mockCart, mockSubscription); await checkoutService.postPaySteps(mockCart, mockSubscription, mockUid);
expect(customerManager.setTaxId).toHaveBeenCalledWith( expect(customerManager.setTaxId).toHaveBeenCalledWith(
mockSubscription.customer, mockSubscription.customer,
mockSubscription.currency mockSubscription.currency
); );
expect(privateMethod).toHaveBeenCalled();
expect(subscriptionManager.update).toHaveBeenCalledWith( expect(subscriptionManager.update).toHaveBeenCalledWith(
mockSubscription.id, mockSubscription.id,
{ {
@ -497,6 +529,7 @@ describe('CheckoutService', () => {
jest.spyOn(checkoutService, 'prePaySteps').mockResolvedValue({ jest.spyOn(checkoutService, 'prePaySteps').mockResolvedValue({
uid: mockCart.uid as string, uid: mockCart.uid as string,
customer: mockCustomer, customer: mockCustomer,
email: faker.internet.email(),
enableAutomaticTax: true, enableAutomaticTax: true,
promotionCode: mockPromotionCode, promotionCode: mockPromotionCode,
price: mockPrice, price: mockPrice,
@ -505,7 +538,7 @@ describe('CheckoutService', () => {
.spyOn(paymentMethodManager, 'attach') .spyOn(paymentMethodManager, 'attach')
.mockResolvedValue(mockPaymentMethod); .mockResolvedValue(mockPaymentMethod);
jest.spyOn(customerManager, 'update').mockResolvedValue(mockCustomer); jest.spyOn(customerManager, 'update').mockResolvedValue(mockCustomer);
jest.spyOn(mockStatsd, 'increment'); jest.spyOn(statsd, 'increment');
jest jest
.spyOn(subscriptionManager, 'create') .spyOn(subscriptionManager, 'create')
.mockResolvedValue(mockSubscription); .mockResolvedValue(mockSubscription);
@ -552,12 +585,9 @@ describe('CheckoutService', () => {
}); });
it('increments the statsd counter', async () => { it('increments the statsd counter', async () => {
expect(mockStatsd.increment).toHaveBeenCalledWith( expect(statsd.increment).toHaveBeenCalledWith('stripe_subscription', {
'stripe_subscription', payment_provider: 'stripe',
{ });
payment_provider: 'stripe',
}
);
}); });
it('creates the subscription', async () => { it('creates the subscription', async () => {
@ -618,6 +648,7 @@ describe('CheckoutService', () => {
jest.spyOn(checkoutService, 'prePaySteps').mockResolvedValue({ jest.spyOn(checkoutService, 'prePaySteps').mockResolvedValue({
uid: mockCart.uid as string, uid: mockCart.uid as string,
customer: mockCustomer, customer: mockCustomer,
email: faker.internet.email(),
enableAutomaticTax: true, enableAutomaticTax: true,
promotionCode: mockPromotionCode, promotionCode: mockPromotionCode,
price: mockPrice, price: mockPrice,
@ -628,7 +659,7 @@ describe('CheckoutService', () => {
jest jest
.spyOn(paypalBillingAgreementManager, 'retrieveOrCreateId') .spyOn(paypalBillingAgreementManager, 'retrieveOrCreateId')
.mockResolvedValue(mockBillingAgreementId); .mockResolvedValue(mockBillingAgreementId);
jest.spyOn(mockStatsd, 'increment'); jest.spyOn(statsd, 'increment');
jest jest
.spyOn(subscriptionManager, 'create') .spyOn(subscriptionManager, 'create')
.mockResolvedValue(mockSubscription); .mockResolvedValue(mockSubscription);
@ -668,12 +699,9 @@ describe('CheckoutService', () => {
}); });
it('increments the statsd counter', async () => { it('increments the statsd counter', async () => {
expect(mockStatsd.increment).toHaveBeenCalledWith( expect(statsd.increment).toHaveBeenCalledWith('stripe_subscription', {
'stripe_subscription', payment_provider: 'paypal',
{ });
payment_provider: 'paypal',
}
);
}); });
it('creates the subscription', async () => { it('creates the subscription', async () => {

Просмотреть файл

@ -27,9 +27,11 @@ import {
StripeCustomer, StripeCustomer,
StripePromotionCode, StripePromotionCode,
} from '@fxa/payments/stripe'; } from '@fxa/payments/stripe';
import { ProfileClient } from '@fxa/profile/client';
import { AccountManager } from '@fxa/shared/account/account'; import { AccountManager } from '@fxa/shared/account/account';
import { ProductConfigurationManager } from '@fxa/shared/cms'; import { ProductConfigurationManager } from '@fxa/shared/cms';
import { StatsDService } from '@fxa/shared/metrics/statsd'; import { StatsDService } from '@fxa/shared/metrics/statsd';
import { NotifierService } from '@fxa/shared/notifier';
import { import {
CartTotalMismatchError, CartTotalMismatchError,
CartEligibilityMismatchError, CartEligibilityMismatchError,
@ -51,15 +53,32 @@ export class CheckoutService {
private customerManager: CustomerManager, private customerManager: CustomerManager,
private eligibilityService: EligibilityService, private eligibilityService: EligibilityService,
private invoiceManager: InvoiceManager, private invoiceManager: InvoiceManager,
private notifierService: NotifierService,
private paymentMethodManager: PaymentMethodManager, private paymentMethodManager: PaymentMethodManager,
private paypalBillingAgreementManager: PaypalBillingAgreementManager, private paypalBillingAgreementManager: PaypalBillingAgreementManager,
private paypalCustomerManager: PaypalCustomerManager, private paypalCustomerManager: PaypalCustomerManager,
private productConfigurationManager: ProductConfigurationManager, private productConfigurationManager: ProductConfigurationManager,
private profileClient: ProfileClient,
private promotionCodeManager: PromotionCodeManager, private promotionCodeManager: PromotionCodeManager,
private subscriptionManager: SubscriptionManager, private subscriptionManager: SubscriptionManager,
@Inject(StatsDService) private statsd: StatsD @Inject(StatsDService) private statsd: StatsD
) {} ) {}
/**
* Reload the customer data to reflect a change.
*/
private async customerChanged(uid: string) {
await this.profileClient.deleteCache(uid);
this.notifierService.send({
event: 'profileDataChange',
data: {
ts: Date.now() / 1000,
uid,
},
});
}
async prePaySteps(cart: ResultCart, customerData: CheckoutCustomerData) { async prePaySteps(cart: ResultCart, customerData: CheckoutCustomerData) {
const taxAddress = cart.taxAddress as any as TaxAddress; const taxAddress = cart.taxAddress as any as TaxAddress;
@ -182,18 +201,23 @@ export class CheckoutService {
return { return {
uid: uid, uid: uid,
customer, customer,
email: cart.email,
enableAutomaticTax, enableAutomaticTax,
promotionCode, promotionCode,
price, price,
}; };
} }
async postPaySteps(cart: ResultCart, subscription: StripeSubscription) { async postPaySteps(
cart: ResultCart,
subscription: StripeSubscription,
uid: string
) {
const { customer: customerId, currency } = subscription; const { customer: customerId, currency } = subscription;
await this.customerManager.setTaxId(customerId, currency); await this.customerManager.setTaxId(customerId, currency);
// TODO: call customerChanged await this.customerChanged(uid);
if (cart.couponCode) { if (cart.couponCode) {
const subscriptionMetadata = { const subscriptionMetadata = {
@ -214,7 +238,7 @@ export class CheckoutService {
paymentMethodId: string, paymentMethodId: string,
customerData: CheckoutCustomerData customerData: CheckoutCustomerData
) { ) {
const { customer, enableAutomaticTax, promotionCode, price } = const { uid, customer, enableAutomaticTax, promotionCode, price } =
await this.prePaySteps(cart, customerData); await this.prePaySteps(cart, customerData);
await this.paymentMethodManager.attach(paymentMethodId, { await this.paymentMethodManager.attach(paymentMethodId, {
@ -281,7 +305,7 @@ export class CheckoutService {
); );
} }
await this.postPaySteps(cart, subscription); await this.postPaySteps(cart, subscription, uid);
} }
async payWithPaypal( async payWithPaypal(
@ -360,6 +384,6 @@ export class CheckoutService {
await this.paypalBillingAgreementManager.cancel(billingAgreementId); await this.paypalBillingAgreementManager.cancel(billingAgreementId);
} }
await this.postPaySteps(cart, subscription); await this.postPaySteps(cart, subscription, uid);
} }
} }

Просмотреть файл

@ -1,25 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Injectable } from '@nestjs/common';
@Injectable()
export class StripeService {
constructor() {}
// TODO: this method should be moved down to the manager layer
async customerChanged(uid: string, email: string) {
// @todo - Unblocked by FXA-9274
//const devices = await this.db.devices(uid);
// @todo - Unblocked by FXA-9275
//await this.profile.deleteCache(uid);
// @todo - Unblocked by FXA-9276
//await this.push.notifyProfileUpdated(uid, devices);
// @todo - Unblocked by FXA-9277
//this.log.notifyAttachedServices('profileDataChange', {} as any, {
// uid,
// email,
//});
}
}

Просмотреть файл

@ -25,22 +25,25 @@ import {
PromotionCodeManager, PromotionCodeManager,
SubscriptionManager, SubscriptionManager,
} from '@fxa/payments/customer'; } from '@fxa/payments/customer';
import { PaymentsGleanManager } from '@fxa/payments/metrics';
import { PaymentsGleanFactory } from '@fxa/payments/metrics/provider';
import { AccountCustomerManager, StripeClient } from '@fxa/payments/stripe'; import { AccountCustomerManager, StripeClient } from '@fxa/payments/stripe';
import { ProfileClient } from '@fxa/profile/client';
import { AccountManager } from '@fxa/shared/account/account'; import { AccountManager } from '@fxa/shared/account/account';
import { ProductConfigurationManager, StrapiClient } from '@fxa/shared/cms'; import { ProductConfigurationManager, StrapiClient } from '@fxa/shared/cms';
import { FirestoreProvider } from '@fxa/shared/db/firestore'; import { FirestoreProvider } from '@fxa/shared/db/firestore';
import { AccountDatabaseNestFactory } from '@fxa/shared/db/mysql/account'; import { AccountDatabaseNestFactory } from '@fxa/shared/db/mysql/account';
import { GeoDBManager, GeoDBNestFactory } from '@fxa/shared/geodb'; import { GeoDBManager, GeoDBNestFactory } from '@fxa/shared/geodb';
import { LocalizerRscFactoryProvider } from '@fxa/shared/l10n/server'; import { LocalizerRscFactoryProvider } from '@fxa/shared/l10n/server';
import { logger, LOGGER_PROVIDER } from '@fxa/shared/log';
import { StatsDProvider } from '@fxa/shared/metrics/statsd'; import { StatsDProvider } from '@fxa/shared/metrics/statsd';
import { PaymentsGleanManager } from '@fxa/payments/metrics'; import { NotifierService, NotifierSnsProvider } from '@fxa/shared/notifier';
import { RootConfig } from './config'; import { RootConfig } from './config';
import { NextJSActionsService } from './nextjs-actions.service'; import { NextJSActionsService } from './nextjs-actions.service';
import { validate } from '../config.utils'; import { validate } from '../config.utils';
import { CurrencyManager } from '@fxa/payments/currency'; import { CurrencyManager } from '@fxa/payments/currency';
import { PaymentsEmitterService } from '../emitter/emitter.service'; import { PaymentsEmitterService } from '../emitter/emitter.service';
import { PaymentsGleanFactory } from '@fxa/payments/metrics/provider';
@Module({ @Module({
imports: [ imports: [
@ -80,6 +83,8 @@ import { PaymentsGleanFactory } from '@fxa/payments/metrics/provider';
InvoiceManager, InvoiceManager,
LocalizerRscFactoryProvider, LocalizerRscFactoryProvider,
NextJSActionsService, NextJSActionsService,
NotifierService,
NotifierSnsProvider,
PaymentMethodManager, PaymentMethodManager,
PaypalBillingAgreementManager, PaypalBillingAgreementManager,
PayPalClient, PayPalClient,
@ -87,6 +92,7 @@ import { PaymentsGleanFactory } from '@fxa/payments/metrics/provider';
PriceManager, PriceManager,
ProductConfigurationManager, ProductConfigurationManager,
ProductManager, ProductManager,
ProfileClient,
PromotionCodeManager, PromotionCodeManager,
StatsDProvider, StatsDProvider,
StrapiClient, StrapiClient,
@ -95,6 +101,7 @@ import { PaymentsGleanFactory } from '@fxa/payments/metrics/provider';
PaymentsGleanFactory, PaymentsGleanFactory,
PaymentsGleanManager, PaymentsGleanManager,
PaymentsEmitterService, PaymentsEmitterService,
{ provide: LOGGER_PROVIDER, useValue: logger },
], ],
}) })
export class AppModule {} export class AppModule {}

Просмотреть файл

@ -14,6 +14,8 @@ import { FirestoreConfig } from 'libs/shared/db/firestore/src/lib/firestore.conf
import { StatsDConfig } from 'libs/shared/metrics/statsd/src/lib/statsd.config'; import { StatsDConfig } from 'libs/shared/metrics/statsd/src/lib/statsd.config';
import { PaymentsGleanConfig } from '@fxa/payments/metrics'; import { PaymentsGleanConfig } from '@fxa/payments/metrics';
import { CurrencyConfig } from 'libs/payments/currency/src/lib/currency.config'; import { CurrencyConfig } from 'libs/payments/currency/src/lib/currency.config';
import { ProfileClientConfig } from '@fxa/profile/client';
import { NotifierSnsConfig } from '@fxa/shared/notifier';
export class RootConfig { export class RootConfig {
@Type(() => MySQLConfig) @Type(() => MySQLConfig)
@ -64,4 +66,14 @@ export class RootConfig {
@ValidateNested() @ValidateNested()
@IsDefined() @IsDefined()
public readonly gleanConfig!: Partial<PaymentsGleanConfig>; public readonly gleanConfig!: Partial<PaymentsGleanConfig>;
@Type(() => ProfileClientConfig)
@ValidateNested()
@IsDefined()
public readonly profileClientConfig!: Partial<ProfileClientConfig>;
@Type(() => NotifierSnsConfig)
@ValidateNested()
@IsDefined()
public readonly notifierSnsConfig!: Partial<NotifierSnsConfig>;
} }

Просмотреть файл

@ -1,8 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Test } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { LOGGER_PROVIDER } from '@fxa/shared/log'; import { LOGGER_PROVIDER } from '@fxa/shared/log';
import { ProfileClient } from './profile.client'; import { ProfileClient } from './profile.client';
import { MockProfileClientConfigProvider } from './profile.config'; import { MockProfileClientConfigProvider } from './profile.config';
@ -47,11 +47,9 @@ describe('ProfileClient', () => {
jest jest
.spyOn(profileClient as any, 'makeRequest') .spyOn(profileClient as any, 'makeRequest')
.mockRejectedValue(new Error('fail')); .mockRejectedValue(new Error('fail'));
try {
await profileClient.deleteCache('test'); await expect(profileClient.deleteCache('test')).rejects.toThrow();
} catch (e) { expect(mockLogger.error).toHaveBeenCalled();
expect(mockLogger.error).toHaveBeenCalled();
}
}); });
}); });
@ -65,11 +63,12 @@ describe('ProfileClient', () => {
jest jest
.spyOn(profileClient as any, 'makeRequest') .spyOn(profileClient as any, 'makeRequest')
.mockRejectedValue(new Error('fail')); .mockRejectedValue(new Error('fail'));
try {
await profileClient.updateDisplayName('test', 'test'); await expect(
} catch (e) { profileClient.updateDisplayName('test', 'test')
expect(mockLogger.error).toHaveBeenCalled(); ).rejects.toThrow();
}
expect(mockLogger.error).toHaveBeenCalled();
}); });
}); });
}); });

Просмотреть файл

@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { LOGGER_PROVIDER } from '@fxa/shared/log'; import { LOGGER_PROVIDER } from '@fxa/shared/log';
import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { LoggerService } from '@nestjs/common';
import Agent from 'agentkeepalive'; import Agent from 'agentkeepalive';
import axios, { AxiosInstance } from 'axios'; import axios, { AxiosInstance } from 'axios';
import { ProfileClientConfig } from './profile.config'; import { ProfileClientConfig } from './profile.config';

Просмотреть файл

@ -7,7 +7,7 @@ import { faker } from '@faker-js/faker';
import { Provider } from '@nestjs/common'; import { Provider } from '@nestjs/common';
export class ProfileClientConfig { export class ProfileClientConfig {
@IsUrl() @IsUrl({ require_tld: false })
public readonly url!: string; public readonly url!: string;
@IsString() @IsString()

Просмотреть файл

@ -5,7 +5,6 @@
import { LOGGER_PROVIDER } from '@fxa/shared/log'; import { LOGGER_PROVIDER } from '@fxa/shared/log';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { StatsD } from 'hot-shots'; import { StatsD } from 'hot-shots';
import { MockStatsDProvider, StatsDService } from '@fxa/shared/metrics/statsd'; import { MockStatsDProvider, StatsDService } from '@fxa/shared/metrics/statsd';
import { NotifierService } from './notifier.service'; import { NotifierService } from './notifier.service';
import { import {

Просмотреть файл

@ -4,7 +4,8 @@
import { LOGGER_PROVIDER } from '@fxa/shared/log'; import { LOGGER_PROVIDER } from '@fxa/shared/log';
import { StatsDService } from '@fxa/shared/metrics/statsd'; import { StatsDService } from '@fxa/shared/metrics/statsd';
import { Inject, Injectable, LoggerService } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { LoggerService } from '@nestjs/common';
import { AWSError, SNS } from 'aws-sdk'; import { AWSError, SNS } from 'aws-sdk';
import { StatsD } from 'hot-shots'; import { StatsD } from 'hot-shots';

Просмотреть файл

@ -9,7 +9,7 @@ export class NotifierSnsConfig {
@IsString() @IsString()
public readonly snsTopicArn!: string; public readonly snsTopicArn!: string;
@IsUrl() @IsUrl({ require_tld: false })
public readonly snsTopicEndpoint!: string; public readonly snsTopicEndpoint!: string;
} }

Просмотреть файл

@ -5,8 +5,9 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { SNS } from 'aws-sdk'; import { SNS } from 'aws-sdk';
import { MockNotifierSnsConfig } from './notifier.sns.config';
import { import {
NotifierSnsFactory, LegacyNotifierSnsFactory,
NotifierSnsService, NotifierSnsService,
} from './notifier.sns.provider'; } from './notifier.sns.provider';
@ -22,14 +23,10 @@ jest.mock('aws-sdk', () => {
describe('NotifierSnsFactory', () => { describe('NotifierSnsFactory', () => {
let sns: SNS; let sns: SNS;
const mockConfig = {
snsTopicArn: 'arn:aws:sns:us-east-1:100010001000:fxa-account-change-dev',
snsTopicEndpoint: 'http://localhost:4100/',
};
const mockConfigService = { const mockConfigService = {
get: jest.fn().mockImplementation((key: string) => { get: jest.fn().mockImplementation((key: string) => {
if (key === 'notifier.sns') { if (key === 'notifier.sns') {
return mockConfig; return MockNotifierSnsConfig;
} }
return null; return null;
}), }),
@ -39,11 +36,8 @@ describe('NotifierSnsFactory', () => {
jest.clearAllMocks(); jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [ providers: [
NotifierSnsFactory, LegacyNotifierSnsFactory,
{ { provide: ConfigService, useValue: mockConfigService },
provide: ConfigService,
useValue: mockConfigService,
},
], ],
}).compile(); }).compile();
@ -53,8 +47,8 @@ describe('NotifierSnsFactory', () => {
it('should provide statsd', async () => { it('should provide statsd', async () => {
expect(sns).toBeDefined(); expect(sns).toBeDefined();
expect(mockSNS).toBeCalledWith({ expect(mockSNS).toBeCalledWith({
endpoint: mockConfig.snsTopicEndpoint, endpoint: MockNotifierSnsConfig.snsTopicEndpoint,
region: 'us-east-1', region: 'us-west-2',
}); });
}); });
}); });

Просмотреть файл

@ -25,7 +25,19 @@ export function setupSns(config: NotifierSnsConfig) {
* Factory for providing access to SNS * Factory for providing access to SNS
*/ */
export const NotifierSnsService = Symbol('NOTIFIER_SNS'); export const NotifierSnsService = Symbol('NOTIFIER_SNS');
export const NotifierSnsFactory: Provider<SNS> = { export const NotifierSnsProvider: Provider<SNS> = {
provide: NotifierSnsService,
useFactory: (config: NotifierSnsConfig) => {
if (config == null) {
throw new Error('Could not locate notifier.sns config');
}
const sns = setupSns(config);
return sns;
},
inject: [NotifierSnsConfig],
};
export const LegacyNotifierSnsFactory: Provider<SNS> = {
provide: NotifierSnsService, provide: NotifierSnsService,
useFactory: (configService: ConfigService) => { useFactory: (configService: ConfigService) => {
const config = configService.get<NotifierSnsConfig>('notifier.sns'); const config = configService.get<NotifierSnsConfig>('notifier.sns');

Просмотреть файл

@ -17,7 +17,7 @@ import { LegacyStatsDProvider } from '@fxa/shared/metrics/statsd';
import { MozLoggerService } from '@fxa/shared/mozlog'; import { MozLoggerService } from '@fxa/shared/mozlog';
import { import {
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
NotifierSnsFactory, LegacyNotifierSnsFactory,
} from '@fxa/shared/notifier'; } from '@fxa/shared/notifier';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
@ -89,8 +89,8 @@ const version = getVersionInfo(__dirname);
provide: LOGGER_PROVIDER, provide: LOGGER_PROVIDER,
useClass: MozLoggerService, useClass: MozLoggerService,
}, },
NotifierSnsFactory,
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
LegacyNotifierSnsFactory,
LegacyStatsDProvider, LegacyStatsDProvider,
], ],
}) })

Просмотреть файл

@ -7,7 +7,7 @@ import { LegacyStatsDProvider } from '@fxa/shared/metrics/statsd';
import { MozLoggerService } from '@fxa/shared/mozlog'; import { MozLoggerService } from '@fxa/shared/mozlog';
import { import {
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
NotifierSnsFactory, LegacyNotifierSnsFactory,
} from '@fxa/shared/notifier'; } from '@fxa/shared/notifier';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { BackendModule } from '../backend/backend.module'; import { BackendModule } from '../backend/backend.module';
@ -31,8 +31,8 @@ import { RelyingPartyResolver } from './relying-party/relying-party.resolver';
AccountResolver, AccountResolver,
EmailBounceResolver, EmailBounceResolver,
LegacyStatsDProvider, LegacyStatsDProvider,
NotifierSnsFactory,
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
LegacyNotifierSnsFactory,
{ {
provide: LOGGER_PROVIDER, provide: LOGGER_PROVIDER,
useClass: MozLoggerService, useClass: MozLoggerService,

Просмотреть файл

@ -7,7 +7,7 @@ import { LegacyStatsDProvider } from '@fxa/shared/metrics/statsd';
import { MozLoggerService } from '@fxa/shared/mozlog'; import { MozLoggerService } from '@fxa/shared/mozlog';
import { import {
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
NotifierSnsFactory, LegacyNotifierSnsFactory,
} from '@fxa/shared/notifier'; } from '@fxa/shared/notifier';
import { HealthModule } from 'fxa-shared/nestjs/health/health.module'; import { HealthModule } from 'fxa-shared/nestjs/health/health.module';
@ -56,8 +56,8 @@ const version = getVersionInfo(__dirname);
providers: [ providers: [
LegacyStatsDProvider, LegacyStatsDProvider,
MozLoggerService, MozLoggerService,
NotifierSnsFactory,
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
LegacyNotifierSnsFactory,
ComplexityPlugin, ComplexityPlugin,
{ {
provide: LOGGER_PROVIDER, provide: LOGGER_PROVIDER,

Просмотреть файл

@ -13,7 +13,7 @@ import { LegacyStatsDProvider } from '@fxa/shared/metrics/statsd';
import { MozLoggerService } from '@fxa/shared/mozlog'; import { MozLoggerService } from '@fxa/shared/mozlog';
import { import {
LegacyNotifierServiceProvider, LegacyNotifierServiceProvider,
NotifierSnsFactory, LegacyNotifierSnsFactory,
} from '@fxa/shared/notifier'; } from '@fxa/shared/notifier';
import { import {
HttpException, HttpException,
@ -59,11 +59,11 @@ export const GraphQLConfigFactory = async (
AccountResolver, AccountResolver,
ClientInfoResolver, ClientInfoResolver,
CustomsService, CustomsService,
LegacyNotifierServiceProvider,
LegacyNotifierSnsFactory,
LegacyStatsDProvider, LegacyStatsDProvider,
LegalResolver, LegalResolver,
LegacyNotifierServiceProvider,
MozLoggerService, MozLoggerService,
NotifierSnsFactory,
SentryPlugin, SentryPlugin,
SessionResolver, SessionResolver,
SubscriptionResolver, SubscriptionResolver,