Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/action/quickbooks.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export async function checkForNonUsCompany(tokenInfo: IntuitAPITokensType) {
message: 'checkForNonUsCompany | Company Info',
})

return companyInfo?.Country !== 'US'
return companyInfo.Country !== 'US'
}

export async function reconnectIfCta(type?: string) {
Expand Down
5 changes: 4 additions & 1 deletion src/app/api/core/utils/withErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { NextRequest, NextResponse } from 'next/server'
import { ZodError, ZodFormattedError } from 'zod'
import { isAxiosError } from '@/app/api/core/exceptions/custom'
import * as Sentry from '@sentry/nextjs'
import { IntuitAPIErrorMessage } from '@/utils/intuitAPI'
import { RetryableError } from '@/utils/error'

type RequestHandler = (req: NextRequest, params: any) => Promise<NextResponse>

Expand Down Expand Up @@ -64,6 +64,9 @@ export const withErrorHandler = (handler: RequestHandler): RequestHandler => {
status = error.status
message = error.message || message
errors = error.errors
} else if (error instanceof RetryableError) {
status = error.status
message = error.message || message
} else if (error instanceof Error && error.message) {
message = error.message
} else if (isAxiosError(error)) {
Expand Down
5 changes: 5 additions & 0 deletions src/app/api/core/utils/withRetry.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { StatusableError } from '@/type/CopilotApiError'
import pRetry, { FailedAttemptError } from 'p-retry'
import * as Sentry from '@sentry/nextjs'
import { RetryableError } from '@/utils/error'

export const withRetry = async <T>(
fn: (...args: any[]) => Promise<T>,
Expand Down Expand Up @@ -46,6 +47,10 @@ export const withRetry = async <T>(
)
},
shouldRetry: (error: any) => {
if (error instanceof RetryableError) {
return error.retry
}

// Typecasting because Copilot doesn't export an error class
const err = error as StatusableError
// Retry only if statusCode === 429
Expand Down
9 changes: 9 additions & 0 deletions src/type/dto/intuitAPI.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,12 @@ export const QBDeletePayloadSchema = z.object({
})

export type QBDeletePayloadType = z.infer<typeof QBDeletePayloadSchema>

export const CompanyInfoSchema = z.object({
CompanyInfo: z.array(
z.object({
Country: z.string(),
}),
),
})
export type CompanyInfoType = z.infer<typeof CompanyInfoSchema>
11 changes: 11 additions & 0 deletions src/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,14 @@ export const getMessageAndCodeFromError = (
}
return { message, code }
}

export class RetryableError extends Error {
readonly retry: boolean
readonly status: number

constructor(status: number, message: string, retry: boolean) {
super(message)
this.retry = retry
this.status = status
}
}
17 changes: 13 additions & 4 deletions src/utils/intuitAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import {
QBAccountUpdatePayloadType,
QBAccountResponseType,
QBAccountResponseSchema,
CompanyInfoType,
CompanyInfoSchema,
} from '@/type/dto/intuitAPI.dto'
import { RetryableError } from '@/utils/error'
import CustomLogger from '@/utils/logger'
import httpStatus from 'http-status'

Expand Down Expand Up @@ -768,16 +771,21 @@ export default class IntuitAPI {
return purchase
}

async _getCompanyInfo() {
async _getCompanyInfo(): Promise<CompanyInfoType['CompanyInfo'][0]> {
CustomLogger.info({
message: `IntuitAPI#getCompanyInfo | Company Info query start for realmId: ${this.tokens.intuitRealmId}.`,
})
const query = `SELECT * FROM CompanyInfo maxresults 1`
const companyInfo = await this.customQuery(query)

if (!companyInfo) return null
if (!companyInfo)
throw new RetryableError(
httpStatus.NOT_FOUND,
'No company info found',
true,
)

if (companyInfo?.Fault) {
if (companyInfo.Fault) {
CustomLogger.error({ obj: companyInfo.Fault?.Error, message: 'Error: ' })
throw new APIError(
companyInfo.Fault?.Error?.code || httpStatus.BAD_REQUEST,
Expand All @@ -786,7 +794,8 @@ export default class IntuitAPI {
)
}

return companyInfo.CompanyInfo?.[0]
const parsedCompanyInfo = CompanyInfoSchema.parse(companyInfo)
return parsedCompanyInfo.CompanyInfo[0]
}

private wrapWithRetry<Args extends unknown[], R>(
Expand Down
Loading