Skip to content
Open
42 changes: 0 additions & 42 deletions frontends/api/src/hooks/articles/queries.ts

This file was deleted.

4 changes: 4 additions & 0 deletions frontends/api/src/hooks/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import type { User } from "../../generated/v0/api"
import { userQueries } from "./queries"

enum Permission {
/**
* Controls access to all website_content types (both "news" and "article"
* content_type). Despite the name, this is not limited to article content.
*/
ArticleEditor = "is_article_editor",
Authenticated = "is_authenticated",
LearningPathEditor = "is_learning_path_editor",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { renderHook, waitFor } from "@testing-library/react"

import { setupReactQueryTest } from "../test-utils"
import { articleKeys } from "./queries"
import { websiteContentKeys } from "./queries"
import { setMockResponse, urls, makeRequest } from "../../test-utils"
import { UseQueryResult } from "@tanstack/react-query"
import { articles as factory } from "../../test-utils/factories"
import { websiteContent as factory } from "../../test-utils/factories"
import {
useArticleList,
useArticleDetail,
useArticleCreate,
useArticlePartialUpdate,
useArticleDestroy,
useWebsiteContentList,
useWebsiteContentDetail,
useWebsiteContentCreate,
useWebsiteContentPartialUpdate,
useWebsiteContentDestroy,
} from "./index"

/**
Expand All @@ -28,86 +28,86 @@ const assertApiCalled = async (
expect(result.current.data).toEqual(data)
}

describe("useArticleList", () => {
describe("useWebsiteContentList", () => {
it.each([undefined, { limit: 5 }, { limit: 5, offset: 10 }])(
"Calls the correct API",
async (params) => {
const data = factory.articles({ count: 3 })
const data = factory.websiteContents({ count: 3 })
const url = urls.websiteContent.list(params)
const { wrapper } = setupReactQueryTest()
setMockResponse.get(url, data)
const useTestHook = () => useArticleList(params)
const useTestHook = () => useWebsiteContentList(params)
const { result } = renderHook(useTestHook, { wrapper })
assertApiCalled(result, url, "GET", data)
},
)
})

describe("useArticleDetail", () => {
describe("useWebsiteContentDetail", () => {
it("Calls the correct API", async () => {
const data = factory.article()
const data = factory.websiteContent()
const url = urls.websiteContent.details(data.id)

const { wrapper } = setupReactQueryTest()
setMockResponse.get(url, data)
const useTestHook = () => useArticleDetail(data.id)
const useTestHook = () => useWebsiteContentDetail(data.id)
const { result } = renderHook(useTestHook, { wrapper })

assertApiCalled(result, url, "GET", data)
})
})

describe("Article CRUD", () => {
test("useArticleCreate calls correct API", async () => {
describe("Website Content CRUD", () => {
test("useWebsiteContentCreate calls correct API", async () => {
const url = urls.websiteContent.list()
const data = factory.article()
const { id, ...requestData } = factory.article()
const data = factory.websiteContent()
const { id, ...requestData } = factory.websiteContent()
setMockResponse.post(url, data)

const { wrapper, queryClient } = setupReactQueryTest()
jest.spyOn(queryClient, "invalidateQueries")
const { result } = renderHook(useArticleCreate, { wrapper })
const { result } = renderHook(useWebsiteContentCreate, { wrapper })
result.current.mutate(requestData)

await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(makeRequest).toHaveBeenCalledWith("post", url, requestData)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
queryKey: articleKeys.listRoot(),
queryKey: websiteContentKeys.listRoot(),
})
})

test("useArticlePartialUpdate calls correct API", async () => {
const article = factory.article()
test("useWebsiteContentPartialUpdate calls correct API", async () => {
const article = factory.websiteContent()
const url = urls.websiteContent.details(article.id)
setMockResponse.patch(url, article)

const { wrapper, queryClient } = setupReactQueryTest()
jest.spyOn(queryClient, "invalidateQueries")
const { result } = renderHook(useArticlePartialUpdate, { wrapper })
const { result } = renderHook(useWebsiteContentPartialUpdate, { wrapper })
result.current.mutate(article)

await waitFor(() => expect(result.current.isSuccess).toBe(true))
const { id, ...patchData } = article
expect(makeRequest).toHaveBeenCalledWith("patch", url, patchData)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
queryKey: articleKeys.detail(article.id),
queryKey: websiteContentKeys.detail(article.id),
})
})

test("useArticleDestroy calls correct API", async () => {
const { id } = factory.article()
test("useWebsiteContentDestroy calls correct API", async () => {
const { id } = factory.websiteContent()
const url = urls.websiteContent.details(id)
setMockResponse.delete(url, null)

const { wrapper, queryClient } = setupReactQueryTest()
jest.spyOn(queryClient, "invalidateQueries")
const { result } = renderHook(useArticleDestroy, { wrapper })
const { result } = renderHook(useWebsiteContentDestroy, { wrapper })
result.current.mutate(id)

await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(makeRequest).toHaveBeenCalledWith("delete", url, undefined)
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
queryKey: articleKeys.listRoot(),
queryKey: websiteContentKeys.listRoot(),
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,52 @@ import type { AxiosProgressEvent } from "axios"

import { websiteContentApi, mediaApi } from "../../clients"
import type {
WebsiteContentApiWebsiteContentListRequest as ArticleListRequest,
WebsiteContent as Article,
WebsiteContentApiWebsiteContentListRequest as WebsiteContentListRequest,
WebsiteContent,
} from "../../generated/v1"
import { articleQueries, articleKeys } from "./queries"
import { websiteContentQueries, websiteContentKeys } from "./queries"

const useArticleList = (
params: ArticleListRequest = {},
const useWebsiteContentList = (
params: WebsiteContentListRequest = {},
opts?: { enabled?: boolean },
) => {
return useQuery({
...articleQueries.list(params),
...websiteContentQueries.list(params),
...opts,
})
}

/**
* Query is disabled if id is undefined.
*/
const useArticleDetail = (id: number | undefined) => {
const useWebsiteContentDetail = (id: number | undefined) => {
return useQuery({
...articleQueries.detail(id ?? -1),
...websiteContentQueries.detail(id ?? -1),
enabled: id !== undefined,
})
}

const useArticleDetailRetrieve = (identifier: string | undefined) => {
const useWebsiteContentDetailRetrieve = (identifier: string | undefined) => {
return useQuery({
...articleQueries.articlesDetailRetrieve(identifier ?? ""),
...websiteContentQueries.websiteContentDetailRetrieve(identifier ?? ""),
enabled: identifier !== undefined,
})
}

const useArticleCreate = () => {
const useWebsiteContentCreate = () => {
const client = useQueryClient()
return useMutation({
mutationFn: (
data: Omit<
Article,
WebsiteContent,
"id" | "user" | "created_on" | "updated_on" | "publish_date"
>,
) =>
websiteContentApi
.websiteContentCreate({ WebsiteContentRequest: data })
.then((response) => response.data),
onSuccess: () => {
client.invalidateQueries({ queryKey: articleKeys.listRoot() })
client.invalidateQueries({ queryKey: websiteContentKeys.listRoot() })
},
})
}
Expand Down Expand Up @@ -97,41 +97,46 @@ export const useMediaUpload = () => {
}
}

const useArticleDestroy = () => {
const useWebsiteContentDestroy = () => {
const client = useQueryClient()
return useMutation({
mutationFn: (id: number) => websiteContentApi.websiteContentDestroy({ id }),
onSuccess: () => {
client.invalidateQueries({ queryKey: articleKeys.listRoot() })
client.invalidateQueries({ queryKey: websiteContentKeys.listRoot() })
},
})
}
const useArticlePartialUpdate = () => {
const useWebsiteContentPartialUpdate = () => {
const client = useQueryClient()
return useMutation({
mutationFn: ({ id, ...data }: Partial<Article> & Pick<Article, "id">) =>
mutationFn: ({
id,
...data
}: Partial<WebsiteContent> & Pick<WebsiteContent, "id">) =>
websiteContentApi
.websiteContentPartialUpdate({
id,
PatchedWebsiteContentRequest: data,
})
.then((response) => response.data),
onSuccess: (article: Article) => {
client.invalidateQueries({ queryKey: articleKeys.detail(article.id) })
const identifier = article.slug || article.id.toString()
onSuccess: (websiteContent: WebsiteContent) => {
client.invalidateQueries({
queryKey: articleKeys.articlesDetailRetrieve(identifier),
queryKey: websiteContentKeys.detail(websiteContent.id),
})
const identifier = websiteContent.slug || websiteContent.id.toString()
client.invalidateQueries({
queryKey: websiteContentKeys.websiteContentDetailRetrieve(identifier),
})
},
})
}

export {
useArticleList,
useArticleDetail,
useArticleCreate,
useArticleDestroy,
useArticlePartialUpdate,
articleQueries,
useArticleDetailRetrieve,
useWebsiteContentList,
useWebsiteContentDetail,
useWebsiteContentCreate,
useWebsiteContentDestroy,
useWebsiteContentPartialUpdate,
websiteContentQueries,
useWebsiteContentDetailRetrieve,
}
45 changes: 45 additions & 0 deletions frontends/api/src/hooks/website_content/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { queryOptions } from "@tanstack/react-query"
import { websiteContentApi } from "../../clients"
import type { WebsiteContentApiWebsiteContentListRequest as WebsiteContentListRequest } from "../../generated/v1"

const websiteContentKeys = {
root: ["website_content"],
listRoot: () => [...websiteContentKeys.root, "list"],
list: (params: WebsiteContentListRequest) => [
...websiteContentKeys.listRoot(),
params,
],
detailRoot: () => [...websiteContentKeys.root, "detail"],
detail: (id: number) => [...websiteContentKeys.detailRoot(), id],
websiteContentDetailRetrieve: (identifier: string) => [
...websiteContentKeys.detailRoot(),
identifier,
],
}

const websiteContentQueries = {
list: (params: WebsiteContentListRequest) =>
queryOptions({
queryKey: websiteContentKeys.list(params),
queryFn: () =>
websiteContentApi.websiteContentList(params).then((res) => res.data),
}),
detail: (id: number) =>
queryOptions({
queryKey: websiteContentKeys.detail(id),
queryFn: () =>
websiteContentApi
.websiteContentRetrieve({ id })
.then((res) => res.data),
}),
websiteContentDetailRetrieve: (identifier: string) =>
queryOptions({
queryKey: websiteContentKeys.websiteContentDetailRetrieve(identifier),
queryFn: () =>
websiteContentApi
.websiteContentDetailRetrieve({ identifier })
.then((res) => res.data),
}),
}

export { websiteContentQueries, websiteContentKeys }
2 changes: 1 addition & 1 deletion frontends/api/src/test-utils/factories/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * as articles from "./articles"
export * as websiteContent from "./websiteContent"
export * as channels from "./channels"
export * as hubspot from "./hubspot"
export * as learningResources from "./learningResources"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { makePaginatedFactory } from "ol-test-utilities"
import type { Factory } from "ol-test-utilities"
import type { WebsiteContent } from "../../generated/v1"

const article: Factory<WebsiteContent> = (overrides = {}) => ({
const websiteContent: Factory<WebsiteContent> = (overrides = {}) => ({
id: faker.number.int(),
title: faker.lorem.sentence(),
content: {
Expand All @@ -25,6 +25,6 @@ const article: Factory<WebsiteContent> = (overrides = {}) => ({
...overrides,
})

const articles = makePaginatedFactory(article)
const websiteContents = makePaginatedFactory(websiteContent)

export { article, articles }
export { websiteContent, websiteContents }
Loading
Loading