Skip to content

Commit

Permalink
handle document not found error in avatax during cancel (#1573)
Browse files Browse the repository at this point in the history
* handle document not found error in avatax during cancel

* use error parser

* Add tests

* return response to client
  • Loading branch information
lkostrowski authored Sep 16, 2024
1 parent badd1f5 commit 5f61e62
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/shiny-wombats-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-avatax": patch
---

Add handling for error occurred due to missing document in AvaTax during cancel (voiding)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";

import { AvataxConfigMockGenerator } from "@/modules/avatax/avatax-config-mock-generator";
import { AvataxOrderCancelledAdapter } from "@/modules/avatax/order-cancelled/avatax-order-cancelled-adapter";
import { AvataxOrderCancelledPayloadTransformer } from "@/modules/avatax/order-cancelled/avatax-order-cancelled-payload-transformer";

describe("AvataxOrderCancelledAdapter", () => {
it("Throws DocumentNotFoundError if Avatax returns DocumentNotFoundError from the response of transaction void", async () => {
const adapter = new AvataxOrderCancelledAdapter(
{
async voidTransaction() {
throw {
code: "EntityNotFoundError",
details: [{}],
};
},
},
new AvataxOrderCancelledPayloadTransformer(),
);

try {
await adapter.send({ avataxId: "1" }, new AvataxConfigMockGenerator().generateAvataxConfig());
} catch (e) {
return expect(e).toBeInstanceOf(AvataxOrderCancelledAdapter.DocumentNotFoundError);
}
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { captureException } from "@sentry/nextjs";

import { BaseError } from "@/error";
import { AvataxErrorsParser } from "@/modules/avatax/avatax-errors-parser";
import { AvataxEntityNotFoundError } from "@/modules/taxes/tax-error";

import { createLogger } from "../../../logger";
import { CancelOrderPayload } from "../../taxes/tax-provider-webhook";
import { WebhookAdapter } from "../../taxes/tax-webhook-adapter";
Expand All @@ -10,9 +16,20 @@ export type AvataxOrderCancelledTarget = VoidTransactionArgs;

export class AvataxOrderCancelledAdapter implements WebhookAdapter<{ avataxId: string }, void> {
private logger = createLogger("AvataxOrderCancelledAdapter");
private errorParser = new AvataxErrorsParser((error) => {
captureException(
new Error("AvataxOrderCancelledAdapter: Unhandled error caught from Avatax", {
cause: error,
}),
);
});

static AvaTaxOrderCancelledAdapterError = BaseError.subclass("AvaTaxOrderCancelledAdapterError");
static DocumentNotFoundError =
this.AvaTaxOrderCancelledAdapterError.subclass("DocumentNotFoundError");

constructor(
private avataxClient: AvataxClient,
private avataxClient: Pick<AvataxClient, "voidTransaction">,
private avataxOrderCancelledPayloadTransformer: AvataxOrderCancelledPayloadTransformer,
) {}

Expand Down Expand Up @@ -42,6 +59,26 @@ export class AvataxOrderCancelledAdapter implements WebhookAdapter<{ avataxId: s
avataxId: payload.avataxId,
});
} catch (e) {
const parsedError = this.errorParser.parse(e);

/**
* This can happen when AvaTax doesn't have document on their side.
* We can't do anything about - hence custom handling of this error
*/
if (parsedError instanceof AvataxEntityNotFoundError) {
/**
* TODO Replace with neverthrow one day
*/
throw new AvataxOrderCancelledAdapter.DocumentNotFoundError(
"AvaTax didnt find the document to void",
{
props: {
error: e,
},
},
);
}

const error = normalizeAvaTaxError(e);

this.logger.error("Error voiding the transaction", {
Expand Down
27 changes: 21 additions & 6 deletions apps/avatax/src/pages/api/webhooks/order-cancelled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ObservabilityAttributes } from "@saleor/apps-otel/src/lib/observability
import * as Sentry from "@sentry/nextjs";
import { captureException } from "@sentry/nextjs";

import { AvataxOrderCancelledAdapter } from "@/modules/avatax/order-cancelled/avatax-order-cancelled-adapter";

import { AppConfigExtractor } from "../../../lib/app-config-extractor";
import { AppConfigurationLogger } from "../../../lib/app-configuration-logger";
import { metadataCache, wrapWithMetadataCache } from "../../../lib/app-metadata-cache";
Expand Down Expand Up @@ -139,12 +141,25 @@ export default wrapWithLoggerContext(
.json({ message: `App is not configured properly for order: ${payload.order?.id}` });
}

await taxProvider.cancelOrder(
{
avataxId: cancelledOrderInstance.getAvataxId(),
},
providerConfig.value.avataxConfig.config,
);
try {
await taxProvider.cancelOrder(
{
avataxId: cancelledOrderInstance.getAvataxId(),
},
providerConfig.value.avataxConfig.config,
);
} catch (e) {
// TODO Test once it becomes testable
if (e instanceof AvataxOrderCancelledAdapter.DocumentNotFoundError) {
logger.warn("Document was not found in AvaTax. Responding 400", {
error: e,
});

return res.status(400).send({
message: "AvaTax responded with DocumentNotFound. Please consult AvaTax docs",
});
}
}

logger.info("Order cancelled");

Expand Down

0 comments on commit 5f61e62

Please sign in to comment.