From bed5143cab11ccbb3a4456a97ce1fb56fb92fce9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:25:12 +0000 Subject: [PATCH 1/7] Initial plan From 9b9000dde0ef486af25b76bc4225a3ace3290cf8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:32:56 +0000 Subject: [PATCH 2/7] Add comprehensive OpenAPI 3.0 documentation Co-authored-by: Packjackisback <83289186+Packjackisback@users.noreply.github.com> --- OPENAPI.md | 94 +++++ openapi.json | 968 ++++++++++++++++++++++++++++++++++++++++++++++++ openapi.yaml | 586 +++++++++++++++++++++++++++++ src/handlers.rs | 22 +- src/routes.rs | 4 +- 5 files changed, 1672 insertions(+), 2 deletions(-) create mode 100644 OPENAPI.md create mode 100644 openapi.json create mode 100644 openapi.yaml diff --git a/OPENAPI.md b/OPENAPI.md new file mode 100644 index 0000000..d28b164 --- /dev/null +++ b/OPENAPI.md @@ -0,0 +1,94 @@ +# OpenAPI Documentation + +This directory contains the OpenAPI 3.0 specification for the Home Access Center API. + +## Files + +- **openapi.yaml** - OpenAPI specification in YAML format (recommended for editing) +- **openapi.json** - OpenAPI specification in JSON format (auto-generated from YAML) + +## Usage + +### Viewing the Documentation + +The OpenAPI documentation is served directly by the API at: +- YAML: https://hac.packjack.dev/openapi.yaml +- JSON: https://hac.packjack.dev/openapi.json + +### Using with API Tools + +You can use these specification files with various OpenAPI tools: + +#### Swagger UI +Visit https://editor.swagger.io/ and import the URL: +``` +https://hac.packjack.dev/openapi.yaml +``` + +#### Redoc +Visit https://redocly.github.io/redoc/ and import the URL: +``` +https://hac.packjack.dev/openapi.yaml +``` + +#### Postman +1. Open Postman +2. Click "Import" +3. Select "Link" tab +4. Paste: `https://hac.packjack.dev/openapi.json` +5. Click "Import" + +#### curl (download locally) +```bash +curl -O https://hac.packjack.dev/openapi.yaml +curl -O https://hac.packjack.dev/openapi.json +``` + +## Updating the Documentation + +If you modify `openapi.yaml`, regenerate the JSON file: + +```bash +npm install -g js-yaml +npx js-yaml openapi.yaml > openapi.json +``` + +To validate the OpenAPI spec: + +```bash +npm install -g @apidevtools/swagger-cli +swagger-cli validate openapi.yaml +``` + +## API Overview + +The Home Access Center API provides 11 endpoints: + +### Student Information +- `GET /api/name` - Get student name +- `GET /api/info` - Get student profile information + +### Classes +- `GET /api/classes` - Get list of classes +- `GET /api/averages` - Get class averages +- `GET /api/weightings` - Get grade category weightings + +### Assignments & Grades +- `GET /api/assignments` - Get detailed assignments +- `GET /api/gradebook` - Get complete gradebook with grades and weightings + +### Reports +- `GET /api/reportcard` - Get report card +- `GET /api/ipr` - Get interim progress report +- `GET /api/transcript` - Get full transcript with GPA +- `GET /api/rank` - Get GPA rank and quartile + +All endpoints (except `/` and `/api/`) require authentication via query parameters: +- `user` - Home Access Center username +- `pass` - Home Access Center password + +Optional parameters: +- `link` - Home Access Center base URL (defaults to https://homeaccess.katyisd.org) +- `short` - Return shortened class names (boolean) +- `six_weeks` - Specific six weeks period (for assignment endpoints) +- `no_cache` - Bypass cache and fetch fresh data (boolean) diff --git a/openapi.json b/openapi.json new file mode 100644 index 0000000..26cea7a --- /dev/null +++ b/openapi.json @@ -0,0 +1,968 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Home Access Center API", + "description": "A REST API for accessing Home Access Center student data. This API provides programmatic access to student information including grades, assignments, classes, transcripts, and more.\n\n## Authentication\n\nAll endpoints (except the root endpoint) require authentication via query parameters:\n- `user`: Your Home Access Center username\n- `pass`: Your Home Access Center password\n\n## Caching\n\nThe API implements intelligent caching:\n- Login sessions are cached for 30 minutes\n- Page data is cached for 5 minutes\n- Add `?no_cache=true` to any endpoint to bypass cache\n\n## Base URL\n\nThis API is hosted at: `https://hac.packjack.dev`\n", + "version": "0.1.0", + "contact": { + "name": "Home Access Center API" + }, + "license": { + "name": "MIT" + } + }, + "servers": [ + { + "url": "https://hac.packjack.dev", + "description": "Production server" + } + ], + "tags": [ + { + "name": "Student Info", + "description": "Basic student information" + }, + { + "name": "Classes", + "description": "Class-related information" + }, + { + "name": "Assignments", + "description": "Assignment and grade data" + }, + { + "name": "Reports", + "description": "Report cards and transcripts" + } + ], + "paths": { + "/": { + "get": { + "summary": "API Information", + "description": "Returns welcome message and available routes", + "tags": [ + "Student Info" + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "example": "Welcome to the Home Access Center API!" + }, + "message": { + "type": "string", + "example": "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + }, + "routes": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "/api/name", + "/api/info", + "/api/classes" + ] + }, + "cache_param": { + "type": "string", + "example": "Add ?no_cache=true to any endpoint to bypass cache" + } + } + } + } + } + } + } + } + }, + "/api/": { + "get": { + "summary": "API Information (alternate route)", + "description": "Returns welcome message and available routes", + "tags": [ + "Student Info" + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WelcomeMessage" + } + } + } + } + } + } + }, + "/api/name": { + "get": { + "summary": "Get Student Name", + "description": "Retrieves the student's name from Home Access Center", + "tags": [ + "Student Info" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "John Doe" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/info": { + "get": { + "summary": "Get Student Information", + "description": "Retrieves detailed student profile information", + "tags": [ + "Student Info" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "John Doe" + }, + "grade": { + "type": "string", + "example": "11" + }, + "school": { + "type": "string", + "example": "Example High School" + }, + "dob": { + "type": "string", + "example": "01/01/2007" + }, + "counselor": { + "type": "string", + "example": "Jane Smith" + }, + "language": { + "type": "string", + "example": "English" + }, + "cohort_year": { + "type": "string", + "example": "2025" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/classes": { + "get": { + "summary": "Get Class List", + "description": "Retrieves a list of the student's classes", + "tags": [ + "Classes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/Short" + }, + { + "$ref": "#/components/parameters/SixWeeks" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "AP Calculus BC", + "English III Honors", + "AP Chemistry" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/averages": { + "get": { + "summary": "Get Class Averages", + "description": "Retrieves current averages for all classes", + "tags": [ + "Classes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/Short" + }, + { + "$ref": "#/components/parameters/SixWeeks" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "AP Calculus BC": "95", + "English III Honors": "92", + "AP Chemistry": "88" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/assignments": { + "get": { + "summary": "Get Assignments", + "description": "Retrieves detailed assignment information for all classes", + "tags": [ + "Assignments" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/Short" + }, + { + "$ref": "#/components/parameters/SixWeeks" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "example": { + "AP Calculus BC": [ + [ + "10/15/2024", + "Quiz 3", + "Major Grades", + "95", + "100" + ], + [ + "10/10/2024", + "Homework 5", + "Daily Grades", + "10", + "10" + ] + ] + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/gradebook": { + "get": { + "summary": "Get Complete Gradebook", + "description": "Retrieves assignments with grades and weightings for all classes", + "tags": [ + "Assignments" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/Short" + }, + { + "$ref": "#/components/parameters/SixWeeks" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "assignments": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "weightings": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/weightings": { + "get": { + "summary": "Get Grade Weightings", + "description": "Retrieves grade category weightings for all classes", + "tags": [ + "Classes" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/Short" + }, + { + "$ref": "#/components/parameters/SixWeeks" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "example": { + "AP Calculus BC": [ + [ + "Major Grades", + "60%", + "95.0" + ], + [ + "Daily Grades", + "40%", + "98.0" + ] + ] + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/reportcard": { + "get": { + "summary": "Get Report Card", + "description": "Retrieves report card tables with grades per marking period", + "tags": [ + "Reports" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": [ + [ + "Course", + "Teacher", + "MP1", + "MP2", + "Sem1", + "MP3", + "MP4", + "Sem2", + "Final" + ], + [ + "AP Calculus BC", + "Smith, John", + "95", + "93", + "94", + "96", + "94", + "95", + "95" + ] + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/ipr": { + "get": { + "summary": "Get Interim Progress Report", + "description": "Retrieves interim progress report data", + "tags": [ + "Reports" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": [ + [ + "Course", + "Teacher", + "Grade", + "Absences", + "Tardies" + ], + [ + "AP Calculus BC", + "Smith, John", + "95", + "0", + "0" + ] + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/transcript": { + "get": { + "summary": "Get Full Transcript", + "description": "Retrieves complete transcript with GPA and semester information", + "tags": [ + "Reports" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "rank": { + "type": "string", + "example": "15 of 450" + }, + "quartile": { + "type": "string", + "example": "Top 25%" + }, + "Weighted GPA": { + "type": "string", + "example": "4.35" + }, + "Unweighted GPA": { + "type": "string", + "example": "3.95" + } + }, + "additionalProperties": { + "type": "object", + "properties": { + "year": { + "type": "string" + }, + "semester": { + "type": "string" + }, + "credits": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/api/rank": { + "get": { + "summary": "Get GPA Rank", + "description": "Retrieves GPA rank and quartile information", + "tags": [ + "Reports" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Username" + }, + { + "$ref": "#/components/parameters/Password" + }, + { + "$ref": "#/components/parameters/Link" + }, + { + "$ref": "#/components/parameters/NoCache" + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "rank": { + "type": "string", + "example": "15 of 450" + }, + "quartile": { + "type": "string", + "example": "Top 25%" + }, + "Weighted GPA": { + "type": "string", + "example": "4.35" + }, + "Unweighted GPA": { + "type": "string", + "example": "3.95" + } + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + }, + "components": { + "parameters": { + "Username": { + "name": "user", + "in": "query", + "required": true, + "description": "Home Access Center username", + "schema": { + "type": "string" + }, + "example": "student123" + }, + "Password": { + "name": "pass", + "in": "query", + "required": true, + "description": "Home Access Center password", + "schema": { + "type": "string", + "format": "password" + }, + "example": "mypassword" + }, + "Link": { + "name": "link", + "in": "query", + "required": false, + "description": "Home Access Center base URL (defaults to https://homeaccess.katyisd.org)", + "schema": { + "type": "string", + "format": "uri", + "default": "https://homeaccess.katyisd.org" + }, + "example": "https://homeaccess.katyisd.org" + }, + "Short": { + "name": "short", + "in": "query", + "required": false, + "description": "Whether to return shortened class names", + "schema": { + "type": "boolean", + "default": false + }, + "example": true + }, + "SixWeeks": { + "name": "six_weeks", + "in": "query", + "required": false, + "description": "Specific six weeks period to retrieve assignments for", + "schema": { + "type": "string" + }, + "example": "1" + }, + "NoCache": { + "name": "no_cache", + "in": "query", + "required": false, + "description": "Bypass cache and fetch fresh data", + "schema": { + "type": "boolean", + "default": false + }, + "example": true + } + }, + "schemas": { + "WelcomeMessage": { + "type": "object", + "properties": { + "title": { + "type": "string", + "example": "Welcome to the Home Access Center API!" + }, + "message": { + "type": "string", + "example": "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + }, + "routes": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "/api/name", + "/api/info", + "/api/classes" + ] + }, + "cache_param": { + "type": "string", + "example": "Add ?no_cache=true to any endpoint to bypass cache" + } + } + }, + "ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Error message describing what went wrong" + } + } + } + }, + "responses": { + "Unauthorized": { + "description": "Invalid username or password", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "Invalid username or password" + } + } + } + }, + "InternalServerError": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "Failed to fetch data from Home Access Center" + } + } + } + } + }, + "securitySchemes": { + "QueryAuth": { + "type": "apiKey", + "in": "query", + "name": "user", + "description": "Authentication via username and password query parameters" + } + } + } +} diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..19d8e8c --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,586 @@ +openapi: 3.0.3 +info: + title: Home Access Center API + description: | + A REST API for accessing Home Access Center student data. This API provides programmatic access to student information including grades, assignments, classes, transcripts, and more. + + ## Authentication + + All endpoints (except the root endpoint) require authentication via query parameters: + - `user`: Your Home Access Center username + - `pass`: Your Home Access Center password + + ## Caching + + The API implements intelligent caching: + - Login sessions are cached for 30 minutes + - Page data is cached for 5 minutes + - Add `?no_cache=true` to any endpoint to bypass cache + + ## Base URL + + This API is hosted at: `https://hac.packjack.dev` + version: 0.1.0 + contact: + name: Home Access Center API + license: + name: MIT + +servers: + - url: https://hac.packjack.dev + description: Production server + +tags: + - name: Student Info + description: Basic student information + - name: Classes + description: Class-related information + - name: Assignments + description: Assignment and grade data + - name: Reports + description: Report cards and transcripts + +paths: + /: + get: + summary: API Information + description: Returns welcome message and available routes + tags: + - Student Info + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + title: + type: string + example: "Welcome to the Home Access Center API!" + message: + type: string + example: "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + routes: + type: array + items: + type: string + example: ["/api/name", "/api/info", "/api/classes"] + cache_param: + type: string + example: "Add ?no_cache=true to any endpoint to bypass cache" + + /api/: + get: + summary: API Information (alternate route) + description: Returns welcome message and available routes + tags: + - Student Info + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/WelcomeMessage' + + /api/name: + get: + summary: Get Student Name + description: Retrieves the student's name from Home Access Center + tags: + - Student Info + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: "John Doe" + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/info: + get: + summary: Get Student Information + description: Retrieves detailed student profile information + tags: + - Student Info + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: "John Doe" + grade: + type: string + example: "11" + school: + type: string + example: "Example High School" + dob: + type: string + example: "01/01/2007" + counselor: + type: string + example: "Jane Smith" + language: + type: string + example: "English" + cohort_year: + type: string + example: "2025" + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/classes: + get: + summary: Get Class List + description: Retrieves a list of the student's classes + tags: + - Classes + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/Short' + - $ref: '#/components/parameters/SixWeeks' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + type: string + example: ["AP Calculus BC", "English III Honors", "AP Chemistry"] + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/averages: + get: + summary: Get Class Averages + description: Retrieves current averages for all classes + tags: + - Classes + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/Short' + - $ref: '#/components/parameters/SixWeeks' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: + type: string + example: + "AP Calculus BC": "95" + "English III Honors": "92" + "AP Chemistry": "88" + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/assignments: + get: + summary: Get Assignments + description: Retrieves detailed assignment information for all classes + tags: + - Assignments + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/Short' + - $ref: '#/components/parameters/SixWeeks' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: + type: array + items: + type: array + items: + type: string + example: + "AP Calculus BC": + - ["10/15/2024", "Quiz 3", "Major Grades", "95", "100"] + - ["10/10/2024", "Homework 5", "Daily Grades", "10", "10"] + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/gradebook: + get: + summary: Get Complete Gradebook + description: Retrieves assignments with grades and weightings for all classes + tags: + - Assignments + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/Short' + - $ref: '#/components/parameters/SixWeeks' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: + type: object + properties: + assignments: + type: array + items: + type: array + items: + type: string + weightings: + type: array + items: + type: array + items: + type: string + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/weightings: + get: + summary: Get Grade Weightings + description: Retrieves grade category weightings for all classes + tags: + - Classes + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/Short' + - $ref: '#/components/parameters/SixWeeks' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: + type: array + items: + type: array + items: + type: string + example: + "AP Calculus BC": + - ["Major Grades", "60%", "95.0"] + - ["Daily Grades", "40%", "98.0"] + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/reportcard: + get: + summary: Get Report Card + description: Retrieves report card tables with grades per marking period + tags: + - Reports + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + example: + - ["Course", "Teacher", "MP1", "MP2", "Sem1", "MP3", "MP4", "Sem2", "Final"] + - ["AP Calculus BC", "Smith, John", "95", "93", "94", "96", "94", "95", "95"] + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/ipr: + get: + summary: Get Interim Progress Report + description: Retrieves interim progress report data + tags: + - Reports + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + example: + - ["Course", "Teacher", "Grade", "Absences", "Tardies"] + - ["AP Calculus BC", "Smith, John", "95", "0", "0"] + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/transcript: + get: + summary: Get Full Transcript + description: Retrieves complete transcript with GPA and semester information + tags: + - Reports + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + rank: + type: string + example: "15 of 450" + quartile: + type: string + example: "Top 25%" + Weighted GPA: + type: string + example: "4.35" + Unweighted GPA: + type: string + example: "3.95" + additionalProperties: + type: object + properties: + year: + type: string + semester: + type: string + credits: + type: string + data: + type: array + items: + type: array + items: + type: string + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + + /api/rank: + get: + summary: Get GPA Rank + description: Retrieves GPA rank and quartile information + tags: + - Reports + parameters: + - $ref: '#/components/parameters/Username' + - $ref: '#/components/parameters/Password' + - $ref: '#/components/parameters/Link' + - $ref: '#/components/parameters/NoCache' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + rank: + type: string + example: "15 of 450" + quartile: + type: string + example: "Top 25%" + Weighted GPA: + type: string + example: "4.35" + Unweighted GPA: + type: string + example: "3.95" + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + +components: + parameters: + Username: + name: user + in: query + required: true + description: Home Access Center username + schema: + type: string + example: "student123" + + Password: + name: pass + in: query + required: true + description: Home Access Center password + schema: + type: string + format: password + example: "mypassword" + + Link: + name: link + in: query + required: false + description: Home Access Center base URL (defaults to https://homeaccess.katyisd.org) + schema: + type: string + format: uri + default: https://homeaccess.katyisd.org + example: "https://homeaccess.katyisd.org" + + Short: + name: short + in: query + required: false + description: Whether to return shortened class names + schema: + type: boolean + default: false + example: true + + SixWeeks: + name: six_weeks + in: query + required: false + description: Specific six weeks period to retrieve assignments for + schema: + type: string + example: "1" + + NoCache: + name: no_cache + in: query + required: false + description: Bypass cache and fetch fresh data + schema: + type: boolean + default: false + example: true + + schemas: + WelcomeMessage: + type: object + properties: + title: + type: string + example: "Welcome to the Home Access Center API!" + message: + type: string + example: "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + routes: + type: array + items: + type: string + example: ["/api/name", "/api/info", "/api/classes"] + cache_param: + type: string + example: "Add ?no_cache=true to any endpoint to bypass cache" + + ErrorResponse: + type: object + properties: + error: + type: string + description: Error message describing what went wrong + + responses: + Unauthorized: + description: Invalid username or password + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + error: "Invalid username or password" + + InternalServerError: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + error: "Failed to fetch data from Home Access Center" + + securitySchemes: + QueryAuth: + type: apiKey + in: query + name: user + description: Authentication via username and password query parameters diff --git a/src/handlers.rs b/src/handlers.rs index 57e5f2c..61b62cd 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -3,6 +3,7 @@ use axum::{ http::StatusCode, response::Json, response::IntoResponse, + http::header, }; use serde_json::json; use serde::Deserialize; @@ -186,7 +187,8 @@ macro_rules! endpoint { pub async fn root() -> impl IntoResponse { let message = json!({ "title": "Welcome to the Home Access Center API!", - "message": "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/", + "message": "OpenAPI documentation available at /openapi.yaml or /openapi.json", + "docs_url": "https://hac.packjack.dev/openapi.yaml", "routes": [ "/api/name", "/api/assignments", "/api/info", "/api/averages", "/api/weightings", "/api/classes", "/api/reportcard", "/api/ipr", "/api/transcript", "/api/rank" ], @@ -195,6 +197,24 @@ pub async fn root() -> impl IntoResponse { Json(message) } +pub async fn serve_openapi_yaml() -> impl IntoResponse { + let openapi_content = include_str!("../openapi.yaml"); + ( + StatusCode::OK, + [(header::CONTENT_TYPE, "application/x-yaml")], + openapi_content + ) +} + +pub async fn serve_openapi_json() -> impl IntoResponse { + let openapi_content = include_str!("../openapi.json"); + ( + StatusCode::OK, + [(header::CONTENT_TYPE, "application/json")], + openapi_content + ) +} + endpoint!( get_classes, assignments_page_scraper: extract_classes diff --git a/src/routes.rs b/src/routes.rs index edc6d43..f37ebdf 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,11 +1,13 @@ use axum::{routing::get, Router}; use crate::cache::Cache; -use crate::handlers::{root, get_averages, get_classes, get_info, get_name, get_assignments, get_gradebook, get_weightings, get_report_card, get_progress_report, get_transcript, get_rank}; +use crate::handlers::{root, get_averages, get_classes, get_info, get_name, get_assignments, get_gradebook, get_weightings, get_report_card, get_progress_report, get_transcript, get_rank, serve_openapi_yaml, serve_openapi_json}; pub fn create_router(cache: Cache) -> Router { Router::new() .route("/", get(root)) .route("/api/", get(root)) + .route("/openapi.yaml", get(serve_openapi_yaml)) + .route("/openapi.json", get(serve_openapi_json)) .route("/api/name", get(get_name)) .route("/api/info", get(get_info)) .route("/api/classes", get(get_classes)) From 4a7d823da29e3962f33df5a6f7ddc111593d28e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:36:05 +0000 Subject: [PATCH 3/7] Fix OpenAPI spec to reference correct documentation URLs Co-authored-by: Packjackisback <83289186+Packjackisback@users.noreply.github.com> --- openapi.json | 12 ++++++++++-- openapi.yaml | 10 ++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/openapi.json b/openapi.json index 26cea7a..d1d2a00 100644 --- a/openapi.json +++ b/openapi.json @@ -57,7 +57,11 @@ }, "message": { "type": "string", - "example": "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + "example": "OpenAPI documentation available at /openapi.yaml or /openapi.json" + }, + "docs_url": { + "type": "string", + "example": "https://hac.packjack.dev/openapi.yaml" }, "routes": { "type": "array", @@ -899,7 +903,11 @@ }, "message": { "type": "string", - "example": "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + "example": "OpenAPI documentation available at /openapi.yaml or /openapi.json" + }, + "docs_url": { + "type": "string", + "example": "https://hac.packjack.dev/openapi.yaml" }, "routes": { "type": "array", diff --git a/openapi.yaml b/openapi.yaml index 19d8e8c..0a331e9 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -60,7 +60,10 @@ paths: example: "Welcome to the Home Access Center API!" message: type: string - example: "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + example: "OpenAPI documentation available at /openapi.yaml or /openapi.json" + docs_url: + type: string + example: "https://hac.packjack.dev/openapi.yaml" routes: type: array items: @@ -542,7 +545,10 @@ components: example: "Welcome to the Home Access Center API!" message: type: string - example: "Visit the docs at https://homeaccesscenterapi-docs.vercel.app/" + example: "OpenAPI documentation available at /openapi.yaml or /openapi.json" + docs_url: + type: string + example: "https://hac.packjack.dev/openapi.yaml" routes: type: array items: From e45cda5cc59facd5e40c6dc5d2d35ea017e8415d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:38:16 +0000 Subject: [PATCH 4/7] Use standard YAML MIME type and remove unused security scheme Co-authored-by: Packjackisback <83289186+Packjackisback@users.noreply.github.com> --- openapi.json | 8 -------- openapi.yaml | 7 ------- src/handlers.rs | 2 +- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/openapi.json b/openapi.json index d1d2a00..8de0f5a 100644 --- a/openapi.json +++ b/openapi.json @@ -963,14 +963,6 @@ } } } - }, - "securitySchemes": { - "QueryAuth": { - "type": "apiKey", - "in": "query", - "name": "user", - "description": "Authentication via username and password query parameters" - } } } } diff --git a/openapi.yaml b/openapi.yaml index 0a331e9..33e43d2 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -583,10 +583,3 @@ components: $ref: '#/components/schemas/ErrorResponse' example: error: "Failed to fetch data from Home Access Center" - - securitySchemes: - QueryAuth: - type: apiKey - in: query - name: user - description: Authentication via username and password query parameters diff --git a/src/handlers.rs b/src/handlers.rs index 61b62cd..c7f234f 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -201,7 +201,7 @@ pub async fn serve_openapi_yaml() -> impl IntoResponse { let openapi_content = include_str!("../openapi.yaml"); ( StatusCode::OK, - [(header::CONTENT_TYPE, "application/x-yaml")], + [(header::CONTENT_TYPE, "application/yaml")], openapi_content ) } From baa7ed8bafc4d938ac3eb46ca5d83076d8d03427 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 01:28:20 +0000 Subject: [PATCH 5/7] Add hosted Swagger UI documentation at /docs endpoint Co-authored-by: Packjackisback <83289186+Packjackisback@users.noreply.github.com> --- OPENAPI.md | 22 +++++++++-- openapi.json | 98 +++++++++++++++++++++++++++++++++++++++++++++++-- openapi.yaml | 69 ++++++++++++++++++++++++++++++++-- src/handlers.rs | 55 ++++++++++++++++++++++++++- src/routes.rs | 3 +- 5 files changed, 232 insertions(+), 15 deletions(-) diff --git a/OPENAPI.md b/OPENAPI.md index d28b164..dc127ee 100644 --- a/OPENAPI.md +++ b/OPENAPI.md @@ -7,11 +7,25 @@ This directory contains the OpenAPI 3.0 specification for the Home Access Center - **openapi.yaml** - OpenAPI specification in YAML format (recommended for editing) - **openapi.json** - OpenAPI specification in JSON format (auto-generated from YAML) -## Usage +## Viewing the Documentation -### Viewing the Documentation +### Interactive Documentation (Recommended) -The OpenAPI documentation is served directly by the API at: +Visit the hosted Swagger UI interface: +``` +https://hac.packjack.dev/docs +``` + +This provides: +- Interactive API explorer with "Try it out" functionality +- Complete parameter descriptions and examples +- Response schemas and examples +- No downloads or setup required +- Search and filter capabilities + +### Raw Specification Files + +The OpenAPI specification files are also available: - YAML: https://hac.packjack.dev/openapi.yaml - JSON: https://hac.packjack.dev/openapi.json @@ -19,7 +33,7 @@ The OpenAPI documentation is served directly by the API at: You can use these specification files with various OpenAPI tools: -#### Swagger UI +#### Swagger Editor Visit https://editor.swagger.io/ and import the URL: ``` https://hac.packjack.dev/openapi.yaml diff --git a/openapi.json b/openapi.json index 8de0f5a..a4bfcc4 100644 --- a/openapi.json +++ b/openapi.json @@ -57,11 +57,24 @@ }, "message": { "type": "string", - "example": "OpenAPI documentation available at /openapi.yaml or /openapi.json" + "example": "Interactive API documentation available at /docs" }, "docs_url": { "type": "string", - "example": "https://hac.packjack.dev/openapi.yaml" + "example": "https://hac.packjack.dev/docs" + }, + "openapi_spec": { + "type": "object", + "properties": { + "yaml": { + "type": "string", + "example": "https://hac.packjack.dev/openapi.yaml" + }, + "json": { + "type": "string", + "example": "https://hac.packjack.dev/openapi.json" + } + } }, "routes": { "type": "array", @@ -107,6 +120,70 @@ } } }, + "/docs": { + "get": { + "summary": "Interactive API Documentation", + "description": "Serves an interactive Swagger UI interface for exploring and testing the API", + "tags": [ + "Student Info" + ], + "responses": { + "200": { + "description": "HTML page with Swagger UI", + "content": { + "text/html": { + "schema": { + "type": "string", + "example": "..." + } + } + } + } + } + } + }, + "/openapi.yaml": { + "get": { + "summary": "OpenAPI Specification (YAML)", + "description": "Returns the OpenAPI 3.0 specification in YAML format", + "tags": [ + "Student Info" + ], + "responses": { + "200": { + "description": "OpenAPI specification in YAML format", + "content": { + "application/yaml": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/openapi.json": { + "get": { + "summary": "OpenAPI Specification (JSON)", + "description": "Returns the OpenAPI 3.0 specification in JSON format", + "tags": [ + "Student Info" + ], + "responses": { + "200": { + "description": "OpenAPI specification in JSON format", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/api/name": { "get": { "summary": "Get Student Name", @@ -903,11 +980,24 @@ }, "message": { "type": "string", - "example": "OpenAPI documentation available at /openapi.yaml or /openapi.json" + "example": "Interactive API documentation available at /docs" }, "docs_url": { "type": "string", - "example": "https://hac.packjack.dev/openapi.yaml" + "example": "https://hac.packjack.dev/docs" + }, + "openapi_spec": { + "type": "object", + "properties": { + "yaml": { + "type": "string", + "example": "https://hac.packjack.dev/openapi.yaml" + }, + "json": { + "type": "string", + "example": "https://hac.packjack.dev/openapi.json" + } + } }, "routes": { "type": "array", diff --git a/openapi.yaml b/openapi.yaml index 33e43d2..5610a33 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -60,10 +60,19 @@ paths: example: "Welcome to the Home Access Center API!" message: type: string - example: "OpenAPI documentation available at /openapi.yaml or /openapi.json" + example: "Interactive API documentation available at /docs" docs_url: type: string - example: "https://hac.packjack.dev/openapi.yaml" + example: "https://hac.packjack.dev/docs" + openapi_spec: + type: object + properties: + yaml: + type: string + example: "https://hac.packjack.dev/openapi.yaml" + json: + type: string + example: "https://hac.packjack.dev/openapi.json" routes: type: array items: @@ -87,6 +96,49 @@ paths: schema: $ref: '#/components/schemas/WelcomeMessage' + /docs: + get: + summary: Interactive API Documentation + description: Serves an interactive Swagger UI interface for exploring and testing the API + tags: + - Student Info + responses: + '200': + description: HTML page with Swagger UI + content: + text/html: + schema: + type: string + example: "..." + + /openapi.yaml: + get: + summary: OpenAPI Specification (YAML) + description: Returns the OpenAPI 3.0 specification in YAML format + tags: + - Student Info + responses: + '200': + description: OpenAPI specification in YAML format + content: + application/yaml: + schema: + type: string + + /openapi.json: + get: + summary: OpenAPI Specification (JSON) + description: Returns the OpenAPI 3.0 specification in JSON format + tags: + - Student Info + responses: + '200': + description: OpenAPI specification in JSON format + content: + application/json: + schema: + type: object + /api/name: get: summary: Get Student Name @@ -545,10 +597,19 @@ components: example: "Welcome to the Home Access Center API!" message: type: string - example: "OpenAPI documentation available at /openapi.yaml or /openapi.json" + example: "Interactive API documentation available at /docs" docs_url: type: string - example: "https://hac.packjack.dev/openapi.yaml" + example: "https://hac.packjack.dev/docs" + openapi_spec: + type: object + properties: + yaml: + type: string + example: "https://hac.packjack.dev/openapi.yaml" + json: + type: string + example: "https://hac.packjack.dev/openapi.json" routes: type: array items: diff --git a/src/handlers.rs b/src/handlers.rs index c7f234f..1e426fc 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -187,8 +187,12 @@ macro_rules! endpoint { pub async fn root() -> impl IntoResponse { let message = json!({ "title": "Welcome to the Home Access Center API!", - "message": "OpenAPI documentation available at /openapi.yaml or /openapi.json", - "docs_url": "https://hac.packjack.dev/openapi.yaml", + "message": "Interactive API documentation available at /docs", + "docs_url": "https://hac.packjack.dev/docs", + "openapi_spec": { + "yaml": "https://hac.packjack.dev/openapi.yaml", + "json": "https://hac.packjack.dev/openapi.json" + }, "routes": [ "/api/name", "/api/assignments", "/api/info", "/api/averages", "/api/weightings", "/api/classes", "/api/reportcard", "/api/ipr", "/api/transcript", "/api/rank" ], @@ -215,6 +219,53 @@ pub async fn serve_openapi_json() -> impl IntoResponse { ) } +pub async fn serve_docs() -> impl IntoResponse { + let html = r#" + + +
+ + +