Skip to content

Commit

Permalink
refactor(core): consume 'ProcessPendingInvoiceResult' in exhaustive s…
Browse files Browse the repository at this point in the history
…witch
  • Loading branch information
vindard committed Feb 20, 2024
1 parent 1986270 commit a08a638
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 80 deletions.
4 changes: 0 additions & 4 deletions core/api/src/app/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import * as KratosErrors from "@/services/kratos/errors"
import * as BriaEventErrors from "@/services/bria/errors"
import * as SvixErrors from "@/services/svix/errors"

import * as WalletErrors from "@/app/wallets/errors"

export const ApplicationErrors = {
...SharedErrors,
...DomainErrors,
Expand Down Expand Up @@ -58,6 +56,4 @@ export const ApplicationErrors = {
...LedgerFacadeErrors,
...BriaEventErrors,
...SvixErrors,

...WalletErrors,
} as const
23 changes: 14 additions & 9 deletions core/api/src/app/wallets/decline-single-pending-invoice.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
ProcessPendingInvoiceResult,
ProcessPendingInvoiceResultType,
ProcessedReason,
} from "./process-pending-invoice-result"

import { InvalidInvoiceProcessingStateError } from "./errors"

import { InvoiceNotFoundError } from "@/domain/bitcoin/lightning"

import { InvalidNonHodlInvoiceError } from "@/domain/errors"
Expand All @@ -15,6 +14,10 @@ import { LndService } from "@/services/lnd"

import { elapsedSinceTimestamp } from "@/utils"

const assertUnreachable = (x: never): never => {
throw new Error(`This should never compile with ${x}`)
}

export const declineHeldInvoice = wrapAsyncToRunInSpan({
namespace: "app.invoices",
fnName: "declineHeldInvoice",
Expand Down Expand Up @@ -42,23 +45,25 @@ export const declineHeldInvoice = wrapAsyncToRunInSpan({

const walletInvoices = WalletInvoicesRepository()
let marked: WalletInvoiceWithOptionalLnInvoice | RepositoryError
switch (true) {
case result.markProcessedAsCanceledOrExpired():
switch (result.type) {
case ProcessPendingInvoiceResultType.MarkProcessedAsCanceledOrExpired:
marked = await walletInvoices.markAsProcessingCompleted(paymentHash)
if (marked instanceof Error) {
pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted")
return marked
}
return true

case result.reason() === ProcessedReason.InvoiceNotPaidYet:
return true
case ProcessPendingInvoiceResultType.Error:
return result.error

case !!result.error():
return result.error() as ApplicationError
case ProcessPendingInvoiceResultType.MarkProcessedAsPaid:
case ProcessPendingInvoiceResultType.MarkProcessedAsPaidWithError:
case ProcessPendingInvoiceResultType.ReasonInvoiceNotPaidYet:
return true

default:
return new InvalidInvoiceProcessingStateError(JSON.stringify(result._state()))
return assertUnreachable(result)
}
},
})
Expand Down
6 changes: 0 additions & 6 deletions core/api/src/app/wallets/errors.ts

This file was deleted.

18 changes: 9 additions & 9 deletions core/api/src/app/wallets/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,29 +135,29 @@ type LnurlPaymentSendArgs = {
type ProcessedReason =
(typeof import("./process-pending-invoice-result").ProcessedReason)[keyof typeof import("./process-pending-invoice-result").ProcessedReason]

type ProcessPendingInvoiceResultState =
type ProcessPendingInvoiceResultType =
(typeof import("./process-pending-invoice-result").ProcessPendingInvoiceResultType)[keyof typeof import("./process-pending-invoice-result").ProcessPendingInvoiceResultType]

type ProcessPendingInvoiceResult =
| {
type: "markProcessedAsPaid"
markProcessedAsPaid: true
}
| {
type: "markProcessedAsPaidWithError"
markProcessedAsPaid: true
error: ApplicationError
}
| {
type: "markProcessedAsCanceledOrExpired"
markProcessedAsCanceledOrExpired: true
reason: ProcessedReason
}
| {
type: "reasonInvoiceNotPaidYet"
reason: "InvoiceNotPaidYet"
}
| {
type: "error"
error: ApplicationError
}

type ProcessPendingInvoiceResult = {
_state: () => ProcessPendingInvoiceResultState
markProcessedAsCanceledOrExpired: () => boolean
markProcessedAsPaid: () => boolean
reason: () => ProcessedReason | false
error: () => false | ApplicationError
}
65 changes: 29 additions & 36 deletions core/api/src/app/wallets/process-pending-invoice-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,35 @@ export const ProcessedReason = {
InvoiceNotPaidYet: "InvoiceNotPaidYet",
} as const

const wrapper = (
state: ProcessPendingInvoiceResultState,
): ProcessPendingInvoiceResult => {
return {
_state: () => state,
markProcessedAsCanceledOrExpired: () =>
"markProcessedAsCanceledOrExpired" in state &&
state.markProcessedAsCanceledOrExpired,
markProcessedAsPaid: () =>
"markProcessedAsPaid" in state && state.markProcessedAsPaid,
error: () => ("error" in state && state.error ? state.error : false),
reason: () => ("reason" in state && state.reason ? state.reason : false),
}
}
export const ProcessPendingInvoiceResultType = {
MarkProcessedAsPaidWithError: "markProcessedAsPaidWithError",
MarkProcessedAsPaid: "markProcessedAsPaid",
MarkProcessedAsCanceledOrExpired: "markProcessedAsCanceledOrExpired",
ReasonInvoiceNotPaidYet: "reasonInvoiceNotPaidYet",
Error: "error",
} as const

export const ProcessPendingInvoiceResult = {
processAsPaid: (): ProcessPendingInvoiceResult =>
wrapper({
markProcessedAsPaid: true,
}),
processAsPaidWithError: (error: ApplicationError): ProcessPendingInvoiceResult =>
wrapper({
markProcessedAsPaid: true,
error,
}),
processAsCanceledOrExpired: (reason: ProcessedReason): ProcessPendingInvoiceResult =>
wrapper({
markProcessedAsCanceledOrExpired: true,
reason,
}),
notPaid: (): ProcessPendingInvoiceResult =>
wrapper({
reason: ProcessedReason.InvoiceNotPaidYet,
}),
err: (error: ApplicationError): ProcessPendingInvoiceResult =>
wrapper({
error,
}),
processAsPaid: (): ProcessPendingInvoiceResult => ({
type: ProcessPendingInvoiceResultType.MarkProcessedAsPaid,
markProcessedAsPaid: true,
}),
processAsPaidWithError: (error: ApplicationError): ProcessPendingInvoiceResult => ({
type: ProcessPendingInvoiceResultType.MarkProcessedAsPaidWithError,
markProcessedAsPaid: true,
error,
}),
processAsCanceledOrExpired: (reason: ProcessedReason): ProcessPendingInvoiceResult => ({
type: ProcessPendingInvoiceResultType.MarkProcessedAsCanceledOrExpired,
markProcessedAsCanceledOrExpired: true,
reason,
}),
notPaid: (): ProcessPendingInvoiceResult => ({
type: ProcessPendingInvoiceResultType.ReasonInvoiceNotPaidYet,
reason: ProcessedReason.InvoiceNotPaidYet,
}),
err: (error: ApplicationError): ProcessPendingInvoiceResult => ({
type: ProcessPendingInvoiceResultType.Error,
error,
}),
}
30 changes: 16 additions & 14 deletions core/api/src/app/wallets/update-single-pending-invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { processPendingInvoiceForDecline } from "./decline-single-pending-invoic

import {
ProcessPendingInvoiceResult,
ProcessPendingInvoiceResultType,
ProcessedReason,
} from "./process-pending-invoice-result"

import { InvalidInvoiceProcessingStateError } from "./errors"

import { removeDeviceTokens } from "@/app/users/remove-device-tokens"
import { getCurrentPriceAsDisplayPriceRatio, usdFromBtcMidPriceFn } from "@/app/prices"

Expand Down Expand Up @@ -40,6 +39,10 @@ import { NotificationsService } from "@/services/notifications"
import { toDisplayBaseAmount } from "@/domain/payments"
import { LockServiceError } from "@/domain/lock"

const assertUnreachable = (x: never): never => {
throw new Error(`This should never compile with ${x}`)
}

export const updatePendingInvoice = wrapAsyncToRunInSpan({
namespace: "app.invoices",
fnName: "updatePendingInvoice",
Expand Down Expand Up @@ -68,36 +71,35 @@ export const updatePendingInvoice = wrapAsyncToRunInSpan({

const walletInvoices = WalletInvoicesRepository()
let marked: WalletInvoiceWithOptionalLnInvoice | RepositoryError
switch (true) {
case walletInvoiceBeforeProcessing.paid:
return true

case result.markProcessedAsCanceledOrExpired():
if (walletInvoiceBeforeProcessing.paid) return true
switch (result.type) {
case ProcessPendingInvoiceResultType.MarkProcessedAsCanceledOrExpired:
marked = await walletInvoices.markAsProcessingCompleted(paymentHash)
if (marked instanceof Error) {
pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted")
return marked
}
return true

case result.markProcessedAsPaid():
case ProcessPendingInvoiceResultType.MarkProcessedAsPaid:
case ProcessPendingInvoiceResultType.MarkProcessedAsPaidWithError:
marked = await walletInvoices.markAsPaid(paymentHash)
if (
marked instanceof Error &&
!(marked instanceof CouldNotFindWalletInvoiceError)
) {
return marked
}
return result.error() || true
return "error" in result ? result.error : true

case result.reason() === ProcessedReason.InvoiceNotPaidYet:
return true
case ProcessPendingInvoiceResultType.Error:
return result.error

case !!result.error():
return result.error() as ApplicationError
case ProcessPendingInvoiceResultType.ReasonInvoiceNotPaidYet:
return true

default:
return new InvalidInvoiceProcessingStateError(JSON.stringify(result._state()))
return assertUnreachable(result)
}
},
})
Expand Down
2 changes: 0 additions & 2 deletions core/api/src/graphql/error-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,8 +707,6 @@ export const mapError = (error: ApplicationError): CustomGraphQLError => {
case "InvalidErrorCodeForPhoneMetadataError":
case "InvalidCountryCodeForPhoneMetadataError":
case "MultipleWalletsFoundForAccountIdAndCurrency":
case "InvoiceProcessingError":
case "InvalidInvoiceProcessingStateError":
message = `Unexpected error occurred, please try again or contact support if it persists (code: ${
error.name
}${error.message ? ": " + error.message : ""})`
Expand Down

0 comments on commit a08a638

Please sign in to comment.