Skip to content

Commit

Permalink
Add documents
Browse files Browse the repository at this point in the history
  • Loading branch information
a2br committed Nov 17, 2022
1 parent fd3814f commit 809fc60
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 36 deletions.
38 changes: 32 additions & 6 deletions lib/accounts/Student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
gradesResSuccess,
studentAccountModule,
course as _course,
studentDocsResSuccess,
} from "ecoledirecte-api-types/v3";
import {
getMainAccount,
Expand All @@ -20,8 +21,9 @@ import {
getCloudFolder,
getTimetable,
getWallets,
getDocuments,
} from "../util";
import { Cloud, TimelineElem } from "../classes";
import { Cloud, Document, TimelineElem } from "../classes";
import { Message, Grade, Period, Assignement, Course } from "../classes";

import { getUpcomingAssignementDates } from "../util/student/textbook";
Expand Down Expand Up @@ -216,14 +218,38 @@ export class Student extends Account {
): Promise<Course[]> {
const _timetable = await getTimetable(this, dates);
this.token = _timetable.token;
const timetable = await Promise.all(
_timetable.data.map(async (c: _course) => {
return new Course(c);
})
);
const timetable = _timetable.data.map((c: _course) => new Course(c));

return timetable;
}

async getDocuments(archiveYear?: string): Promise<{
admin: Document[];
bills: Document[];
grades: Document[];
schoolLife: Document[];
_raw: studentDocsResSuccess["data"];
}> {
const _documents = await getDocuments(this.token, archiveYear);
this.token = _documents.token;
const documents = {
admin: _documents.data.administratifs.map(
d => new Document(d, this, archiveYear)
),
bills: _documents.data.factures.map(
d => new Document(d, this, archiveYear)
),
grades: _documents.data.notes.map(
d => new Document(d, this, archiveYear)
),
schoolLife: _documents.data.viescolaire.map(
d => new Document(d, this, archiveYear)
),
_raw: _documents.data,
};
return documents;
}

hasModule(module: studentAccountModule["code"]): boolean {
return this._raw.modules.some(m => m.code === module && m.enable);
}
Expand Down
33 changes: 33 additions & 0 deletions lib/classes/Document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { downloadDocument } from "../util";
import { studentDoc as _doc } from "ecoledirecte-api-types/v3";
import { Account } from "../accounts";

export class Document {
date: Date;
id: number;
studentId: number;
name: string;
type?: string;

_raw: _doc;

constructor(o: _doc, private account: Account, private archiveYear?: string) {
this.date = new Date(o.date);
this.id = o.id;
this.studentId = o.idEleve;
this.name = o.libelle;
this.type = o.type || undefined;

this._raw = o;
}

async download() {
const doc = await downloadDocument(
this.account.token,
this.id,
this.type || "",
this.archiveYear
);
return doc;
}
}
1 change: 1 addition & 0 deletions lib/classes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export * from "./Timeline";
export * from "./Cloud";
export * from "./Course";
export * from "./School";
export * from "./Document";

export * from "./ExpandedB64";
4 changes: 4 additions & 0 deletions lib/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export * from "./student/timelines";

export * from "./student/wallets";

//! DOCUMENTS

export * from "./student/documents";

//! CLOUD

export * from "./global/cloud";
Expand Down
49 changes: 49 additions & 0 deletions lib/util/student/documents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Routes } from "ecoledirecte-api-types/v3";
import { studentDocsResSuccess } from "ecoledirecte-api-types/v3";

import { makeRequest } from "../util";

export async function getDocuments(
token: string,
archiveYear?: string
): Promise<studentDocsResSuccess> {
const body: studentDocsResSuccess = await makeRequest({
method: "POST",
path: Routes.studentDocuments({ archive: archiveYear ?? "" }),
body: { token },
guard: true,
});

return body;
}

export async function downloadDocument(
token: string,
documentId: number,
fileType?: string,
archiveYear?: string
): Promise<Blob> {
const body: Blob = await makeRequest(
{
method: "GET",
path: Routes.downloadDocument({
fichierId: documentId,
leTypeDeFichier: fileType || "",
archive: !!archiveYear,
anneeArchive: archiveYear || "",
}),
body: {
forceDownload: 0,
archivve: !!archiveYear,
anneeArchive: archiveYear || "",
},
guard: true,
expectBuffer: true,
},
undefined,
undefined,
token
);

return body;
}
52 changes: 48 additions & 4 deletions lib/util/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fetch, { RequestInit } from "node-fetch";
import mime from "mime-types";
import { failureRes, isFailure, root, rootp } from "ecoledirecte-api-types/v3";

import logs from "../events";
Expand Down Expand Up @@ -39,20 +40,44 @@ export function formatBytes(bytes: number): string {
return formatted;
}

function getFileName(contentDisposition: string): string {
const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; ?|$)/i;
const asciiFilenameRegex = /^filename=(["']?)(.*?[^\\])\1(?:; ?|$)/i;

let fileName = "";
if (utf8FilenameRegex.test(contentDisposition)) {
const execArray = utf8FilenameRegex.exec(contentDisposition);
fileName = decodeURIComponent(execArray ? execArray[1] : "");
} else {
// prevent ReDos attacks by anchoring the ascii regex to string start and
// slicing off everything before 'filename='
const filenameStart = contentDisposition.toLowerCase().indexOf("filename=");
if (filenameStart >= 0) {
const partialDisposition = contentDisposition.slice(filenameStart);
const matches = asciiFilenameRegex.exec(partialDisposition);
if (matches != null && matches[2]) {
fileName = matches[2];
}
}
}
return fileName;
}

export async function makeRequest(
options: {
method: "GET" | "POST";
path: string;
body?: Record<string, unknown>;
guard?: boolean;
teachRoot?: boolean;
expectBuffer?: boolean;
},
context: Record<string, unknown> = {},
account?: Account,
token?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> {
const { method, path, body, guard, teachRoot } = options;
const { method, path, body, guard, teachRoot, expectBuffer } = options;
const url = Config.get(teachRoot ? "rootp" : "root") + path;
const resListener = new EventEmitter();
function onRes(callback: (res: Response) => void) {
Expand Down Expand Up @@ -87,17 +112,36 @@ export async function makeRequest(

const response = await fetch(url, params);

const resBody = (await response.json()) as Record<string, unknown>;
const resBody = expectBuffer
? await response.buffer()
: ((await response.json()) as Record<string, unknown>);

resListener.emit("response", { response, body: resBody });

const failure = isFailure(resBody);
const failure =
!expectBuffer && isFailure(resBody as Record<string, unknown>);
if (guard && failure) throw new EcoleDirecteAPIError(resBody as failureRes);

const someToken =
(resBody.token as string | undefined) || response.headers.get("x-token");
(!expectBuffer &&
((resBody as Record<string, unknown>).token as string | undefined)) ||
response.headers.get("x-token");

if (!failure && account && someToken) account.token = someToken;

if (expectBuffer) {
const buff = resBody as Buffer;
const contentDisposition = response.headers.get("content-disposition");
if (!contentDisposition) throw new Error("No content-disposition");
// Get file name
const fileName = getFileName(contentDisposition);
// Get MIME type
const mimeType = mime.lookup(fileName);
// Create Blob
const blob = new Blob([buff], { type: mimeType || undefined });
return blob;
}

return resBody;
}

Expand Down
60 changes: 35 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 809fc60

Please sign in to comment.