Database Connection Update #4

Merged
humenius merged 49 commits from feature/database-connection into master 2021-02-08 03:16:51 +01:00
11 changed files with 77 additions and 162 deletions
Showing only changes of commit d7208b773c - Show all commits

View File

@@ -32,8 +32,9 @@ export class AppController {
return await this.sinusBotService.fetchStats();
}
@Get('/stats/season/:id')
@Get('/stats/season/:id?')
async getStats(@Param('id') id?: string): Promise<UserStatsResponse> {
logger.info(`Got id ${id}`)
return await this.databaseService
.fetchStats(id)
.catch(err => {

View File

@@ -7,6 +7,7 @@ import {
} from '../models/aliases';
import { PrismaService } from '../prisma/prisma.service';
import { seasons } from '@prisma/client';
import logger from 'src/logger/Logger';
@Injectable()
export class DatabaseService {
@@ -25,7 +26,7 @@ export class DatabaseService {
})
.then(value => this.prismaClient.seasons.findUnique({
where: {
season_id: Number((!seasonId ? value : seasonId))
season_id: Number(seasonId ?? value)
}
}) as Promise<seasons>)
.then((result: SeasonInfo) => {

View File

@@ -14,8 +14,8 @@
"husky": "^4.3.7",
"lint-staged": "^10.5.3",
"prettier": "^2.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react": "17.0.0",
"react-dom": "17.0.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",

View File

@@ -11,30 +11,10 @@ library.add(faArrowCircleLeft, faArrowCircleRight)
const App: FunctionComponent = () => (
<Router>
<Switch>
{/*<Route exact path={'/'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />*/}
<Route path={'/season/:id'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
<Route path={'/season/:id?'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
<Redirect to={'/season'} />
</Switch>
</Router>
);
export default App;
// export default class App extends React.Component {
// componentDidMount() {
// this.setState({loading: false, mock: this.mockService.getStatsWithoutPromise()});
// this.apiService.getStats()
// .then(data => this.setState({
// isLoaded: true,
// users: data
// }))
// .catch(error => {
// error.response.json()
// .then((err: any) => this.setState({
// isLoaded: true,
// error: err.error
// }))
// });
// }
export default App;

View File

@@ -7,17 +7,21 @@ const SeasonDetail: React.FC<IUserListProperties> = (props: IUserListProperties)
return (
<span className="SeasonDetail" data-testid="SeasonDetail">
Season {seasonId} - Duration: {dates.start.toDateString()} - {dates.end.toDateString()}
Season {seasonId} - Duration: {dateToString(dates.start)} - {dateToString(dates.end)}
</span>
)
}
const dateToString = (date?: Date): string => {
return date ? date.toLocaleDateString('en-GB') : 'TBD';
}
export interface SeasonDetailProperties {
seasonId: string,
seasonId?: string,
maxSeasonId: string
dates: {
start: Date,
end: Date
start?: Date,
end?: Date
}
}

View File

@@ -10,14 +10,18 @@ const SeasonSwitch: React.FC<IUserListProperties> = (props: IUserListProperties)
const maxSeasonId = Number(props.userStats.maxSeasonId)
return (
<div className={"SeasonSwitch " + (!props.enabled && " no-click")} data-testid="SeasonSwitch">
<div className={"SeasonSwitch"} data-testid="SeasonSwitch">
{
seasonId > 1
&& (props.enabled
? <Link to={"/season/" + (seasonId - 1)} onClick={() => props.onSeasonIdChange('' + (seasonId - 1))}>
&& <Link to={"/season/" + (seasonId - 1)} onClick={() => {
console.log("nothing happens")
props.onSeasonIdChange('' + (seasonId - 1))}}>
<FontAwesomeIcon icon="arrow-circle-left"/>
</Link>
: <Link to={""} onClick={(e) => e.preventDefault()}>
|| <Link to={""} onClick={(e) => {
console.log("nothing happens2")
e.preventDefault()}}>
<FontAwesomeIcon icon="arrow-circle-left"/>
</Link>)
}

View File

@@ -24,7 +24,7 @@ const createTableEntries = (entries: TableEntry[]) =>
const UserList: React.FC<IUserListProperties> = (props: IUserListProperties) => (
<div className="UserList" data-testid="UserList">
<SeasonSwitch {...props} />
<table>
<table className={!props.enabled ? "blurred" : ""}>
<thead>
<tr>
<th>Placement</th>
@@ -43,7 +43,7 @@ const UserList: React.FC<IUserListProperties> = (props: IUserListProperties) =>
export interface IUserListProperties {
userStats: UserStatsResponse
mocked?: boolean
onSeasonIdChange: any
onSeasonIdChange: React.Dispatch<React.SetStateAction<string | undefined>>
enabled: boolean
}

View File

@@ -1,6 +1,7 @@
import UserStatsResponse from "../models/UserStatsResponse";
import UserStatsService from "../services/UserStatsService";
export default class UserStatsMockService {
export default class UserStatsMockService extends UserStatsService {
private static readonly mocks: UserStatsResponse[] = [
{
seasonId: "1",
@@ -33,14 +34,30 @@ export default class UserStatsMockService {
{ name: "Humen", rank: "12", onlineTime: "0d 1h 0m 0s" },
{ name: "Humen", rank: "ok", onlineTime: "0d 1h 0m 0s" }
]
}
},
{
seasonId: 'undefined',
maxSeasonId: "2",
dates: {
start: undefined,
end: undefined
},
stats: [
{ name: "Humen", rank: "Overwatch Noob", onlineTime: "0d 1h 0m 0s" },
{ name: "Humen", rank: "Random Rank 3", onlineTime: "0d 1h 0m 0s" },
{ name: "Humen", rank: "Kas is cool", onlineTime: "0d 1h 0m 0s" },
{ name: "Humen", rank: "Bremsspur", onlineTime: "0d 1h 0m 0s" },
{ name: "Humen", rank: "12", onlineTime: "0d 1h 0m 0s" },
{ name: "Humen", rank: "ok", onlineTime: "0d 1h 0m 0s" }
]
}
]
static async getStats(seasonId: string): Promise<UserStatsResponse> {
return Promise.resolve(this.mocks[Number(seasonId) - 1])
public static async getStats(seasonId?: string): Promise<UserStatsResponse> {
return Promise.resolve(this.mocks[Number((seasonId ? seasonId : 2)) - 1])
}
static getStatsWithoutPromise(seasonId: string): UserStatsResponse {
return this.mocks[Number(seasonId) - 1]
static getStatsWithoutPromise(seasonId?: string): UserStatsResponse {
return this.mocks[Number((seasonId ? seasonId : 2)) - 1]
}
}

View File

@@ -13,13 +13,15 @@ import {ClipLoader} from "react-spinners";
import UserStatsService from "../../services/UserStatsService";
const MainPage: FC<IMainPageProps> = (props: IMainPageProps) => {
const [seasonId, setSeasonId] = useState(props.match.params.id)
const [error, setError] = useState<RequestError | undefined>(undefined)
const [loading, setLoadingState] = useState(true)
const [seasonId, setSeasonId] = useState(props.match.params.id)
const [error, setError] = useState<RequestError | undefined>(undefined)
const [loading, setLoadingState] = useState(true)
const [seasonStats, setSeasonStats] = useState<UserStatsResponse>(UserStatsMockService.getStatsWithoutPromise(seasonId))
const [spinnerColor] = useState('#61dafb')
useEffect(() => {
setLoadingState(true)
setSeasonStats(UserStatsMockService.getStatsWithoutPromise('3'))
UserStatsService.getStats(seasonId)
.then(res => {
setSeasonStats(res)
@@ -30,7 +32,7 @@ const MainPage: FC<IMainPageProps> = (props: IMainPageProps) => {
setLoadingState(false)
setError(new RequestError(0, "Could not retrieve stats. Try again later."))
})
}, [seasonId, setLoadingState])
}, [seasonId])
const spinnerCss = `
margin: 0;
@@ -40,22 +42,20 @@ const MainPage: FC<IMainPageProps> = (props: IMainPageProps) => {
return (
<div className="MainPage">
<Header />
<Header />
{
error && <ErrorContainer message={error.message} />
}
<ClipLoader color={spinnerColor} loading={loading} size={150} css={spinnerCss} />
<div className={loading || error ? "blurred" : ""}>
<UserList enabled={loading || !(!error)} onSeasonIdChange={setSeasonId} userStats={seasonStats} />
</div>
<UserList enabled={!loading && (error == null)} onSeasonIdChange={setSeasonId} userStats={seasonStats} />
<Footer />
</div>
)
}
export interface IMainPageProps extends RouteComponentProps<{ id: string }> {
export interface IMainPageProps extends RouteComponentProps<{ id?: string }> {
}

View File

@@ -4,8 +4,7 @@ import UserStatsResponse from "../models/UserStatsResponse";
export default class UserStatsService {
private static apiURL = 'https://api.tsotr.humenius.me/stats'
// private static apiURL = 'http://localhost:3500/stats'
//private static apiURL = 'https://mock.codes/501'
//private static apiURL = 'http://localhost:3500/stats'
private static requestInit: RequestInit = {
method: 'GET',
@@ -14,13 +13,13 @@ export default class UserStatsService {
},
};
public static async getStats(seasonId: string): Promise<UserStatsResponse> {
return fetch(`${this.apiURL}/season/${seasonId}`, this.requestInit)
public static async getStats(seasonId?: string): Promise<UserStatsResponse> {
return fetch(`${this.apiURL}/season/${seasonId ?? ''}`, this.requestInit)
.then(res => UserStatsService.checkResponse(res))
.then(data => data.json())
.then(data => {
data.dates.start = new Date(data.dates.start)
data.dates.end = new Date(data.dates.end)
data.dates.start = data.dates.start ? new Date(data.dates.start) : undefined
data.dates.end = data.dates.end ? new Date(data.dates.end) : undefined
return data
})
}
@@ -32,70 +31,4 @@ export default class UserStatsService {
}
return response;
}
}
// const checkResponse = <T extends Response>(response: T): T => {
// if (!response.ok) {
// console.log(response);
// let error = new RequestError(response.statusText);
// error.response = response;
// throw error;
// }
// return response;
// }
//
// const useApiService = <T>(url: string, method: string = 'GET'): { response: T | null; error: RequestError | null } => {
// const baseUrl = "https://api.tsotr.humenius.me"
// const [response, setResponse] = useState<T | null>(null)
// const [error, setError] = useState<RequestError | null>(null)
// const requestInit = {
// method: method,
// headers: {
// 'Content-Type': 'application/json'
// }
// }
//
// useEffect(() => {
// const fetchData = async (): Promise<void> => {
// const result = await fetch(`${baseUrl}${url}`, requestInit)
// .then(res => checkResponse(res))
// .then(data => data.json())
// .catch(err => setError(err))
// setResponse(result)
// }
//
// fetchData()
// }, [url])
//
// return { response, error }
// }
//
// // const seasonStatsQuery = (seasonId: number): { response: UserStatsResponse | null; error: RequestError | null } => {
// // const baseUrl = "https://api.tsotr.humenius.me/stats/season/"
// // const [response, setResponse] = useState<UserStatsResponse | null>(null)
// // const [error, setError] = useState<RequestError | null>(null)
// // const requestInit = {
// // method: 'GET',
// // headers: {
// // 'Content-Type': 'application/json'
// // }
// // }
// //
// // useEffect(() => {
// // const fetchData = async (): Promise<void> => {
// // const result = await fetch(baseUrl, requestInit)
// // .then(res => checkResponse(res))
// // .then(data => data.json())
// // .catch(err => setError(err))
// // setResponse(result)
// // }
// //
// // fetchData()
// // }, [seasonId])
// //
// // return { response, error }
// // }
//
// const currentSeasonStatsQuery = useApiService<UserStatsResponse>('/stats')
//
// export default useApiService;
}

View File

@@ -1667,11 +1667,6 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
"@types/isomorphic-fetch@^0.0.35":
version "0.0.35"
resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.35.tgz#c1c0d402daac324582b6186b91f8905340ea3361"
integrity sha512-DaZNUvLDCAnCTjgwxgiL1eQdxIKEpNLOlTNtAgnZc50bG2copGhRrFN9/PxPBuJe+tZVLCbQ7ls0xveXVRPkvw==
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@@ -4806,11 +4801,6 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
fetch-retry-ts@^1.1.23:
version "1.1.23"
resolved "https://registry.yarnpkg.com/fetch-retry-ts/-/fetch-retry-ts-1.1.23.tgz#7659974215c7a66b9bb81c006e5acee34d932ca8"
integrity sha512-UwqrMGfDPRa60etXl1HkDgIhY9k2AoB/Qa41/U2thzHtA/NVHGSmn037dw4iOg62ccyX2imtkU8SFfSwxLLEYA==
figgy-pudding@^3.5.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
@@ -6179,14 +6169,6 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-fetch@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4"
integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==
dependencies:
node-fetch "^2.6.1"
whatwg-fetch "^3.4.1"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -7522,11 +7504,6 @@ no-case@^3.0.4:
lower-case "^2.0.2"
tslib "^2.0.3"
node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@@ -9241,15 +9218,14 @@ react-dev-utils@^10.2.1:
strip-ansi "6.0.0"
text-table "0.2.0"
react-dom@^16.13.1:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
react-dom@17.0.0:
version "17.0.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.0.tgz#f8266e4d9861584553ccbd186d596a1c7dd8dcb4"
integrity sha512-OGnFbxCjI2TMAZYMVxi4hqheJiN8rCEVVrL7XIGzCB6beNc4Am8M47HtkvxODZw9QgjmAPKpLba9FTu4fC1byA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.19.1"
scheduler "^0.20.0"
react-error-overlay@^6.0.7:
version "6.0.8"
@@ -9362,14 +9338,13 @@ react-spinners@^0.10.4:
dependencies:
"@emotion/core" "^10.0.35"
react@^16.13.1:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
react@17.0.0:
version "17.0.0"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.0.tgz#ad96d5fa1a33bb9b06d0cc52672f7992d84aa662"
integrity sha512-rG9bqS3LMuetoSUKHN8G3fMNuQOePKDThK6+2yXFWtoeTDLVNh/QCaxT+Jr+rNf4lwNXpx+atdn3Aa0oi8/6eQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
read-pkg-up@^2.0.0:
version "2.0.0"
@@ -9864,10 +9839,10 @@ saxes@^3.1.9:
dependencies:
xmlchars "^2.1.1"
scheduler@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
scheduler@^0.20.0:
version "0.20.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c"
integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@@ -11335,7 +11310,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
whatwg-fetch@^3.0.0, whatwg-fetch@^3.4.1:
whatwg-fetch@^3.0.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868"
integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==