Show when PESU Academy last responded in /health
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 62/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Ambito
- api, authentication, backend, observability, testing
Direzione di ricerca
After #168 merges, trace the authentication flow through app/app.py and app/pesu.py, including _upstream_call and its tests in tests/unit/test_metrics_instrumentation.py. Add the upstream timestamps to the health model, route, docs, and README, then run the focused tests and uv run python scripts/run_tests.py. Done means all listed status cases pass, /metrics is unchanged, and /health shows IST timestamps or null.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
🔄 Updated for v5.0.0 (2026-10-08). Rewritten for 5.0.0. pesu-auth now signs in through PESU Academy's mobile API (#241): a login is at most two upstream calls (
login, thenprofile_fetch), with no CSRF token, no background refresh and no redirects followed. So nothing calls PESU at startup any more, and both timestamps start asnulluntil the first login. The walkthrough, the success/error rules and the tests are updated to match. The goal and the rules for what/healthmay show are unchanged.
🌱 This is a beginner-friendly issue, and a step up from #168. It isn't the easiest issue in the repo, and that's deliberate. Every contributor to pesu-auth should understand how a login actually flows through the code: how the login request reaches PESU Academy's mobile API, how the profile is fetched with the token it returns, and how each of those calls is timed and recorded. The best way to learn that is to change it. This issue walks you through the real authentication pipeline one step at a time. Ask questions in the comments at any point.
⚠️ Who can take this issue
- PES University students only. This change sits in the login path and has to be tested end to end against PESU Academy with real PESU credentials, which only PES University students have.
- Wait until you're assigned. Comment to ask for the issue, then wait for a maintainer to assign it to you before writing any code. PRs for unassigned issues are closed without review.
- Do #168 first. This issue extends the
HealthModelthat #168 creates, so it can't start until #168 has merged.
The problem
pesu-auth has one dependency: PESU Academy. When a login fails, the first question is always "is pesu-auth broken, or is PESU Academy down?" Today nothing public can answer it:
/healthonly says that pesu-auth itself is running./metricscounts calls to PESU Academy, but those are totals since startup. They can't tell you when PESU last answered, and/metricsmay need a token.
What you'll build
Add an upstream object to the /health response ("upstream" is the usual name for a service that we depend on):
"upstream": {
"lastSuccessAt": "2024-07-28T22:29:51.004211+05:30",
"lastErrorAt": "2024-07-28T21:02:13.772930+05:30"
}
| Field | What it means |
|---|---|
lastSuccessAt |
The last time PESU Academy answered one of our requests normally. null if it never has yet. |
lastErrorAt |
The last time PESU Academy couldn't be reached, or refused or failed one of our requests. null if that hasn't happened yet. |
A reader compares the two. If lastErrorAt is recent and lastSuccessAt is old, PESU Academy is probably down.
Rules the solution must follow
- Record, never probe. The timestamps are recorded as a side effect of calls pesu-auth already makes.
/healthmust never call PESU Academy itself: it has to stay fast, and it mustn't fail just because PESU is slow. - Timestamps only. Never store usernames, error messages, status codes or exception text here.
/healthis public and unauthenticated. - It doesn't change health.
statusstaystrueand the HTTP code stays 200. If PESU Academy were down and/healthreturned an error, Render could restart pesu-auth, and restarting us doesn't fix PESU. - Timestamps, not a yes/no. During quiet hours there may be no logins for a while, so the values can be old, and after a restart both are
nulluntil the first login. A timestamp lets the reader see how old; a boolean would claim more than we know.
Step-by-step guide
Step 1: Follow a login through the code
Before changing anything, trace one /authenticate request from start to finish. Open these and read them in this order:
app/app.py, theauthenticateroute. It validates the request and callspesu_academy.authenticate(...).app/pesu.py,PESUAcademy.authenticate. It creates onehttpx2.AsyncClientfor this login only, and closes it infinally, however the login ends._login. The username and password are POSTed toLOGIN_URL, PESU Academy's mobile login endpoint. A401means the credentials were wrong; any other non-200, or a response that doesn't saySUCCESS, is PESU failing (UpstreamError, a 502). A good response carries an access token._fetch_profile, only if the caller asked for the profile. It POSTsPROFILE_FORMtoDISPATCHER_URLwith that access token. PESU can answer with an HTTP 200 that is really an error (its "error envelope", for example{"status": 403, "message": "Access Denied"}for a non-student account); that becomesProfileFetchError.lifespaninapp/app.py. Notice what isn't there: since 5.0.0, pesu-auth makes no call to PESU at startup or in the background.
Write down every place where pesu-auth talks to PESU Academy. You should find exactly two places in the code, and each is wrapped in async with _upstream_call(...). One place is always one HTTP request: the client doesn't follow redirects, so a redirect comes back as a 3xx response.
Step 2: Understand _upstream_call
_upstream_call, near the top of app/pesu.py, is the single gateway for every request to PESU Academy. Read it and its comments carefully. It:
- times each call;
- labels its outcome as
success,errororcancelled; - records all of that as metrics, in a
finallyblock, so it runs however the call ends.
Then read tests/unit/test_metrics_instrumentation.py, which tests it. Pay attention to test_a_failed_profile_status_is_still_a_successful_call: a 500 response from PESU is recorded with the outcome success. That's because _upstream_call treats "no exception was raised" as success, and the status code is checked later, by the caller. Keep this in mind for Step 3.
Step 3: Decide what counts as success and error
Every call through _upstream_call updates one of the timestamps, except cancelled calls (see the rules below). There are two kinds:
| Operation | When it happens |
|---|---|
login |
Every /authenticate request that passes validation |
profile_fetch |
Every /authenticate that asks for the profile, once the login has succeeded and returned an access token |
For these fields, use these rules. They are not the same as the outcome label that already exists:
- Success: PESU Academy sent a response with a status below 500, except
403and429.- A wrong password counts as success. PESU answered normally (a
401); the student just typed the wrong password. The fields answer "is PESU Academy reachable?", not "are logins working?". - So does an HTTP 200 carrying PESU's error envelope: PESU answered, it just declined that request.
- A wrong password counts as success. PESU answered normally (a
- Error: any of these:
- an exception (a timeout or connection failure);
- a response with a status of 500 or above;
- a
403 Forbiddenor429 Too Many RequestsHTTP status. PESU did answer, but it's refusing to serve us (blocked or rate limited), so for our purposes it's unreachable.
- Ignored:
cancelled. It means we stopped waiting (a client disconnected, or the app is shutting down), not that PESU failed. The existing comment in_upstream_callexplains this.
Step 4: Record the timestamps
- Create a small class to hold the two timestamps, for example an
UpstreamStatuswithlast_success_atandlast_error_at(both starting asNone) and a method to record each.PESUAcademy.__init__creates it and keeps it in an attribute, next toself._metrics. - Pass it to
_upstream_callthe same waymetricsis passed, and update both call sites. - Record inside
_upstream_call, using the rules from Step 3. Thefinallyblock already knows the outcome and the response. Make sure a cancelled call records nothing. - Don't store these in
MetricsCollector. They aren't metrics, and/metricsmust not change.
Two things to watch for:
- Timezones.
/healthshows times in IST. Don't importISTfromapp/app.pyintoapp/pesu.py: that would create a circular import. (app/pesu.pyhas its ownIST, used for dates of birth; #230 plans to merge the two.) Store timezone-aware UTC times (datetime.now(UTC)), and convert them with.astimezone(IST)in the/healthroute. - Concurrency. Many requests run at the same time, but they all share one event loop, and assigning a single attribute can't be interrupted part-way through. So no lock is needed. Say so in a comment.
Step 5: Show it in /health
- Add an
UpstreamModelsub-model toapp/models/health.py, following the style #168 used. Both fields are optional timestamps, and each field's description should say when it isnull. - Read the values in the
/healthroute inapp/app.py, and expose them through a small public property or method onPESUAcademy, not by reaching into a private attribute. - Update the example in
app/docs/health.pyand the/healthtable inREADME.md.
Step 6: See it work
Start the app (uv run python -m app.app --debug) and open http://localhost:5000/health.
-
Right after startup, both values are
null: nothing has called PESU yet. -
Log in with a wrong password from Swagger at http://localhost:5000/.
lastSuccessAtis set, because PESU answered. -
Log in with your real credentials, with
profile: true. It moves forward again (twice: the login and the profile call). -
Turn off your internet and try to log in. This time
lastErrorAtgets set. Swagger's page loads its scripts from the internet, so it won't work while you're offline. Send the request from a terminal instead:curl -X POST http://localhost:5000/authenticate \ -H "Content-Type: application/json" \ -d '{"username": "test", "password": "test"}'
Step 7: Write tests
Add your tests next to the existing ones in tests/unit/test_metrics_instrumentation.py, or in a new tests/unit/test_upstream_status.py, using the same pesu, upstream and make_response fixtures. Cover:
- both values start as
None; - a successful call sets
lastSuccessAtonly; - a 500 response sets
lastErrorAtonly; - a
403and a429each setlastErrorAtonly, while another4xx(for example404) setslastSuccessAt; - an HTTP 200 error envelope on the profile call sets
lastSuccessAt; - a timeout or connection error sets
lastErrorAtonly; - a cancelled call changes neither;
- a wrong-password login sets
lastSuccessAt; - both operations record;
/healthreturns the values in IST, andnullwhen they're unset;- the existing
/metricstests still pass unchanged.
Then run the whole suite with your PESU credentials in .env (see "Tests that need credentials" in CONTRIBUTING.md):
uv run python scripts/run_tests.py
Coverage must stay at 100%, lines and branches, and the live tests must pass. Paste the summary line into your PR. A PR that hasn't run the live tests will not be reviewed.
Step 8: Open your PR
- Bump the minor version in
pyproject.toml, then runuv lock. - Run
uv run pre-commit run --all-files. - Open the PR from your fork, against
dev, with a Conventional Commit title such asfeat: show when PESU Academy last responded in /health, and writeCloses #170in the description.
Done when
-
/healthreturnsupstream.lastSuccessAtandupstream.lastErrorAtas described - They're recorded only in
_upstream_call, following the rules in Step 3, for both operations - Nothing about a user or an error ends up in them
-
/metricsoutput and its tests are unchanged - Swagger and the README show the new object
- Every case in Step 7 is tested, coverage is 100%, and the live tests pass
- Pre-commit passes, the version is bumped, and the PR is from your fork against
dev
Want to go deeper?
Run git submodule update --init, then read these in .agents/pesudev-skills/auth/docs/:
upstream.md: how pesu-auth talks to PESU Academy.architecture.md: the client lifecycle.security.md: what may and may not be exposed.
They are being updated for 5.0.0; where they still describe the CSRF flow, the code in app/pesu.py wins. The repo's checklist for changing this part of the code is .agents/skills/change-upstream-client/SKILL.md. Reading it before you open your PR is a good way to catch mistakes.
- Lingua principale
- Python
- Stelle
- 22
- Fork
- 29
- Merge medio
- 22h 1m
- PR unite (30g)
- 20
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di pesu-dev/auth
-
Check the README for dead links and fix the misleading Docker Hub badgeForse di nuovo libera @knownshah l’ha presa 16 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
pesu-dev/auth#220 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Run the same checks locally and in CI: pre-commit hooks in CI, and hooks that match CIForse già presa @achyu-dev l’ha presa oggi. Apertabug tests and ci/cd
pesu-dev/auth#250 · 2 assegnatari ·
I maintainer di solito rispondono entro 1 giorno
-
bug documentation enhancement
Difficoltà 3/5 1-2 giorni Idoneità per principianti 54/100
I maintainer di solito rispondono entro 1 giorno
-
Commit SHA should be used instead of tag git based deployForse già presa @aditeyabaral l’ha presa 10 giorni fa. Apertabug
pesu-dev/auth#236 · 2 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
Private Functions in `app.py` needs to be in separate fileForse già presa @aditeyabaral l’ha presa 10 giorni fa. Apertaapi enhancement
pesu-dev/auth#235 · 1 commento · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di pesu-dev/auth
Issue simili
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
-
[Bug]: Importing weave changes the process-wide localeForse già presa @himanshu748 l’ha presa oggi. Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
-
UX
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
mediajunkie/piper-morgan-product#2008 · 2 commenti ·