mirror of
https://github.com/dd060606/WebAurion-API.git
synced 2026-01-18 16:47:26 +01:00
feat: add viewstate cache to optimize requests
This commit is contained in:
@@ -12,29 +12,13 @@ class NotesApi {
|
|||||||
public fetchNotes(): Promise<NotesList[]> {
|
public fetchNotes(): Promise<NotesList[]> {
|
||||||
return new Promise<NotesList[]>(async (resolve, reject) => {
|
return new Promise<NotesList[]>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
// On part depuis la page planning pour pouvoir finalement accéder à la page des notes
|
// On initialise le ViewState (obligatoire pour avoir une réponse correcteur du backend)
|
||||||
const schedulePage = await this.session.sendGET<string>(
|
// Ici 1_1 correspond au sous-menu 'Mes notes' dans la sidebar
|
||||||
"/faces/Planning.xhtml",
|
await this.session.getViewState("1_1");
|
||||||
|
const response = await this.session.sendGET<string>(
|
||||||
|
"faces/LearnerNotationListPage.xhtml",
|
||||||
);
|
);
|
||||||
let viewState = getViewState(schedulePage);
|
resolve(getNotesFromResponse(response));
|
||||||
if (viewState) {
|
|
||||||
// Ici 291906 correspond au menu 'Scolarité' dans la sidebar
|
|
||||||
// Requête utile pour intialiser le ViewState (obligatoire pour effectuer une requête)
|
|
||||||
await this.session.sendSidebarRequest("291906", viewState);
|
|
||||||
|
|
||||||
// Ici 1_1 correspond au sous-menu 'Mes notes' dans la sidebar
|
|
||||||
// On récupère le ViewState pour effectuer la prochaine requête
|
|
||||||
viewState = await this.session.sendSidebarSubmenuRequest(
|
|
||||||
"1_1",
|
|
||||||
viewState,
|
|
||||||
);
|
|
||||||
const response = await this.session.sendGET<string>(
|
|
||||||
"faces/LearnerNotationListPage.xhtml",
|
|
||||||
);
|
|
||||||
resolve(getNotesFromResponse(response));
|
|
||||||
} else {
|
|
||||||
reject(new Error("Viewstate not found"));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,68 +12,49 @@ class PlanningApi {
|
|||||||
public fetchPlanning(startDate?: string): Promise<PlanningEvent[]> {
|
public fetchPlanning(startDate?: string): Promise<PlanningEvent[]> {
|
||||||
return new Promise<PlanningEvent[]>(async (resolve, reject) => {
|
return new Promise<PlanningEvent[]>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const schedulePage = await this.session.sendGET<string>(
|
// On récupère le ViewState pour effectuer la requête
|
||||||
"/faces/Planning.xhtml",
|
// Ici 1_4 correspond au sous-menu 'Emploi du temps' dans la sidebar
|
||||||
|
let viewState = await this.session.getViewState("1_4");
|
||||||
|
// On envoie enfin la requête pour obtenir l'emploi du temps
|
||||||
|
const params = getJSFFormParams(
|
||||||
|
"j_idt118",
|
||||||
|
"j_idt118",
|
||||||
|
viewState,
|
||||||
);
|
);
|
||||||
let viewState = getViewState(schedulePage);
|
if (startDate) {
|
||||||
if (viewState) {
|
params.append("form:j_idt118_start", startDate);
|
||||||
// Ici 291906 correspond au menu 'Scolarité' dans la sidebar
|
// La date de fin est fixée à une semaine après la date de début
|
||||||
// Requête utile pour intialiser le ViewState (obligatoire pour effectuer une requête)
|
const endDate = startDate + 6 * 24 * 60 * 60 * 1000;
|
||||||
await this.session.sendSidebarRequest("291906", viewState);
|
params.append("form:j_idt118_end", endDate);
|
||||||
|
|
||||||
// Ici 1_4 correspond au sous-menu 'Emploi du temps' dans la sidebar
|
|
||||||
// On récupère le ViewState pour effectuer la prochaine requête
|
|
||||||
viewState = await this.session.sendSidebarSubmenuRequest(
|
|
||||||
"1_4",
|
|
||||||
viewState,
|
|
||||||
);
|
|
||||||
|
|
||||||
// On envoie enfin la requête pour obtenir l'emploi du temps
|
|
||||||
const params = getJSFFormParams(
|
|
||||||
"j_idt118",
|
|
||||||
"j_idt118",
|
|
||||||
viewState,
|
|
||||||
);
|
|
||||||
if (startDate) {
|
|
||||||
params.append("form:j_idt118_start", startDate);
|
|
||||||
// La date de fin est fixée à une semaine après la date de début
|
|
||||||
const endDate = startDate + 6 * 24 * 60 * 60 * 1000;
|
|
||||||
params.append("form:j_idt118_end", endDate);
|
|
||||||
} else {
|
|
||||||
const now = new Date();
|
|
||||||
// Obtenir le jour actuel (0 = dimanche, 1 = lundi, ..., 6 = samedi)
|
|
||||||
let day = now.getDay();
|
|
||||||
// Calculer la différence pour atteindre lundi (0 = dimanche => 1 jour pour atteindre lundi)
|
|
||||||
const daysToMonday = day === 0 ? 1 : 1 - day;
|
|
||||||
// Créer la date de début (lundi 6h00)
|
|
||||||
const startDate = new Date(now);
|
|
||||||
startDate.setDate(now.getDate() + daysToMonday); // Passer au lundi
|
|
||||||
startDate.setHours(6, 0, 0, 0); // Fixer à 6h00
|
|
||||||
// Date de fin (6 jours après la date de début)
|
|
||||||
const endDate = new Date(startDate);
|
|
||||||
endDate.setDate(startDate.getDate() + 6); // Ajouter 6 jours
|
|
||||||
// Convertir les dates en timestamp
|
|
||||||
const startTimestamp = startDate.getTime();
|
|
||||||
const endTimestamp = endDate.getTime();
|
|
||||||
|
|
||||||
params.append(
|
|
||||||
"form:j_idt118_start",
|
|
||||||
startTimestamp.toString(),
|
|
||||||
);
|
|
||||||
params.append(
|
|
||||||
"form:j_idt118_end",
|
|
||||||
endTimestamp.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await this.session.sendPOST<string>(
|
|
||||||
"faces/Planning.xhtml",
|
|
||||||
params,
|
|
||||||
);
|
|
||||||
resolve(planningResponseToEvents(response));
|
|
||||||
} else {
|
} else {
|
||||||
reject(new Error("Viewstate not found"));
|
const now = new Date();
|
||||||
|
// Obtenir le jour actuel (0 = dimanche, 1 = lundi, ..., 6 = samedi)
|
||||||
|
let day = now.getDay();
|
||||||
|
// Calculer la différence pour atteindre lundi (0 = dimanche => 1 jour pour atteindre lundi)
|
||||||
|
const daysToMonday = day === 0 ? 1 : 1 - day;
|
||||||
|
// Créer la date de début (lundi 6h00)
|
||||||
|
const startDate = new Date(now);
|
||||||
|
startDate.setDate(now.getDate() + daysToMonday); // Passer au lundi
|
||||||
|
startDate.setHours(6, 0, 0, 0); // Fixer à 6h00
|
||||||
|
// Date de fin (6 jours après la date de début)
|
||||||
|
const endDate = new Date(startDate);
|
||||||
|
endDate.setDate(startDate.getDate() + 6); // Ajouter 6 jours
|
||||||
|
// Convertir les dates en timestamp
|
||||||
|
const startTimestamp = startDate.getTime();
|
||||||
|
const endTimestamp = endDate.getTime();
|
||||||
|
|
||||||
|
params.append(
|
||||||
|
"form:j_idt118_start",
|
||||||
|
startTimestamp.toString(),
|
||||||
|
);
|
||||||
|
params.append("form:j_idt118_end", endTimestamp.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response = await this.session.sendPOST<string>(
|
||||||
|
"faces/Planning.xhtml",
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
resolve(planningResponseToEvents(response));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ import NotesApi from "./NotesApi";
|
|||||||
class Session {
|
class Session {
|
||||||
private client: AxiosInstance;
|
private client: AxiosInstance;
|
||||||
|
|
||||||
|
//Permet de sauvegarder le ViewState et le subMenuId pour les réutiliser dans les prochaines requêtes (optimisation)
|
||||||
|
//Cela a pour but d'éviter d'effectuer 3 requêtes lorsque l'on refait la même demande (emploi du temps de la semaine suivante par exemple)
|
||||||
|
private viewStateCache: string = "";
|
||||||
|
private subMenuIdCache: string = "";
|
||||||
|
|
||||||
constructor(baseURL: string, token: string) {
|
constructor(baseURL: string, token: string) {
|
||||||
this.client = axios.create({
|
this.client = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
@@ -85,6 +90,47 @@ class Session {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Récupération du ViewState pour effectuer les différentes requêtes
|
||||||
|
public getViewState(subMenuId: string): Promise<string> {
|
||||||
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
|
//On optimise l'accès au ViewState
|
||||||
|
if (this.viewStateCache && this.subMenuIdCache === subMenuId) {
|
||||||
|
return resolve(this.viewStateCache);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const schedulePage = await this.sendGET<string>(
|
||||||
|
"/faces/Planning.xhtml",
|
||||||
|
);
|
||||||
|
let viewState = getViewState(schedulePage);
|
||||||
|
if (viewState) {
|
||||||
|
// Ici 291906 correspond au menu 'Scolarité' dans la sidebar
|
||||||
|
// Requête utile pour intialiser le ViewState (obligatoire pour effectuer une requête)
|
||||||
|
await this.sendSidebarRequest("291906", viewState);
|
||||||
|
|
||||||
|
// On récupère le ViewState pour effectuer la prochaine requête
|
||||||
|
viewState = await this.sendSidebarSubmenuRequest(
|
||||||
|
subMenuId,
|
||||||
|
viewState,
|
||||||
|
);
|
||||||
|
if (viewState) {
|
||||||
|
this.viewStateCache = viewState;
|
||||||
|
this.subMenuIdCache = subMenuId;
|
||||||
|
return resolve(viewState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reject(new Error("Viewstate not found"));
|
||||||
|
} catch (error) {
|
||||||
|
reject(new Error("Viewstate not found"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Permet de vider le cache du ViewState et du subMenuId (si besoin)
|
||||||
|
public clearViewStateCache(): void {
|
||||||
|
this.viewStateCache = "";
|
||||||
|
this.subMenuIdCache = "";
|
||||||
|
}
|
||||||
|
|
||||||
public sendGET<T>(url: string): Promise<T> {
|
public sendGET<T>(url: string): Promise<T> {
|
||||||
return this.client.get<T>(url).then((response) => response.data);
|
return this.client.get<T>(url).then((response) => response.data);
|
||||||
}
|
}
|
||||||
|
|||||||
47
tests/Cache.test.ts
Normal file
47
tests/Cache.test.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { login } from "../src/api/Session";
|
||||||
|
describe("CacheTests", () => {
|
||||||
|
it("should test for function performance", async () => {
|
||||||
|
const username = process.env.TEST_USERNAME;
|
||||||
|
const password = process.env.TEST_PASSWORD;
|
||||||
|
if (!username || !password) {
|
||||||
|
throw new Error(
|
||||||
|
"TEST_USERNAME or TEST_PASSWORD is not set in environment variables.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = await login(username, password);
|
||||||
|
|
||||||
|
//Première fois sans le cache on récupère l'emploi du temps
|
||||||
|
let start = performance.now();
|
||||||
|
let planning = await session.getPlanningApi().fetchPlanning();
|
||||||
|
let end = performance.now();
|
||||||
|
let duration = end - start;
|
||||||
|
console.log(`fetchPlanning (sans cache): ${duration.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
//Deuxième fois avec le cache on récupère l'emploi du temps
|
||||||
|
start = performance.now();
|
||||||
|
planning = await session.getPlanningApi().fetchPlanning();
|
||||||
|
end = performance.now();
|
||||||
|
duration = end - start;
|
||||||
|
console.log(`fetchPlanning (avec cache): ${duration.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
//Première fois sans le cache on récupère les notes
|
||||||
|
start = performance.now();
|
||||||
|
let notes = await session.getNotesApi().fetchNotes();
|
||||||
|
end = performance.now();
|
||||||
|
duration = end - start;
|
||||||
|
console.log(`fetchNotes (sans cache): ${duration.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
//Deuxième fois avec le cache on récupère les notes
|
||||||
|
start = performance.now();
|
||||||
|
notes = await session.getNotesApi().fetchNotes();
|
||||||
|
end = performance.now();
|
||||||
|
duration = end - start;
|
||||||
|
console.log(`fetchNotes (avec cache): ${duration.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
// console.log("Le planning", JSON.stringify(planning));
|
||||||
|
// console.log("Les notes ", JSON.stringify(notes));
|
||||||
|
expect(notes).toBeInstanceOf(Array);
|
||||||
|
expect(planning).toBeInstanceOf(Array);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user