work on schedule API

This commit is contained in:
dd060606
2024-11-17 15:48:57 +01:00
parent b0b91d7949
commit f8557394f8
5 changed files with 187 additions and 59 deletions

View File

@@ -1,4 +1,8 @@
import { getJSONSchedule, getViewState } from "../utils/AurionUtils";
import {
getJSFFormParams,
getViewState,
scheduleResponseToEvents,
} from "../utils/AurionUtils";
import Session from "./Session";
class ScheduleApi {
@@ -7,75 +11,66 @@ class ScheduleApi {
this.session = session;
}
public fetchSchedule(): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
// Récupération de l'emploi du temps en fonction de la date de début et de fin (timestamps en millisecondes)
public fetchSchedule(
startDate?: string,
endDate?: string,
): Promise<ScheduleEvent[]> {
return new Promise<ScheduleEvent[]>(async (resolve, reject) => {
try {
const schedulePage = await this.session.sendGET<string>(
"/faces/Planning.xhtml",
);
let viewState = getViewState(schedulePage);
if (viewState) {
const params = new URLSearchParams();
// On ajoute les paramètres nécessaires pour effectuer une requête POST
params.append("javax.faces.partial.ajax", "true");
params.append("javax.faces.source", "form:j_idt46");
params.append(
"javax.faces.partial.execute",
"form:j_idt46",
// 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_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,
);
params.append("javax.faces.partial.render", "form:sidebar");
params.append("form:j_idt46", "form:j_idt46");
params.append(
"webscolaapp.Sidebar.ID_SUBMENU",
"submenu_291906",
// On envoie enfin la requête pour obtenir l'emploi du temps
const params = getJSFFormParams(
"j_idt118",
"j_idt118",
viewState,
);
params.append("form", "form");
params.append("javax.faces.ViewState", viewState);
if (startDate && endDate) {
params.append("form:j_idt118_start", startDate);
params.append("form:j_idt118_end", endDate);
} else {
// On récupère le timestamp du lundi de la semaine en cours
const currentDate = new Date();
const currentDay = currentDate.getDay();
const daysUntilMonday =
(currentDay === 0 ? 1 : 8) - currentDay;
currentDate.setDate(
currentDate.getDate() + daysUntilMonday,
);
currentDate.setHours(0, 0, 0, 0);
const startTimestamp = currentDate.getTime();
const endTimestamp =
startTimestamp + 6 * 24 * 60 * 60 * 1000;
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,
);
const params2 = new URLSearchParams();
// On ajoute les paramètres nécessaires pour effectuer une requête POST
params2.append("form", "form");
params2.append("javax.faces.ViewState", viewState);
params2.append("form:sidebar", "form:sidebar");
params2.append("form:sidebar_menuid", "1_4");
const response2 = await this.session.sendPOST<string>(
"faces/Planning.xhtml",
params2,
);
viewState = getViewState(response2);
if (!viewState) {
return reject(new Error("Viewstate not found"));
}
const params3 = new URLSearchParams();
params3.append("javax.faces.partial.ajax", "true");
params3.append("javax.faces.source", "form:j_idt118");
params3.append(
"javax.faces.partial.execute",
"form:j_idt118",
);
params3.append(
"javax.faces.partial.render",
"form:j_idt118",
);
params3.append("form:j_idt118", "form:j_idt118");
params3.append("form:j_idt118_start", "1731279600000");
params3.append("form:j_idt118_end", "1731798000000");
params3.append("form", "form");
params3.append(
"form:idInit",
"webscolaapp.Planning_-6802915683822110557",
);
params3.append("javax.faces.ViewState", viewState);
const response3 = await this.session.sendPOST<string>(
"faces/Planning.xhtml",
params3,
);
console.log(getJSONSchedule(response3));
resolve(response2);
resolve(scheduleResponseToEvents(response));
} else {
reject(new Error("Viewstate not found"));
}

View File

@@ -1,5 +1,6 @@
import axios, { AxiosInstance } from "axios";
import ScheduleApi from "./ScheduleApi";
import { getJSFFormParams, getViewState } from "../utils/AurionUtils";
class Session {
private client: AxiosInstance;
@@ -19,6 +20,66 @@ class Session {
return new ScheduleApi(this);
}
// (1ère phase) Besoin de simuler le clic sur la sidebar pour obtenir le ViewState nécessaire aux fonctionnements des reqûetes
public sendSidebarRequest(
subMenuId: string,
viewState: string,
): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
try {
// 1 ère sidebar: formId = j_idt46, renderId = sidebar
const params = getJSFFormParams(
"j_idt46",
"sidebar",
viewState,
);
// On ajoute l'ID du sous-menu qui correspond à la rubrique chosie (Scolarité, mon compte, divers, ...)
params.append(
"webscolaapp.Sidebar.ID_SUBMENU",
`submenu_${subMenuId}`,
);
// On envoie la requête POST
const response = await this.sendPOST<string>(
"faces/Planning.xhtml",
params,
);
resolve(response);
} catch (err) {
reject(err);
}
});
}
// (2ème phase) Simulation du sous menu de la side bar pour obtenir le ViewState nécessaire aux fonctionnements des requêtes
// Cette fonction retourne un second ViewState qui sera utilisé pour effectuer les prochaines requêtes POST
public sendSidebarSubmenuRequest(
subMenuId: string,
viewState: string,
): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
try {
const params = new URLSearchParams();
// On ajoute les paramètres nécessaires pour effectuer une requête POST
params.append("form", "form");
params.append("javax.faces.ViewState", viewState);
params.append("form:sidebar", "form:sidebar");
params.append("form:sidebar_menuid", subMenuId);
const response = await this.sendPOST<string>(
"faces/Planning.xhtml",
params,
);
const secondViewState = getViewState(response);
if (secondViewState) {
resolve(secondViewState);
} else {
reject(new Error("Viewstate not found"));
}
} catch (err) {
reject(err);
}
});
}
public sendGET<T>(url: string): Promise<T> {
return this.client.get<T>(url).then((response) => response.data);
}

View File

@@ -22,3 +22,60 @@ export function getJSONSchedule(xml: string): object {
const json = parser('update[id="form:j_idt118"]').text();
return JSON.parse(json)["events"];
}
export function scheduleResponseToEvents(response: string): ScheduleEvent[] {
const json: any = getJSONSchedule(response);
return json.map((event: any) => {
// On récupère les informations des cours
const eventInfo = event.title.split(" - ");
let room = eventInfo[1].trim();
// Pour les matières qui ne sont pas bien formatées...
let subject = "";
let title = "";
if (eventInfo.length >= 9) {
subject = eventInfo[eventInfo.length - 5].trim();
title = eventInfo[eventInfo.length - 4].trim();
} else {
subject = eventInfo[eventInfo.length - 4].trim();
title = eventInfo[eventInfo.length - 3].trim();
}
let instructors = eventInfo[eventInfo.length - 2].trim();
let learners = eventInfo[eventInfo.length - 1].trim();
return {
id: event.id,
title,
subject,
room,
instructors,
learners,
start: event.start,
end: event.end,
allDay: event.allDay,
editable: event.editable,
className: event.className,
};
});
}
// Paramètres nécessaires pour effectuer une requête avec le backend Java Server Faces (JSF)
// Form ID : ID du formulaire (Récupérable avec BurpSuite / Inspecteur de requêtes)
// Render ID : ID de l'élément à mettre à jour (Récupérable avec BurpSuite / Inspecteur de requêtes)
export function getJSFFormParams(
formId: string,
renderId: string,
viewState: string,
): URLSearchParams {
const params = new URLSearchParams();
params.append("javax.faces.partial.ajax", "true");
params.append("javax.faces.source", `form:${formId}`);
params.append("javax.faces.partial.execute", `form:${formId}`);
params.append("javax.faces.partial.render", `form:${renderId}`);
params.append(`form:${formId}`, `form:${formId}`);
params.append("form", "form");
params.append("javax.faces.ViewState", viewState);
return params;
}

View File

@@ -0,0 +1,13 @@
type ScheduleEvent = {
id: string;
title: string;
subject: string;
room: string;
instructors: string;
learners: string;
start: string;
end: string;
allDay: boolean;
editable: boolean;
className: string;
};

View File

@@ -10,6 +10,8 @@ describe("ScheduleApi", () => {
}
const session = await login(username, password);
await session.getScheduleApi().fetchSchedule();
const schedule = await session.getScheduleApi().fetchSchedule();
console.log(schedule);
expect(schedule).not.toBeNull();
});
});