Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@
"react-navigation-shared-element": "^3.1.3",
"rn-slick-bottom-tabs": "^1.1.1",
"styled-components": "^5.3.6",
"yarn": "^1.22.19"
"yarn": "^1.22.19",
"zod": "^3.21.4"
},
"devDependencies": {
"@babel/core": "^7.20.0",
Expand Down
24 changes: 18 additions & 6 deletions src/api/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import axios, {
AxiosResponse,
InternalAxiosRequestConfig,
} from "axios"
import { PolimiToken, PoliNetworkToken, Tokens } from "contexts/login"
import { PolimiToken, PoliNetworkToken, Tokens, tokensSchema } from "./schemas"
import AsyncStorage from "@react-native-async-storage/async-storage"
import { wait } from "utils/functions"
import { Alert } from "react-native"
import { z } from "zod"

/*Docs used to make this:
Singleton:
Expand Down Expand Up @@ -76,9 +77,14 @@ declare module "axios" {
waitingTime?: number //seconds
readonly retryCount?: number
authType?: AuthType
zodSchema?: z.ZodSchema
}
}

type InferrableAxiosRequestConfig<T> = AxiosRequestConfig & {
zodSchema?: z.ZodSchema<T>
}

/**
* Singleton object which manages requests and retries
* to PoliNetwork Server and Polimi Server.
Expand Down Expand Up @@ -188,6 +194,9 @@ export class HttpClient extends EventEmitter {
* does (or will do) something before `.then` is called
* */
private _handleResponse = (res: AxiosResponse): AxiosResponse => {
if (res.config.zodSchema) {
res.config.zodSchema.parse(res.data)
}
return res
}

Expand Down Expand Up @@ -289,7 +298,9 @@ export class HttpClient extends EventEmitter {
throw error
}

callPolimi<T = void>(options: AxiosRequestConfig): CancellableApiRequest<T> {
callPolimi<T = void>(
options: InferrableAxiosRequestConfig<T>
): CancellableApiRequest<T> {
const controller = new AbortController()
const request = this.polimiInstance.request<T>({
...options,
Expand All @@ -302,7 +313,7 @@ export class HttpClient extends EventEmitter {
}

callPoliNetwork<T = void>(
options: AxiosRequestConfig
options: InferrableAxiosRequestConfig<T>
): CancellableApiRequest<T> {
const controller = new AbortController()
const request = this.poliNetworkInstance.request<T>({
Expand All @@ -315,7 +326,9 @@ export class HttpClient extends EventEmitter {
return request
}

callGeneral<T = void>(options: AxiosRequestConfig): CancellableApiRequest<T> {
callGeneral<T = void>(
options: InferrableAxiosRequestConfig<T>
): CancellableApiRequest<T> {
const controller = new AbortController()
const request = this.generalInstance.request<T>({
...options,
Expand Down Expand Up @@ -419,8 +432,7 @@ export class HttpClient extends EventEmitter {
async loadTokens() {
const tokens = await AsyncStorage.getItem("api:tokens")
if (tokens) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsedTokens: Tokens = JSON.parse(tokens)
const parsedTokens: Tokens = tokensSchema.parse(JSON.parse(tokens))
console.log("Loaded tokens from local storage")
this.polimiToken = parsedTokens.polimiToken
this.poliNetworkToken = parsedTokens.poliNetworkToken
Expand Down
58 changes: 7 additions & 51 deletions src/api/collections/articles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,7 @@
import { mapAxiosRequest } from "api/mapAxiosRequest"
import { ApiCollection } from "api/useApiCall"
import { HttpClient, RequestOptions } from "../HttpClient"

export interface Tags {
tags: Tag[]
}

export interface Tag {
name: string
image: string
blurhash: string
}

export interface Articles {
articles: Article[]
start: string | null
end: string | null
tag: string | null
author_id: number | null
title: string | null
}
interface ArticleAuthor {
name?: string
link?: string
image?: string
}

export interface Article {
id: number
tag_id: string
latitude?: number
longitude?: number
publish_time: string
target_time?: string
hidden_until?: string
content: {
it?: ArticleParams
en?: ArticleParams
}
image?: string
blurhash?: string
author?: ArticleAuthor
}

interface ArticleParams {
content: string
title: string
subtitle: string
url: string
}
import { articlesSchema, tagsSchema } from "api/schemas"

const client = HttpClient.getInstance()

Expand Down Expand Up @@ -86,7 +39,7 @@ export const articles = {
params: { tag: string; limit: number; offset: number },
options?: RequestOptions
) {
const request = client.callPoliNetwork<Articles>({
const request = client.callPoliNetwork({
url: "/v1/articles",
method: "GET",
params: {
Expand All @@ -96,6 +49,7 @@ export const articles = {
tag: params.tag,
sort: "date",
},
zodSchema: articlesSchema,
...options,
})
return mapAxiosRequest(request, res => res.articles)
Expand All @@ -109,10 +63,11 @@ export const articles = {
* @param options see {@link RequestOptions}
*/
getLastArticleByTag(params: { tag: string }, options?: RequestOptions) {
const request = client.callPoliNetwork<Articles>({
const request = client.callPoliNetwork({
url: "/v1/articles",
method: "GET",
params: { tag: params.tag, limit: 1, sort: "date", platform: 1 },
zodSchema: articlesSchema,
...options,
})
return mapAxiosRequest(request, res => res.articles[0])
Expand All @@ -122,9 +77,10 @@ export const articles = {
* Retrieves Tags (news categories) from PoliNetwork server.
*/
getTags(_params?: Record<string, unknown>, options?: RequestOptions) {
const request = client.callPoliNetwork<Tags>({
const request = client.callPoliNetwork({
url: "/v1/tags",
method: "GET",
zodSchema: tagsSchema,
...options,
})

Expand Down
7 changes: 4 additions & 3 deletions src/api/collections/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mapAxiosRequest } from "api/mapAxiosRequest"
import { ApiCollection } from "api/useApiCall"
import { PolimiToken } from "contexts/login"
import { HttpClient, RequestOptions } from "../HttpClient"
import { polimiTokenSchema } from "api/schemas"

const client = HttpClient.getInstance()

Expand All @@ -15,13 +15,14 @@ export const auth = {
* @returns polimi accessToken and refreshToken
*/
getPolimiToken(params: { authcode: string }, options?: RequestOptions) {
const request = client.callPolimi<PolimiToken>({
const request = client.callPolimi({
url: `/rest/jaf/oauth/token/get/${params.authcode}`,
method: "GET",
...options,
headers: {
accept: "application/json",
},
zodSchema: polimiTokenSchema,
...options,
})
return mapAxiosRequest(request, res => res)
},
Expand Down
104 changes: 6 additions & 98 deletions src/api/collections/event.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,7 @@
import { mapAxiosRequest } from "api/mapAxiosRequest"
import { ApiCollection } from "api/useApiCall"
import { AuthType, HttpClient, RequestOptions } from "../HttpClient"
import { EventType } from "utils/events"

/* eslint-disable @typescript-eslint/naming-convention */
export interface Event {
event_id: number
date_start: string
date_end: string
show_agenda: boolean
matricola?: string
title: {
it: string
en: string
}
event_type: {
typeId: EventType
type_dn: {
it: string
en: string
}
}
event_subtype?: string
calendar: {
calendar_id: number
calendar_dn: {
it: string
en: string
}
}
room?: {
room_id: number
acronym_dn: string
classroom_id: number
room_dn: string
}
//used by Timetable to store color
lectureColor?: string
}

export interface RemoteLink {
url: string
link_description: {
it: string
en: string
}
}

export interface Lecture {
event_id: number
room_id: number
date_start: string
date_end: string
class_code: number
teaching_description: {
it: string
en: string
}
lecturer: string
lecture_type: string
event_type: {
typeId: number
type_dn: {
it: string
en: string
}
}
calendar: {
calendar_id: number
calendar_dn: {
it: string
en: string
}
}
room: {
room_id: number
campus: string
address: string
floor: string
building: string
type: string
acronym_dn: string
coordinates: {
latitude: number
longitude: number
}
csi_id: string
csi_parent_id: string
classroom_id: number
room_dn: string
}
remote_links: RemoteLink[]
}

export interface LectureDetails {
lecture: Lecture
personal_event: Event
}
import { eventSchema, lectureDetailsSchema } from "api/schemas"

const client = HttpClient.getInstance()

Expand All @@ -106,11 +11,13 @@ export const events = {
options?: RequestOptions
) {
const url = "/agenda/api/me/" + params.matricola + "/events"
const request = client.callPolimi<Event[]>({
const request = client.callPolimi({
url,
method: "GET",
authType: AuthType.POLIMI,
// eslint-disable-next-line @typescript-eslint/naming-convention
params: { start_date: params.startDate, n_events: params.nEvents },
zodSchema: eventSchema.array(),
...options,
})
return mapAxiosRequest(request, response => response)
Expand All @@ -121,10 +28,11 @@ export const events = {
options?: RequestOptions
) {
const url = `/agenda/api/me/${params.matricola}/lectures/${params.eventId}`
const request = client.callPolimi<LectureDetails>({
const request = client.callPolimi({
url,
method: "GET",
authType: AuthType.POLIMI,
zodSchema: lectureDetailsSchema,
...options,
})
return mapAxiosRequest(request, response => response.lecture)
Expand Down
Loading