diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..09cf720 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "denoland.vscode-deno" + ] +} diff --git a/deno.jsonc b/deno.jsonc index 175e392..f6d841b 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -2,5 +2,8 @@ "tasks": { "npm": "deno run -A scripts/build_npm.ts" }, - "exclude": ["npm"] + "exclude": ["npm"], + "imports": { + "~/": "./" + } } diff --git a/deno.lock b/deno.lock index f44f1e6..43b65b6 100644 --- a/deno.lock +++ b/deno.lock @@ -79,6 +79,38 @@ "https://deno.land/std@0.200.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", "https://deno.land/std@0.200.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", "https://deno.land/std@0.200.0/fmt/colors.ts": "a7eecffdf3d1d54db890723b303847b6e0a1ab4b528ba6958b8f2e754cf1b3bc", + "https://deno.land/std@0.201.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.201.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", + "https://deno.land/std@0.201.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.201.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.201.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.201.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.201.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.201.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.201.0/assert/assert_false.ts": "a9962749f4bf5844e3fa494257f1de73d69e4fe0e82c34d0099287552163a2dc", + "https://deno.land/std@0.201.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.201.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.201.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.201.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.201.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.201.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.201.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.201.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.201.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.201.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.201.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.201.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.201.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.201.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.201.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.201.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.201.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.201.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.201.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.201.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.201.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.201.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.201.0/fmt/colors.ts": "87544aa2bc91087bb37f9c077970c85bfb041b48e4c37356129d7b450a415b6f", "https://deno.land/x/code_block_writer@12.0.0/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", "https://deno.land/x/code_block_writer@12.0.0/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", "https://deno.land/x/deno_cache@0.5.2/auth_tokens.ts": "5d1d56474c54a9d152e44d43ea17c2e6a398dd1e9682c69811a313567c01ee1e", diff --git a/deps_dev.ts b/deps_dev.ts new file mode 100644 index 0000000..e094a6d --- /dev/null +++ b/deps_dev.ts @@ -0,0 +1,5 @@ +export { + assertEquals, + assertExists, + assertRejects, +} from "https://deno.land/std@0.201.0/assert/mod.ts"; diff --git a/index.html b/index.html index e005a08..c532332 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,10 @@ ClassCharts-API - + -
diff --git a/mod.ts b/mod.ts index 249b084..7650092 100644 --- a/mod.ts +++ b/mod.ts @@ -1,2 +1,2 @@ -export * from "./src/core/studentClient.ts"; -export * from "./src/core/parentClient.ts"; +export * from "~/src/core/studentClient.ts"; +export * from "~/src/core/parentClient.ts"; diff --git a/scripts/build_npm.ts b/scripts/build_npm.ts index 4d7cf8d..8cce7c8 100644 --- a/scripts/build_npm.ts +++ b/scripts/build_npm.ts @@ -1,3 +1,4 @@ +// This dependancy cannot be moved to dev_deps.ts since dnt complains about a top-level await import { build, emptyDir } from "https://deno.land/x/dnt@0.38.1/mod.ts"; if (!Deno.args[0]) throw new Error("No version specified"); @@ -13,6 +14,7 @@ await build({ path: "./src/types.ts", }], outDir: "./npm", + importMap: "./deno.jsonc", shims: { deno: true, }, @@ -22,13 +24,12 @@ await build({ }, typeCheck: "both", package: { - // package.json properties name: "classcharts-api", + version: String(Deno.args[0]).replace("v", ""), author: { name: "James Cook", email: "james@jaminit.co.uk", }, - version: Deno.args[0], description: "A Typescript wrapper for getting information from the ClassCharts API", license: "ISC", @@ -47,7 +48,6 @@ await build({ sideEffects: false, }, postBuild() { - // steps to run after building and before running the tests Deno.copyFileSync("LICENSE", "npm/LICENSE"); Deno.copyFileSync("README.md", "npm/README.md"); }, diff --git a/src/core/baseClient.ts b/src/core/baseClient.ts index b243d15..8c3ebac 100644 --- a/src/core/baseClient.ts +++ b/src/core/baseClient.ts @@ -17,7 +17,7 @@ import type { LessonsResponse, PupilFieldsResponse, } from "../types.ts"; -import { PING_INTERVAL } from "../utils/consts.ts"; +import { PING_INTERVAL } from "~/src/utils/consts.ts"; /** * Shared client for both parent and student. This is not exported and should not be used directly @@ -105,7 +105,8 @@ export class BaseClient { } } const request = await fetch(path, requestOptions); - let responseJSON: ClassChartsResponse; + // deno-lint-ignore no-explicit-any + let responseJSON: ClassChartsResponse; try { responseJSON = await request.json(); } catch { @@ -116,8 +117,7 @@ export class BaseClient { if (responseJSON.success == 0) { throw new Error(responseJSON.error); } - // deno-lint-ignore no-explicit-any - return responseJSON as unknown as any; + return responseJSON; } /** * Gets general information about the current student diff --git a/src/core/parentClient.ts b/src/core/parentClient.ts index b20882d..6f2451a 100644 --- a/src/core/parentClient.ts +++ b/src/core/parentClient.ts @@ -1,8 +1,8 @@ import type { GetPupilsResponse } from "../types.ts"; -import { BaseClient } from "./baseClient.ts"; -import { API_BASE_PARENT, BASE_URL } from "../utils/consts.ts"; -import { parseCookies } from "../utils/utils.ts"; +import { BaseClient } from "~/src/core/baseClient.ts"; +import { API_BASE_PARENT, BASE_URL } from "~/src/utils/consts.ts"; +import { parseCookies } from "~/src/utils/utils.ts"; /** * Parent Client */ @@ -26,6 +26,7 @@ export class ParentClient extends BaseClient { */ async login(): Promise { if (!this.email) throw new Error("Email not provided"); + if (!this.password) throw new Error("Password not provided"); const formData = new URLSearchParams(); formData.append("_method", "POST"); formData.append("email", this.email); @@ -39,14 +40,12 @@ export class ParentClient extends BaseClient { method: "POST", body: formData, headers: headers, - credentials: undefined, + redirect: "manual", }); if (response.status != 302 || !response.headers.has("set-cookie")) { + await response.body?.cancel(); // Make deno tests happy by closing the body, unsure whether this is needed for the actual library throw new Error( - "Unauthenticated: ClassCharts returned an error: " + - response.status + - " " + - response.statusText, + "Unauthenticated: ClassCharts didn't return authentication cookies", ); } @@ -56,19 +55,20 @@ export class ParentClient extends BaseClient { const sessionID = JSON.parse( String(sessionCookies["parent_session_credentials"]), ); - super.sessionId = sessionID.session_id; + this.sessionId = sessionID.session_id; this.pupils = await this.getPupils(); if (!this.pupils) throw new Error("Account has no pupils attached"); - super.studentId = this.pupils[0].id; + this.studentId = this.pupils[0].id; } /** * Get a list of pupils connected to this parent's account * @returns an array of Pupils connected to this parent's account */ async getPupils(): Promise { - return await super.makeAuthedRequest(super.API_BASE + "/pupils", { + const response = await this.makeAuthedRequest(this.API_BASE + "/pupils", { method: "GET", }); + return response.data; } /** * Selects a pupil to be used with API requests @@ -82,7 +82,7 @@ export class ParentClient extends BaseClient { for (let i = 0; i < pupils.length; i++) { const pupil = pupils[i]; if (pupil.id == pupilId) { - super.studentId = pupil.id; + this.studentId = pupil.id; return; } } diff --git a/src/core/parentClient_test.ts b/src/core/parentClient_test.ts new file mode 100644 index 0000000..d947082 --- /dev/null +++ b/src/core/parentClient_test.ts @@ -0,0 +1,35 @@ +import { assertRejects } from "~/deps_dev.ts"; +import { ParentClient } from "~/src/core/parentClient.ts"; + +Deno.test("Throws when no email is provided", async () => { + const client = new ParentClient("", "password"); + await assertRejects( + async () => { + await client.login(); + }, + Error, + "Email not provided", + ); +}); + +Deno.test("Throws when no password is provided", async () => { + const client = new ParentClient("email", ""); + await assertRejects( + async () => { + await client.login(); + }, + Error, + "Password not provided", + ); +}); + +Deno.test("Throws with invalid username and password", async () => { + const client = new ParentClient("invalid", "invalid"); + await assertRejects( + async () => { + await client.login(); + }, + Error, + "Unauthenticated: ClassCharts didn't return authentication cookies", + ); +}); diff --git a/src/core/studentClient.ts b/src/core/studentClient.ts index 2be301d..3e44937 100644 --- a/src/core/studentClient.ts +++ b/src/core/studentClient.ts @@ -1,7 +1,7 @@ -import { API_BASE_STUDENT, BASE_URL } from "../utils/consts.ts"; -import { BaseClient } from "./baseClient.ts"; -import { parseCookies } from "../utils/utils.ts"; -import { RewardPurchaseResponse, RewardsResponse } from "../types.ts" +import { API_BASE_STUDENT, BASE_URL } from "~/src/utils/consts.ts"; +import { BaseClient } from "~/src/core/baseClient.ts"; +import { parseCookies } from "~/src/utils/utils.ts"; +import { RewardPurchaseResponse, RewardsResponse } from "~/src/types.ts" /** * Student Client diff --git a/src/core/studentClient_test.ts b/src/core/studentClient_test.ts index 6932a75..a5b000a 100644 --- a/src/core/studentClient_test.ts +++ b/src/core/studentClient_test.ts @@ -1,6 +1,5 @@ -import { assertRejects } from "https://deno.land/std@0.200.0/assert/mod.ts"; - -import { StudentClient } from "./studentClient.ts"; +import { assertRejects } from "~/deps_dev.ts"; +import { StudentClient } from "~/src/core/studentClient.ts"; Deno.test("Throws when no student code is provided", async () => { const client = new StudentClient(""); diff --git a/src/utils/utils_test.ts b/src/utils/utils_test.ts index 7ecfdff..ccb7696 100644 --- a/src/utils/utils_test.ts +++ b/src/utils/utils_test.ts @@ -1,8 +1,4 @@ -import { - assertEquals, - assertExists, -} from "https://deno.land/std@0.200.0/assert/mod.ts"; - +import { assertEquals, assertExists } from "~/deps_dev.ts"; import { parseCookies } from "./utils.ts"; Deno.test("Parses simple cookie", () => { const cookie =