Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Remove Webhook Service, use underlying services directly #1600

Merged
merged 8 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
65 changes: 0 additions & 65 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
Loading