Skip to content

Commit

Permalink
Move and export errorInterceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
razor-x committed Nov 8, 2023
1 parent 5fc585d commit c81bfe8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 82 deletions.
66 changes: 2 additions & 64 deletions src/lib/seam/connect/client.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import axios, {
type AxiosError,
type AxiosInstance,
type AxiosRequestConfig,
isAxiosError,
} from 'axios'
import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
// @ts-expect-error https://github.com/svsool/axios-better-stacktrace/issues/12
import axiosBetterStacktrace from 'axios-better-stacktrace'
import axiosRetry, { type AxiosRetry, exponentialDelay } from 'axios-retry'

import { paramsSerializer } from 'lib/params-serializer.js'

import type { ApiErrorResponse } from './api-error-type.js'
import {
SeamHttpApiError,
SeamHttpInvalidInputError,
SeamHttpUnauthorizedError,
} from './seam-http-error.js'
import { errorInterceptor } from './error-interceptor.js'

export type Client = AxiosInstance

Expand Down Expand Up @@ -48,55 +38,3 @@ export const createClient = (options: ClientOptions): AxiosInstance => {

return client
}

const errorInterceptor = async (err: unknown): Promise<void> => {
if (!isAxiosError(err)) {
throw err
}

const status = err.response?.status
const headers = err.response?.headers
const requestId = headers?.['seam-request-id'] ?? ''

if (status == null) throw err

if (status === 401) {
throw new SeamHttpUnauthorizedError(requestId)
}

if (!isApiErrorResponse(err)) throw err

const { response } = err
if (response == null) throw err

const { type } = response.data.error

const args = [response.data.error, status, requestId] as const

if (type === 'invalid_input') throw new SeamHttpInvalidInputError(...args)
throw new SeamHttpApiError(...args)
}

const isApiErrorResponse = (
err: AxiosError,
): err is AxiosError<ApiErrorResponse> => {
const headers = err.response?.headers
if (headers == null) return false

const contentType = headers['content-type']
if (
typeof contentType === 'string' &&
!contentType.startsWith('application/json')
) {
return false
}

const data = err.response?.data as any
if (typeof data === 'object') {
return (
'error' in data && typeof data.error === 'object' && 'type' in data.error
)
}

return false
}
62 changes: 62 additions & 0 deletions src/lib/seam/connect/error-interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { type AxiosError, isAxiosError } from 'axios'

import type { ApiErrorResponse } from './api-error-type.js'
import {
SeamHttpApiError,
SeamHttpInvalidInputError,
SeamHttpUnauthorizedError,
} from './seam-http-error.js'

export const errorInterceptor = async (err: unknown): Promise<void> => {
if (!isAxiosError(err)) throw err

const status = err.response?.status
const headers = err.response?.headers
const requestId = headers?.['seam-request-id'] ?? ''

if (status == null) throw err

if (status === 401) {
throw new SeamHttpUnauthorizedError(requestId)
}

if (!isApiErrorResponse(err)) throw err

const { response } = err
if (response == null) throw err

const { type } = response.data.error

const args = [response.data.error, status, requestId] as const

if (type === 'invalid_input') throw new SeamHttpInvalidInputError(...args)
throw new SeamHttpApiError(...args)
}

const isApiErrorResponse = (
err: AxiosError,
): err is AxiosError<ApiErrorResponse> => {
const headers = err.response?.headers
if (headers == null) return false

const contentType = headers['content-type']
if (
typeof contentType === 'string' &&
!contentType.startsWith('application/json')
) {
return false
}

const data = err.response?.data
if (typeof data === 'object' && data != null) {
return (
'error' in data &&
typeof data.error === 'object' &&
data.error != null &&
'type' in data.error &&
typeof data.error === 'string'
)
}

return false
}
1 change: 1 addition & 0 deletions src/lib/seam/connect/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './error-interceptor.js'
export * from './options.js'
export * from './routes/index.js'
export * from './seam-http.js'
Expand Down
36 changes: 18 additions & 18 deletions src/lib/seam/connect/seam-http-error.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,5 @@
import type { ApiError } from './api-error-type.js'

export const isSeamHttpApiError = (
error: unknown,
): error is SeamHttpApiError => {
return error instanceof SeamHttpApiError
}

export const isSeamHttpInvalidInputError = (
error: unknown,
): error is SeamHttpInvalidInputError => {
return error instanceof SeamHttpInvalidInputError
}

export const isSeamHttpUnauthorizedError = (
error: unknown,
): error is SeamHttpUnauthorizedError => {
return error instanceof SeamHttpUnauthorizedError
}

export class SeamHttpApiError extends Error {
code: string
statusCode: number
Expand All @@ -36,6 +18,12 @@ export class SeamHttpApiError extends Error {
}
}

export const isSeamHttpApiError = (
error: unknown,
): error is SeamHttpApiError => {
return error instanceof SeamHttpApiError
}

export class SeamHttpUnauthorizedError extends SeamHttpApiError {
override code: 'unauthorized'
override statusCode: 401
Expand All @@ -50,6 +38,12 @@ export class SeamHttpUnauthorizedError extends SeamHttpApiError {
}
}

export const isSeamHttpUnauthorizedError = (
error: unknown,
): error is SeamHttpUnauthorizedError => {
return error instanceof SeamHttpUnauthorizedError
}

export class SeamHttpInvalidInputError extends SeamHttpApiError {
override code: 'invalid_input'

Expand All @@ -60,3 +54,9 @@ export class SeamHttpInvalidInputError extends SeamHttpApiError {
this.code = 'invalid_input'
}
}

export const isSeamHttpInvalidInputError = (
error: unknown,
): error is SeamHttpInvalidInputError => {
return error instanceof SeamHttpInvalidInputError
}

0 comments on commit c81bfe8

Please sign in to comment.