Skip to content

Commit

Permalink
Refactor: Remove Webhook Service, use underlying services directly (#…
Browse files Browse the repository at this point in the history
…1600)

* add factories, remove webhook service

* use case usage

* checkout calculate taxes webhook refactor;

* wip

* Order Calculate Taxes handler refactor

* refactor order cancelled and confirmed webhooks
  • Loading branch information
lkostrowski authored Oct 1, 2024
1 parent f1025fa commit 630c68a
Show file tree
Hide file tree
Showing 12 changed files with 388 additions and 605 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-taxis-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-avatax": patch
---

Refactored so called "webhook service" class. Now each webhook creates it's own dependencies. It's a part of larger refactor that aims to simplify app's architecture. No functional change is expected.
64 changes: 0 additions & 64 deletions apps/avatax/src/modules/avatax/avatax-webhook.service.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AvataxClient } from "@/modules/avatax/avatax-client";
import { AvataxConfig } from "@/modules/avatax/avatax-connection-schema";
import { AvataxSdkClientFactory } from "@/modules/avatax/avatax-sdk-client-factory";
import { AvataxOrderCancelledAdapter } from "@/modules/avatax/order-cancelled/avatax-order-cancelled-adapter";
import { AvataxOrderCancelledPayloadTransformer } from "@/modules/avatax/order-cancelled/avatax-order-cancelled-payload-transformer";

export const createAvaTaxOrderCancelledAdapterFromConfig = (avataxConfig: AvataxConfig) => {
const avaTaxSdk = new AvataxSdkClientFactory().createClient(avataxConfig);
const avaTaxClient = new AvataxClient(avaTaxSdk);

const avataxOrderCancelledPayloadTransformer = new AvataxOrderCancelledPayloadTransformer();

return new AvataxOrderCancelledAdapter(avaTaxClient, avataxOrderCancelledPayloadTransformer);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { AvataxCalculationDateResolver } from "@/modules/avatax/avatax-calculation-date-resolver";
import { AvataxClient } from "@/modules/avatax/avatax-client";
import { AvataxConfig } from "@/modules/avatax/avatax-connection-schema";
import { AvataxDocumentCodeResolver } from "@/modules/avatax/avatax-document-code-resolver";
import { AvataxEntityTypeMatcher } from "@/modules/avatax/avatax-entity-type-matcher";
import { AvataxSdkClientFactory } from "@/modules/avatax/avatax-sdk-client-factory";
import { AvataxOrderConfirmedAdapter } from "@/modules/avatax/order-confirmed/avatax-order-confirmed-adapter";
import { AvataxOrderConfirmedPayloadService } from "@/modules/avatax/order-confirmed/avatax-order-confirmed-payload.service";
import { AvataxOrderConfirmedPayloadTransformer } from "@/modules/avatax/order-confirmed/avatax-order-confirmed-payload-transformer";
import { AvataxOrderConfirmedResponseTransformer } from "@/modules/avatax/order-confirmed/avatax-order-confirmed-response-transformer";
import { SaleorOrderToAvataxLinesTransformer } from "@/modules/avatax/order-confirmed/saleor-order-to-avatax-lines-transformer";

/**
* Wrap all deps to create this service from minimum possible value (avatax config)
*
* Once we refactor these services to not require such configs, these should be created top level on each webhook
*/
export const createAvaTaxOrderConfirmedAdapterFromAvaTaxConfig = (config: AvataxConfig) => {
const avaTaxSdk = new AvataxSdkClientFactory().createClient(config);
const avaTaxClient = new AvataxClient(avaTaxSdk);
const entityTypeMatcher = new AvataxEntityTypeMatcher(avaTaxClient);

const orderToAvataxLinesTransformer = new SaleorOrderToAvataxLinesTransformer();
const calculationDateResolver = new AvataxCalculationDateResolver();
const documentCodeResolver = new AvataxDocumentCodeResolver();
const avataxOrderConfirmedResponseTransformer = new AvataxOrderConfirmedResponseTransformer();
const orderConfirmedPayloadTransformer = new AvataxOrderConfirmedPayloadTransformer(
orderToAvataxLinesTransformer,
entityTypeMatcher,
calculationDateResolver,
documentCodeResolver,
);

const avataxOrderConfirmedPayloadService = new AvataxOrderConfirmedPayloadService(
orderConfirmedPayloadTransformer,
);

return new AvataxOrderConfirmedAdapter(
avaTaxClient,
avataxOrderConfirmedResponseTransformer,
avataxOrderConfirmedPayloadService,
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { AuthData } from "@saleor/app-sdk/APL";
import { err, ok, Result } from "neverthrow";
import { beforeEach, describe, expect, it, vi } from "vitest";

import { AvataxCalculateTaxesPayloadLinesTransformer } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-payload-lines-transformer";
import { AvataxCalculateTaxesResponseTransformer } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-response-transformer";
import { AvataxCalculateTaxesTaxCodeMatcher } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-tax-code-matcher";
import { SHIPPING_ITEM_CODE } from "@/modules/avatax/calculate-taxes/avatax-shipping-line";
import { ILogWriter, LogWriterContext, NoopLogWriter } from "@/modules/client-logs/log-writer";

Expand All @@ -10,7 +13,6 @@ import { AppConfig } from "../../../lib/app-config";
import { AppConfigExtractor, IAppConfigExtractor } from "../../../lib/app-config-extractor";
import { AvataxClient } from "../../avatax/avatax-client";
import { AvataxSdkClientFactory } from "../../avatax/avatax-sdk-client-factory";
import { AvataxWebhookServiceFactory } from "../../taxes/avatax-webhook-service-factory";
import { CalculateTaxesPayload } from "../../webhooks/payloads/calculate-taxes-payload";
import { CalculateTaxesUseCase } from "./calculate-taxes.use-case";

Expand Down Expand Up @@ -177,6 +179,10 @@ describe("CalculateTaxesUseCase", () => {
return logWriter;
},
},
calculateTaxesResponseTransformer: new AvataxCalculateTaxesResponseTransformer(),
payloadLinesTransformer: new AvataxCalculateTaxesPayloadLinesTransformer(
new AvataxCalculateTaxesTaxCodeMatcher(),
),
});
});

Expand Down Expand Up @@ -209,7 +215,7 @@ describe("CalculateTaxesUseCase", () => {
const error = result._unsafeUnwrapErr();

expect(error).toBeInstanceOf(CalculateTaxesUseCase.ConfigBrokenError);
expect(error.errors![0]).toBeInstanceOf(AvataxWebhookServiceFactory.BrokenConfigurationError);
expect(error.errors![0]).toBeInstanceOf(BaseError);
});

it("Returns XXX error if taxes calculation fails", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { AuthData } from "@saleor/app-sdk/APL";
import * as Sentry from "@sentry/nextjs";
import { captureException } from "@sentry/nextjs";
import { err, fromPromise, Result } from "neverthrow";

import { AvataxClient } from "@/modules/avatax/avatax-client";
import { AvataxConfig } from "@/modules/avatax/avatax-connection-schema";
import { AvataxEntityTypeMatcher } from "@/modules/avatax/avatax-entity-type-matcher";
import { AvataxSdkClientFactory } from "@/modules/avatax/avatax-sdk-client-factory";
import { AvataxCalculateTaxesPayloadService } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-payload.service";
import { AvataxCalculateTaxesPayloadLinesTransformer } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-payload-lines-transformer";
import { AvataxCalculateTaxesPayloadTransformer } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-payload-transformer";
import { AvataxCalculateTaxesResponseTransformer } from "@/modules/avatax/calculate-taxes/avatax-calculate-taxes-response-transformer";
import { AutomaticallyDistributedProductLinesDiscountsStrategy } from "@/modules/avatax/discounts";
import { AvataxTaxCodeMatchesService } from "@/modules/avatax/tax-code/avatax-tax-code-matches.service";
import { ClientLogStoreRequest } from "@/modules/client-logs/client-log";
import { ILogWriterFactory } from "@/modules/client-logs/log-writer-factory";

Expand All @@ -12,7 +20,10 @@ import { BaseError } from "../../../error";
import { AppConfigExtractor, IAppConfigExtractor } from "../../../lib/app-config-extractor";
import { AppConfigurationLogger } from "../../../lib/app-configuration-logger";
import { createLogger } from "../../../logger";
import { AvataxCalculateTaxesResponse } from "../../avatax/calculate-taxes/avatax-calculate-taxes-adapter";
import {
AvataxCalculateTaxesAdapter,
AvataxCalculateTaxesResponse,
} from "../../avatax/calculate-taxes/avatax-calculate-taxes-adapter";
import { TaxIncompletePayloadErrors } from "../../taxes/tax-error";
import { CalculateTaxesPayload } from "../../webhooks/payloads/calculate-taxes-payload";
import { verifyCalculateTaxesPayload } from "../../webhooks/validate-webhook-payload";
Expand All @@ -36,6 +47,8 @@ export class CalculateTaxesUseCase {
private deps: {
configExtractor: IAppConfigExtractor;
logWriterFactory: ILogWriterFactory;
payloadLinesTransformer: AvataxCalculateTaxesPayloadLinesTransformer;
calculateTaxesResponseTransformer: AvataxCalculateTaxesResponseTransformer;
},
) {}

Expand Down Expand Up @@ -83,6 +96,39 @@ export class CalculateTaxesUseCase {
});
}

private async callAvaTax(
payload: CalculateTaxesPayload,
avataxConfig: AvataxConfig,
discountStrategy: AutomaticallyDistributedProductLinesDiscountsStrategy,
authData: AuthData,
) {
/**
* Create local dependencies. They more-or-less need runtime values, like AuthData.
* This is part of the refactor. Later we should refactor these and inject them into use-case
*/
const avaTaxSdk = new AvataxSdkClientFactory().createClient(avataxConfig);
const avaTaxClient = new AvataxClient(avaTaxSdk);

const calculateTaxesAdapter = new AvataxCalculateTaxesAdapter(
avaTaxClient,
this.deps.calculateTaxesResponseTransformer,
);

const payloadService = new AvataxCalculateTaxesPayloadService(
AvataxTaxCodeMatchesService.createFromAuthData(authData),
new AvataxCalculateTaxesPayloadTransformer(
this.deps.payloadLinesTransformer,
new AvataxEntityTypeMatcher(avaTaxClient),
),
);

const avataxModel = await payloadService.getPayload(payload, avataxConfig, discountStrategy);

const response = await calculateTaxesAdapter.send(avataxModel);

return response;
}

async calculateTaxes(
payload: CalculateTaxesPayload,
authData: AuthData,
Expand Down Expand Up @@ -127,60 +173,8 @@ export class CalculateTaxesUseCase {
);
}

const AvataxWebhookServiceFactory = await import(
"../../../modules/taxes/avatax-webhook-service-factory"
).then((m) => m.AvataxWebhookServiceFactory);

const webhookServiceResult = AvataxWebhookServiceFactory.createFromConfig(
config.value,
channelSlug,
).mapErr((innerError) => {
this.logger.warn(
`Error in taxes calculation occurred: ${innerError.name} ${innerError.message}`,
{
error: innerError,
},
);

switch (innerError["constructor"]) {
case AvataxWebhookServiceFactory.BrokenConfigurationError: {
return err(
new CalculateTaxesUseCase.ConfigBrokenError(
"Failed to create instance of AvaTax connection due to invalid config",
{
errors: [innerError],
},
),
);
}
default: {
Sentry.captureException(innerError);
this.logger.fatal("Unhandled error", { error: innerError });

return err(
new CalculateTaxesUseCase.UnhandledError("Unhandled error", { errors: [innerError] }),
);
}
}
});

if (webhookServiceResult.isErr()) {
ClientLogStoreRequest.create({
level: "error",
message: "Failed to calculate taxes. Invalid config",
checkoutOrOrderId: payload.taxBase.sourceObject.id,
channelId: payload.taxBase.channel.slug,
checkoutOrOrder: "checkout",
})
.mapErr(captureException)
.map(logWriter.writeLog);

return webhookServiceResult.error;
}

this.logger.info("Found active connection service. Calculating taxes...");

const { taxProvider } = webhookServiceResult.value;
const providerConfig = config.value.getConfigForChannelSlug(channelSlug);

if (providerConfig.isErr()) {
Expand All @@ -205,11 +199,11 @@ export class CalculateTaxesUseCase {
}

return fromPromise(
taxProvider.calculateTaxes(
this.callAvaTax(
payload,
providerConfig.value.avataxConfig.config,
authData,
this.discountsStrategy,
authData,
),
(err) => {
ClientLogStoreRequest.create({
Expand Down
Loading

0 comments on commit 630c68a

Please sign in to comment.