Skip to content

Commit

Permalink
Replace axios with ofetch
Browse files Browse the repository at this point in the history
Signed-off-by: Olga Bulat <[email protected]>
  • Loading branch information
obulat committed Feb 5, 2024
1 parent d3bcfd4 commit 2cd94a6
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 245 deletions.
19 changes: 11 additions & 8 deletions frontend/src/components/VContentReport/VContentReportForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@
<script setup lang="ts">
import { useNuxtApp, useRuntimeConfig } from "#imports"
import { computed, ref } from "vue"
import { ofetch } from "ofetch"
import axios from "axios"
import { computed, ref } from "vue"
import {
reasons,
Expand All @@ -138,7 +138,7 @@ import {
FAILED,
WIP,
DMCA_FORM_URL,
ReportReason,
type ReportReason,
} from "~/constants/content-report"
import type { AudioDetail, ImageDetail } from "~/types/media"
Expand Down Expand Up @@ -208,13 +208,16 @@ const handleSubmit = async (event: Event) => {
} = useRuntimeConfig()
// Not proxied through the Nuxt `/api/` because it is a POST request
await axios.post(
await ofetch(
`${apiUrl}v1/${mediaSlug(mediaType)}/${props.media.id}/report/`,
{
mediaType,
reason,
identifier: props.media.id,
description: description.value,
method: "POST",
body: {
mediaType,
reason,
identifier: props.media.id,
description: description.value,
},
}
)
Expand Down
31 changes: 12 additions & 19 deletions frontend/src/constants/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,19 @@ export const customErrorCodes = [
ERR_UNKNOWN,
] as const

/**
* The error codes Axios uses.
* @see https://github.com/axios/axios/blob/9588fcdec8aca45c3ba2f7968988a5d03f23168c/lib/core/AxiosError.js#L57C2-L71
*/
const axiosErrorCodes = [
"ERR_BAD_OPTION_VALUE",
"ERR_BAD_OPTION",
"ECONNABORTED",
"ETIMEDOUT",
"ERR_NETWORK",
"ERR_FR_TOO_MANY_REDIRECTS",
"ERR_DEPRECATED",
"ERR_BAD_RESPONSE",
"ERR_BAD_REQUEST",
"ERR_CANCELED",
"ERR_NOT_SUPPORT",
"ERR_INVALID_URL",
] as const
export const ofetchErrorCodes = [
"Too many results",
"Request Timeout",
"Conflict",
"Too Early",
"Too Many Requests",
"Internal Server Error",
"Bad Gateway",
"Service Unavailable",
"Gateway Timeout",
]

export const errorCodes = [...customErrorCodes, ...axiosErrorCodes] as const
export const errorCodes = [...customErrorCodes, ...ofetchErrorCodes] as const

export const clientSideErrorCodes: readonly ErrorCode[] = [
ECONNABORTED,
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/image/[id]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ import {
import { computed, onMounted, ref, watch } from "vue"
import axios from "axios"
import { ofetch } from "ofetch"
import { IMAGE } from "~/constants/media"
import { skipToContentTargetId } from "~/constants/window"
Expand Down Expand Up @@ -181,8 +181,8 @@ const sketchFabUid = computed(() => {
})
const fetchFiletype = async (url: string) => {
const response = await axios.head(url)
const contentType = response.headers["content-type"]
const response = await ofetch.raw(url, { method: "HEAD" })
const contentType = response.headers.get("content-type")
if (contentType?.includes("image")) {
return contentType.split("/")[1]
}
Expand Down
109 changes: 50 additions & 59 deletions frontend/src/stores/media/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { decodeMediaData, useRequestEvent } from "#imports"
import { createError, decodeMediaData } from "#imports"

import { ofetch } from "ofetch"
import { defineStore } from "pinia"

import axios from "axios"

import { warn } from "~/utils/console"
import { hash, rand as prng } from "~/utils/prng"
import { isRetriable, parseFetchingError } from "~/utils/errors"
Expand All @@ -20,9 +19,9 @@ import {
AUDIO,
IMAGE,
isAdditionalSearchType,
SupportedMediaType,
supportedMediaTypes,
SupportedSearchType,
type SupportedMediaType,
type SupportedSearchType,
} from "~/constants/media"
import { NO_RESULT } from "~/constants/errors"
import { isSearchTypeSupported, useSearchStore } from "~/stores/search"
Expand All @@ -31,6 +30,10 @@ import { deepFreeze } from "~/utils/deep-freeze"
import { mediaSlug, DEFAULT_REQUEST_TIMEOUT } from "~/utils/query-utils"

import type { PaginatedApiMediaResult } from "~/types/api"
import type {
PaginatedCollectionQuery,
PaginatedSearchQuery,
} from "~/types/search"

export type MediaStoreResult = {
count: number
Expand Down Expand Up @@ -459,34 +462,18 @@ export const useMediaStore = defineStore("media", {
}) {
this._updateFetchState(mediaType, "start")

let page = this.results[mediaType].page + 1
const page = this.results[mediaType].page + 1
const { pathSlug, queryParams } = this.getSearchUrlParts(
mediaType,
page,
shouldPersistMedia
)
const { url, baseURL } = this.getFetchUrl(mediaType, pathSlug)
const url = `/api/${mediaSlug(mediaType)}/${pathSlug}`

try {
const res = await axios.get<PaginatedApiMediaResult>(url, {
baseURL,
params: queryParams,
timeout: DEFAULT_REQUEST_TIMEOUT,
})

const data = this.decodeData(res.data, mediaType)

const data = await this.fetchAndDecode(mediaType, url, queryParams)
this._updateFetchState(mediaType, "end")
const mediaCount = data.result_count
if (!mediaCount) {
page = 1
}
const errorData = createNoResultErrorData(
queryParams.q ?? "",
mediaType,
mediaCount
)
this._updateFetchState(mediaType, "end", errorData)

this.setMedia({
mediaType,
media: data.results,
Expand All @@ -497,17 +484,48 @@ export const useMediaStore = defineStore("media", {
})
return mediaCount
} catch (error: unknown) {
const errorData = parseFetchingError(error, mediaType, "search", {
searchTerm: queryParams.q ?? "",
})
this._updateFetchState(mediaType, "end", errorData)
this.handleFetchingError(error, mediaType, queryParams.q)
}
},

console.warn(error, { extra: errorData })
throw createError(errorData)
async fetchAndDecode(
mediaType: SupportedMediaType,
url: string,
queryParams: PaginatedSearchQuery | PaginatedCollectionQuery
) {
const res = await ofetch.raw<PaginatedApiMediaResult>(url, {
params: queryParams,
retry: 0,
timeout: DEFAULT_REQUEST_TIMEOUT,
})
const data = this.decodeData(res._data, mediaType)
if (!data.result_count) {
throw new Error(NO_RESULT)
}
return data
},

decodeData(data: PaginatedApiMediaResult, mediaType: SupportedMediaType) {
handleFetchingError(
error: unknown,
mediaType: SupportedMediaType,
searchTerm: string
) {
const errorData = parseFetchingError(error, mediaType, "search", {
searchTerm,
})
this._updateFetchState(mediaType, "end", errorData)

console.warn(error, { extra: errorData })
throw createError(errorData)
},

decodeData(
data: PaginatedApiMediaResult | undefined,
mediaType: SupportedMediaType
) {
if (!data) {
throw new Error("No data to decode")
}
return {
...data,
results: (data.results ?? []).reduce((acc, item) => {
Expand All @@ -534,16 +552,6 @@ export const useMediaStore = defineStore("media", {
return { pathSlug, queryParams }
},

getFetchUrl(mediaType: SupportedMediaType, pathSlug: string) {
const url = `/api/${mediaSlug(mediaType)}/${pathSlug}`

// TODO: Check if baseURL works in prod.
const nitroOrigin = useRequestEvent()?.context.siteConfigNitroOrigin
let baseURL = nitroOrigin ? nitroOrigin : location?.origin
baseURL = baseURL.replace("localhost", "0.0.0.0")
return { url, baseURL }
},

setMediaProperties(
type: SupportedMediaType,
id: string,
Expand Down Expand Up @@ -577,20 +585,3 @@ const findNoResultError = (errors: FetchingError[]): FetchingError | null => {
? { ...errors[0], searchType: ALL_MEDIA }
: null
}

export const createNoResultErrorData = (
searchTerm: string,
mediaType: SupportedMediaType,
mediaCount: number
) => {
if (mediaCount) {
return undefined
}
return {
message: `No results found for ${searchTerm}`,
code: NO_RESULT,
requestKind: "search",
searchType: mediaType,
details: { searchTerm },
} as const
}
23 changes: 5 additions & 18 deletions frontend/src/stores/media/related-media.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { decodeMediaData, useRequestEvent } from "#imports"
import { decodeMediaData } from "#imports"

import { defineStore } from "pinia"

import axios from "axios"

import { parseFetchingError } from "~/utils/errors"
import { mediaSlug, DEFAULT_REQUEST_TIMEOUT } from "~/utils/query-utils"

import { FetchingError, FetchState } from "~/types/fetch-state"
import type { FetchingError, FetchState } from "~/types/fetch-state"
import type { AudioDetail, ImageDetail, Media } from "~/types/media"
import type { SupportedMediaType } from "~/constants/media"
import type { PaginatedApiMediaResult } from "~/types/api"
Expand Down Expand Up @@ -49,11 +47,7 @@ export const useRelatedMediaStore = defineStore("related-media", {
this.fetchState.fetchingError = null
},

async fetchMedia(
mediaType: SupportedMediaType,
id: string,
baseUrl?: string
) {
async fetchMedia(mediaType: SupportedMediaType, id: string) {
if (this.mainMediaId === id && this.media.length > 0) {
return this.media
}
Expand All @@ -64,19 +58,12 @@ export const useRelatedMediaStore = defineStore("related-media", {
const url = `/api/${mediaSlug(mediaType)}/${id}/related/`
const params = mediaType === "audio" ? { peaks: true } : {}

let baseURL = baseUrl ? baseUrl : null
if (!baseURL) {
const nitroOrigin = useRequestEvent()?.context?.siteConfigNitroOrigin
baseURL = nitroOrigin ? nitroOrigin : location?.origin
baseURL = baseURL.replace("localhost", "0.0.0.0")
}
try {
const res = await axios.get<PaginatedApiMediaResult>(url, {
const res = await $fetch.raw<PaginatedApiMediaResult>(url, {
timeout: DEFAULT_REQUEST_TIMEOUT,
params,
baseURL,
})
this.media = (res.data.results ?? []).map(
this.media = (res._data?.results ?? []).map(
(item: AudioDetail | ImageDetail) => decodeMediaData(item, mediaType)
)
this._endFetching()
Expand Down
26 changes: 5 additions & 21 deletions frontend/src/stores/media/single-result.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import {
createError,
decodeMediaData,
isServer,
useRequestEvent,
} from "#imports"
import { createError, decodeMediaData, isServer } from "#imports"

import { defineStore } from "pinia"

import axios from "axios"

import type {
ApiMedia,
AudioDetail,
Expand Down Expand Up @@ -167,13 +160,8 @@ export const useSingleResultStore = defineStore("single-result", {
? existingItem
: ((await this.fetchMediaItem<T>(type, id)) as DetailFromMediaType<T>)
if (item && isServer) {
const baseURL =
useRequestEvent()?.context.siteConfigNitroOrigin.replace(
"localhost",
"0.0.0.0"
)
const relatedMediaStore = useRelatedMediaStore()
await relatedMediaStore.fetchMedia(type, id, baseURL)
await relatedMediaStore.fetchMedia(type, id)
}
return item
},
Expand All @@ -188,19 +176,15 @@ export const useSingleResultStore = defineStore("single-result", {
) {
this._updateFetchState("start")

const nitroOrigin = useRequestEvent()?.context.siteConfigNitroOrigin

let baseURL = nitroOrigin ? nitroOrigin : location?.origin
baseURL = baseURL.replace("localhost", "0.0.0.0")
try {
const rawItem = await axios.get<ApiMedia>(
const rawItem = await $fetch.raw<ApiMedia>(
`/api/${mediaSlug(type)}/${id}/`,
{
timeout: DEFAULT_REQUEST_TIMEOUT,
baseURL,
}
)
const transformedItem = decodeMediaData(rawItem.data, type)

const transformedItem = decodeMediaData(rawItem._data, type)
const item = this._addProviderName(transformedItem)

this.setMediaItem(item)
Expand Down
Loading

0 comments on commit 2cd94a6

Please sign in to comment.