HOMEWORK 2 - #2
Open
borysenko-oleksandr wants to merge 1 commit into
Open
Conversation
Alexey-Popov
suggested changes
May 12, 2026
Alexey-Popov
left a comment
There was a problem hiding this comment.
Please structure PR properly, following the requirements (read them carefully)
HW not approved
Owner
Author
|
@Alexey-Popov description was updated |
Alexey-Popov
approved these changes
May 15, 2026
Alexey-Popov
left a comment
There was a problem hiding this comment.
Thanks for the updates.
HW approved
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issues:
✅ Summary
Intelligent Customer Support System — complete REST API for ticket management with multi-format import (CSV/JSON/XML), automatic issue categorization & priority assignment, and comprehensive test suite (65 tests, 92.3% line coverage, 88.41% statements). Built with Node.js + Express, in-memory store, Jest testing.
Endpoints
POST/ticketsGET/ticketsGET/tickets/:idPUT/tickets/:idresolved_aton status change)DELETE/tickets/:idPOST/tickets/importPOST/tickets/:id/auto-classifyWhat's inside
Task 1 — Multi-Format Ticket Import API
Implemented 7 REST endpoints in src/routes/tickets.js for complete ticket lifecycle management:
{total, successful, failed, errors[]}failed[]array without aborting the batch)Task 2 — Auto-Classification
Implemented intelligent ticket categorization & priority assignment (src/services/classifier.js):
account_access|technical_issue|billing_question|feature_request|bug_report|otherurgent|high|medium|low?auto_classify=trueon creation, orPOST /tickets/:id/auto-classifyanytimeTask 3 — AI-Generated Test Suite
65 comprehensive tests across 8 files covering all layers:
Overall coverage: 92.3% lines, 88.41% statements (exceeds 85% threshold)
Task 4 — Multi-Level Documentation
4 docs + 1 README with 5 Mermaid diagrams for different audiences:
Project structure
🛠️ AI Tools Used
Tool: GitHub Copilot CLI (
claude-sonnet-4.6) via the VS Code extensionWorkflow
[[PLAN]]mode to analyzeTASKS.mdand produce a step-by-step plan; reviewed structure and asked clarifying questions (tech stack, storage, test framework, port) before writing a single line of codenpm testafter each phase to catch regressions; manually verified endpoint shapes by inspecting route handler return values against the specTASKS.mdrequirements (HTTP status codes, bulk import error handling,resolved_atauto-set, UUID server-side generation)Prompts used (representative sample)
What I verified myself
app.js+server.jssplit was the right pattern — Supertest requiresappwithout a bound port, so the listen call lives only inserver.jsforloop intickets.jscollects errors per row and always returns{total, successful, failed}resolved_atis auto-set only onresolvedtransition (notclosed) by readingstore.jsdirectly1. Import route shadowed by parameterized route
Problem: In Express,
POST /tickets/importwas being matched by the/:idhandler because it was registered afterrouter.post('/:id/auto-classify', ...). The literal segmentimportwas treated as an:idvalue, causing the handler to look up a ticket with id"import"and return 404.Solution: Moved the
/importroute registration to before any/:idroutes intickets.jsso Express matches the static segment first.2. XML
tagsunwrapping withxml2jsProblem:
xml2jswithexplicitArray: falsereturnstagsas{ tag: "value" }for a single child or{ tag: ["a", "b"] }for multiple children — not a plain array. The ticket model expectsstring[].Solution: Added explicit unwrapping in
importXml()— checks for the.tagsub-property, normalises single values to[value], falls back to|-split for legacy string encoding.3. CSV flat format vs. nested
metadataobjectProblem: CSV is inherently flat;
csv-parsereadsmetadataas a raw JSON string andtagsas a pipe-delimited string. The model validator rejects a string where an object is expected.Solution: Added post-parse deserialization in
importCsv()— attemptsJSON.parseonmetadata, falls back to{}on failure; splitstagson|ifJSON.parsefails.📸 Screenshots
AI tool interaction — planning & implementation
1 · Initial Copilot question asking about tech stack, storage, and test framework
Copilot asking setup questions before scaffolding

2 · Jest coverage report — 92.3% line coverage across all source files
Jest coverage report showing 92.3% line coverage

Live demo results
POST /tickets?auto_classify=true — create ticket with automatic classification
{ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "customer_id": "cust-001", "customer_email": "alice@example.com", "customer_name": "Alice Smith", "subject": "Can't access account — login blocked", "description": "I keep getting a security error when trying to login with my password.", "category": "account_access", "priority": "urgent", "status": "new", "created_at": "2026-05-10T18:00:00.000Z", "updated_at": "2026-05-10T18:00:00.000Z", "resolved_at": null, "assigned_to": null, "tags": [], "metadata": { "source": "api", "browser": "", "device_type": "desktop" }, "confidence": 0.95, "reasoning": "Matched keywords: [can't access, security, login, password]. Assigned priority=\"urgent\", category=\"account_access\".", "keywords": ["can't access", "security", "login", "password"] }GET /tickets?category=account_access&priority=urgent — filtered list
[ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "subject": "Can't access account — login blocked", "category": "account_access", "priority": "urgent", "status": "new" } ]POST /tickets/import (CSV, 50 rows, 2 invalid) — bulk import summary
{ "total": 50, "successful": 48, "failed": [ { "row": 3, "errors": [{ "field": "customer_email", "message": "customer_email must be a valid email address" }] }, { "row": 17, "errors": [{ "field": "description", "message": "description is required and must be 10-2000 characters" }] } ] }400 Validation error on POST /tickets
{ "errors": [ { "field": "customer_email", "message": "customer_email must be a valid email address" }, { "field": "subject", "message": "subject is required and must be 1-200 characters" } ] }🔗 Related