Skip to content
This repository has been archived by the owner on Sep 17, 2024. It is now read-only.

Commit

Permalink
add homepageApi
Browse files Browse the repository at this point in the history
  • Loading branch information
ByAnthony committed May 15, 2024
1 parent 6867afe commit 87f87e7
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 59 deletions.
6 changes: 3 additions & 3 deletions client/src/components/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useGetAllHistoryArticleLinkQuery } from '../../redux/slices/historySlice';
import { useGetHomepageDataQuery } from '../../redux/slices/homepageSlice';

import { Footer } from '../Footer/Footer';
import { HistoryChapters } from './HistoryChapters/HistoryChapter';
Expand All @@ -9,7 +9,7 @@ import STYLES from './HomePage.module.scss';
export function HomePage() {
const {
data, error, isLoading, isSuccess,
} = useGetAllHistoryArticleLinkQuery();
} = useGetHomepageDataQuery();

if (data) {
return (
Expand All @@ -32,7 +32,7 @@ export function HomePage() {
during World War I
</h1>
</div>
<HistoryChapters articles={data} />
<HistoryChapters articles={data.historyChapters} />
</div>
)}
<Footer />
Expand Down
48 changes: 1 addition & 47 deletions client/src/redux/slices/historySlice.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Provider } from 'react-redux';
import fetchMock from 'jest-fetch-mock';
import { mockArticle } from '../../utils/mocks/mockArticle';
import { store } from '../store';
import { useGetAllHistoryArticleLinkQuery, useGetHistoryArticleByIdQuery } from './historySlice';
import { useGetHistoryArticleByIdQuery } from './historySlice';

function Wrapper({ children }: { children: ReactNode }) {
return (
Expand All @@ -14,52 +14,6 @@ function Wrapper({ children }: { children: ReactNode }) {
);
}

describe('useGetAllHistoryArticleLinkQuery', () => {
const endpointName = 'getAllHistoryArticleLink';
const data = [
{ url: 'test-article-1', title: 'test-article-1', chapter: 1 },
{ url: 'test-article-2', title: 'test-article-2', chapter: 2 },
{ url: 'test-article-3', title: 'test-article-3', chapter: 3 },
];

beforeEach(() => {
fetchMock.resetMocks();
fetchMock.mockOnceIf('http://localhost:5000/', () => Promise.resolve({
status: 200,
body: JSON.stringify({ data }),
}));
});

it('renders hook', async () => {
const { result } = renderHook(() => useGetAllHistoryArticleLinkQuery(), {
wrapper: Wrapper,
});

expect(result.current).toMatchObject({
status: 'pending',
endpointName,
isLoading: true,
isSuccess: false,
isError: false,
isFetching: true,
});

await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(fetchMock).toBeCalledTimes(1);

expect(result.current).toMatchObject({
status: 'fulfilled',
endpointName,
data: { data },
isLoading: false,
isSuccess: true,
isError: false,
currentData: {},
isFetching: false,
});
});
});

describe('useGetHistoryArticleByIdQuery', () => {
const endpointName = 'getHistoryArticleById';
const article = 'test-article';
Expand Down
8 changes: 1 addition & 7 deletions client/src/redux/slices/historySlice.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { Article, Articles } from '../../types/article';
import { Article } from '../../types/article';

export const historyApi = createApi({
reducerPath: 'historyApi',
baseQuery: fetchBaseQuery({
baseUrl: 'http://localhost:5000/',
}),
endpoints: (builder) => ({
getAllHistoryArticleLink: builder.query<Array<Articles>, void>({
query: () => ({
url: '/',
}),
}),
getHistoryArticleById: builder.query<Article, string>({
query: (id: string) => `history/${id}`,
}),
}),
});

export const {
useGetAllHistoryArticleLinkQuery,
useGetHistoryArticleByIdQuery,
} = historyApi;
67 changes: 67 additions & 0 deletions client/src/redux/slices/homepageSlice.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ReactNode } from 'react';
import { renderHook, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import fetchMock from 'jest-fetch-mock';
import { store } from '../store';
import { useGetHomepageDataQuery } from './homepageSlice';

function Wrapper({ children }: { children: ReactNode }) {
return (
<Provider store={store}>
{children}
</Provider>
);
}

describe('useGetAllHistoryArticleLinkQuery', () => {
const endpointName = 'getHomepageData';
const data = {
tunnellers: [
{ id: 1, image: 'image-1.jpg' },
{ id: 2, image: 'image-2.jpg' },
{ id: 3, image: 'image-3.jpg' },
],
historyChapters: [
{ url: 'test-article-1', title: 'test-article-1', chapter: 1 },
{ url: 'test-article-2', title: 'test-article-2', chapter: 2 },
{ url: 'test-article-3', title: 'test-article-3', chapter: 3 },
],
};

beforeEach(() => {
fetchMock.resetMocks();
fetchMock.mockOnceIf('http://localhost:5000/', () => Promise.resolve({
status: 200,
body: JSON.stringify({ data }),
}));
});

it('renders hook', async () => {
const { result } = renderHook(() => useGetHomepageDataQuery(), {
wrapper: Wrapper,
});

expect(result.current).toMatchObject({
status: 'pending',
endpointName,
isLoading: true,
isSuccess: false,
isError: false,
isFetching: true,
});

await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(fetchMock).toBeCalledTimes(1);

expect(result.current).toMatchObject({
status: 'fulfilled',
endpointName,
data: { data },
isLoading: false,
isSuccess: true,
isError: false,
currentData: {},
isFetching: false,
});
});
});
20 changes: 20 additions & 0 deletions client/src/redux/slices/homepageSlice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { Articles } from '../../types/article';

export const homepageApi = createApi({
reducerPath: 'homepageApi',
baseQuery: fetchBaseQuery({
baseUrl: 'http://localhost:5000/',
}),
endpoints: (builder) => ({
getHomepageData: builder.query<Record<string, Array<Articles>>, void>({
query: () => ({
url: '/',
}),
}),
}),
});

export const {
useGetHomepageDataQuery,
} = homepageApi;
9 changes: 8 additions & 1 deletion client/src/redux/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import { setupListeners } from '@reduxjs/toolkit/dist/query';
import { tunnellersApi } from './slices/rollSlice';
import { historyApi } from './slices/historySlice';
import { aboutUsApi } from './slices/aboutUsSlice';
import { homepageApi } from './slices/homepageSlice';

export const store = configureStore({
reducer: {
[homepageApi.reducerPath]: homepageApi.reducer,
[tunnellersApi.reducerPath]: tunnellersApi.reducer,
[historyApi.reducerPath]: historyApi.reducer,
[aboutUsApi.reducerPath]: aboutUsApi.reducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware()
.concat(tunnellersApi.middleware, historyApi.middleware, aboutUsApi.middleware),
.concat(
homepageApi.middleware,
tunnellersApi.middleware,
historyApi.middleware,
aboutUsApi.middleware,
),
});

setupListeners(store.dispatch);
Expand Down
20 changes: 20 additions & 0 deletions server/models/homepage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from dataclasses import dataclass


from .article import ArticleReference


@dataclass
class TunnellerImage:
id: int
image: str

def __getitem__(self, key: str):
return getattr(self, key)


@dataclass
class Homepage:
tunnellers: list[TunnellerImage]
history_chapters: list[ArticleReference]
63 changes: 63 additions & 0 deletions server/repositories/homepage_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# flask_mysqldb does not have stub files
from flask_mysqldb import MySQL # type: ignore


from ..models.homepage import Homepage, TunnellerImage

from ..db.models.ArticleData import (
ArticleReferenceData,
FileData,
)

from ..models.helpers.article_helpers import (
get_images,
get_next_chapter,
)


from ..models.article import ArticleReference
from ..repositories.queries.article_query import (
next_article_query,
images_query,
)
from ..repositories.queries.tunnellers_images_query import tunnellers_images_query

from ..db.run_sql import run_sql


def get_homepage(mysql: MySQL) -> Homepage:
tunnellers: list[TunnellerImage] = []

images_sql: str = tunnellers_images_query()
images_results: tuple[TunnellerImage, ...] = run_sql(images_sql, mysql, None)

for index, row in enumerate(images_results):
image = TunnellerImage(
row["id"],
row["image"],
)
tunnellers.append(image)

articles: list[ArticleReference] = []

articles_sql: str = next_article_query()
articles_results: tuple[ArticleReferenceData, ...] = run_sql(
articles_sql, mysql, None
)
images_sql = images_query()
image_result: tuple[FileData, ...] = run_sql(images_sql, mysql, None)
next_sql = next_article_query()
next_result: tuple[ArticleReferenceData, ...] = run_sql(next_sql, mysql, None)

for index, row in enumerate(articles_results):
article = ArticleReference(
row["id"],
row["chapter"],
row["title"],
get_images(image_result, index),
get_next_chapter(row["chapter"], next_result),
)
articles.append(article)

return Homepage(tunnellers, articles)
7 changes: 7 additions & 0 deletions server/repositories/queries/tunnellers_images_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
def tunnellers_images_query() -> str:
return """SELECT
t.id
, t.image
FROM tunneller t WHERE t.image IS NOT NULL"""
3 changes: 2 additions & 1 deletion server/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .models.helpers.camelize_helpers import underscore_to_camel
from .repositories import about_us_repository
from .repositories import article_repository
from .repositories import homepage_repository
from .repositories import roll_repository
from .repositories import tunneller_repository
from .models.jsonencoder import JSONEncoder
Expand Down Expand Up @@ -63,7 +64,7 @@ def tunneller(id: int):

@app.route("/", methods=["GET"])
def homepage():
get_homepage = article_repository.select_all(mysql)
get_homepage = homepage_repository.get_homepage(mysql)
homepage = json.dumps(get_homepage, cls=JSONEncoder, indent=4)
camelized_data_for_ts: str = underscore_to_camel(homepage)
return camelized_data_for_ts
Expand Down

0 comments on commit 87f87e7

Please sign in to comment.