Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Refactor media services #867

Merged
merged 12 commits into from
Feb 23, 2022
8 changes: 2 additions & 6 deletions src/composables/use-related.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import AudioService from '~/data/audio-service'
import ImageService from '~/data/image-service'
import { ref, useFetch } from '@nuxtjs/composition-api'
import { AUDIO, IMAGE } from '~/constants/media'

const services = { [AUDIO]: AudioService, [IMAGE]: ImageService }
import { mediaServices } from '~/store/media'

export default function useRelated({
mediaType,
mediaId,
service = services[mediaType],
service = mediaServices[mediaType],
}) {
const media = ref([])
// fetch and fetchState are available as this.$fetch and this.$fetchState
Expand Down
2 changes: 1 addition & 1 deletion src/constants/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const statuses = {
ADDITIONAL: 'additional',
}

/** @type {Object.<MediaType, SupportStatus>} */
/** @type {Record.<MediaType, SupportStatus>} */
obulat marked this conversation as resolved.
Show resolved Hide resolved
export const contentStatus = {
[ALL_MEDIA]: statuses.SUPPORTED,
[IMAGE]: statuses.SUPPORTED,
Expand Down
26 changes: 20 additions & 6 deletions src/data/api-service.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import axios from 'axios'
import { warn } from '~/utils/warn'
import { AUDIO, IMAGE } from '~/constants/media'

const DEFAULT_REQUEST_TIMEOUT = 30000

/**
* Returns a slug with trailing slash for a given resource name.
* For media types, converts the name into resource slug when necessary (i.e. pluralizes 'image'),
* for other resources uses the resource name as the slug.
* @param {import('../constants/media').MediaType|string} resource
* @returns {*}
obulat marked this conversation as resolved.
Show resolved Hide resolved
*/
const getResourceSlug = (resource) => {
const slug = { [AUDIO]: 'audio', [IMAGE]: 'images' }[resource] ?? resource
return `${slug}/`
}
Comment on lines +14 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice fix here.

/**
* @param {boolean} errorCondition
* @param {string} message
Expand Down Expand Up @@ -62,7 +74,7 @@ export const createApiService = (baseUrl = process.env.apiUrl) => {
* @returns {Promise<import('axios').AxiosResponse<T>>} response The API response object
*/
query(resource, params) {
return client.get(resource, { params })
return client.get(`${getResourceSlug(resource)}`, { params })
},

/**
Expand All @@ -72,7 +84,7 @@ export const createApiService = (baseUrl = process.env.apiUrl) => {
* @returns {Promise<import('axios').AxiosResponse<T>>} Response The API response object
*/
get(resource, slug) {
return client.get(`${resource}/${slug}/`)
return client.get(`${getResourceSlug(resource)}${slug}/`)
},

/**
Expand All @@ -82,7 +94,7 @@ export const createApiService = (baseUrl = process.env.apiUrl) => {
* @returns {Promise<import('axios').AxiosResponse<T>>} Response The API response object
*/
post(resource, data) {
return client.post(resource, data)
return client.post(getResourceSlug(resource), data)
},

/**
Expand All @@ -94,7 +106,9 @@ export const createApiService = (baseUrl = process.env.apiUrl) => {
* @returns {Promise<import('axios').AxiosResponse<T>>} Response The API response object
*/
update(resource, slug, data, headers) {
return client.put(`${resource}/${slug}`, data, { headers })
return client.put(`${getResourceSlug(resource)}${slug}`, data, {
headers,
})
},

/**
Expand All @@ -103,7 +117,7 @@ export const createApiService = (baseUrl = process.env.apiUrl) => {
* @returns {Promise<import('axios').AxiosResponse<any>>} Response The API response object
*/
put(resource, params) {
return client.put(resource, params)
return client.put(getResourceSlug(resource), params)
},

/**
Expand All @@ -113,7 +127,7 @@ export const createApiService = (baseUrl = process.env.apiUrl) => {
* @returns {Promise<import('axios').AxiosResponse<any>>} Response The API response object
*/
delete(resource, slug, headers) {
return client.delete(`${resource}/${slug}`, { headers })
return client.delete(`${getResourceSlug(resource)}${slug}`, { headers })
},
}
}
Expand Down
52 changes: 0 additions & 52 deletions src/data/audio-service.js

This file was deleted.

13 changes: 0 additions & 13 deletions src/data/base-media-service.js

This file was deleted.

47 changes: 0 additions & 47 deletions src/data/image-service.js

This file was deleted.

75 changes: 75 additions & 0 deletions src/data/media-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import ApiService from '~/data/api-service'

import decodeMediaData from '~/utils/decode-media-data'

/**
* @template {import('../store/types').FrontendMediaType} [T=any]
*/
class MediaService {
/**
* @param {T} mediaType
*/
constructor(mediaType) {
/** @type {T} */
this.mediaType = mediaType
}

/**
* Decodes the text data to avoid encoding problems.
* Also, converts the results from an array of media objects into an object with
* media id as keys.
* @param {import('../store/types').MediaResult<T[]>} data
* @returns {import('../store/types').MediaStoreResult<T>}
*/
transformResults(data) {
return {
...data,
results: data.results.reduce((acc, item) => {
acc[item.id] = decodeMediaData(item, this.mediaType)
return acc
}, /** @type {Record<string, import('../store/types').DetailFromMediaType<T>>} */ ({})),
}
}

/**
* Search for media items by keyword.
* @param {import('../store/types').ApiQueryParams} params
* @return {Promise<import('axios').AxiosResponse<import('../store/types').MediaResult<T[]>>>}
*/
search(params) {
return ApiService.query(this.mediaType, params)
}

/**
* Retrieve media details by its id.
* SSR-called
* @param {{ id: string }} params
* @return {Promise<import('axios').AxiosResponse<import('../store/types').MediaResult<T>>>}
*/
getMediaDetail(params) {
if (!params.id) {
throw new Error(
`MediaService.getMediaDetail() id parameter required to retrieve ${this.mediaType} details.`
)
}

return ApiService.get(this.mediaType, params.id)
}

/**
* Retrieve related media
* @param {{ id: string }} params
* @return {Promise<import('axios').AxiosResponse<import('../store/types').MediaResult<T[]>>>}
*/
getRelatedMedia(params) {
if (!params.id) {
throw new Error(
`MediaService.getRelatedMedia() id parameter required to retrieve related media.`
)
}

return ApiService.get(this.mediaType, `${params.id}/related`)
}
}

export default MediaService
14 changes: 8 additions & 6 deletions src/store/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ import {
} from '~/constants/usage-data-analytics-types'
import { AUDIO, IMAGE, VIDEO, ALL_MEDIA } from '~/constants/media'
import { USAGE_DATA } from '~/constants/store-modules'
import AudioService from '~/data/audio-service'
import ImageService from '~/data/image-service'
import MediaService from '~/data/media-service'

// Note: images should always be first here,
// and this only includes 'real' media. ALL is a
Expand Down Expand Up @@ -69,7 +68,12 @@ export const state = () => ({
image: {},
})

export const createActions = (services) => ({
export const mediaServices = {
[AUDIO]: new MediaService(AUDIO),
[IMAGE]: new MediaService(IMAGE),
}

export const createActions = (services = mediaServices) => ({
/**
*
* @param {import('vuex').ActionContext} context
Expand Down Expand Up @@ -258,6 +262,7 @@ export const createActions = (services) => ({
}
},
})
const actions = createActions()

export const getters = {
/**
Expand Down Expand Up @@ -420,9 +425,6 @@ export const mutations = {
},
}

const mediaServices = { [AUDIO]: AudioService, [IMAGE]: ImageService }
const actions = createActions(mediaServices)

export default {
state,
getters,
Expand Down
Loading