Skip to content

Self-service school onboarding endpoint#623

Merged
hardbyte merged 7 commits intomainfrom
feat/school-onboarding
Apr 3, 2026
Merged

Self-service school onboarding endpoint#623
hardbyte merged 7 commits intomainfrom
feat/school-onboarding

Conversation

@hardbyte
Copy link
Copy Markdown
Owner

@hardbyte hardbyte commented Apr 3, 2026

Summary

New POST /v1/onboarding/school endpoint for self-service school signup, replacing the Airtable form.

What it does

  1. Accepts authenticated user + school selection/creation data + contact info
  2. Creates new school (PENDING state) or selects existing school and sets to PENDING
  3. Promotes user from PUBLIC to SCHOOL_ADMIN
  4. Binds user as school administrator
  5. Stores contact info in school's info JSONB under onboarding key
  6. Creates an event visible in the admin UI for review

Admin approval flow

  • Schools go to PENDING state, visible in admin UI
  • Admin reviews and changes state to ACTIVE via existing school edit form
  • No auto-approval — all schools require manual review

Test plan

  • Unit tests pass
  • Integration test: create new school via endpoint
  • Integration test: select existing school via endpoint
  • Verify event appears in admin UI
  • Verify school state is PENDING after onboarding

hardbyte added 2 commits April 3, 2026 21:04
POST /v1/onboarding/school creates or selects a school, promotes the
authenticated user to SchoolAdmin, binds them, and sets the school to
PENDING for admin review. Creates an event visible in the admin UI.
Tests cover: new school creation, existing school selection,
active school rejection, missing fields, unauthenticated access,
event creation, and user type promotion to SchoolAdmin.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a26e2459e1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


subclass_table = type_table_map.get(user.type)
if subclass_table:
from sqlalchemy import text
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hoist text import before unconditional SQL statements

_promote_to_school_admin imports text only inside if subclass_table, but text(...) is used unconditionally for the UPDATE users and subsequent inserts. When user.type is not in type_table_map (for example UserAccountType.WRIVETED), that branch is skipped and this path raises UnboundLocalError, causing /onboarding/school to fail with a 500 instead of completing onboarding.

Useful? React with 👍 / 👎.

UserAccountType.PUBLIC: "public_readers",
UserAccountType.STUDENT: "students",
UserAccountType.EDUCATOR: "educators",
UserAccountType.PARENT: "parents",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude parent accounts from raw subclass deletion path

Mapping UserAccountType.PARENT into the raw delete flow causes _promote_to_school_admin to execute DELETE FROM parents WHERE id = :uid; however Reader.parent_id (app/models/reader.py) references parents.id without ondelete, so parents with existing child readers will hit a foreign key violation and the onboarding request fails with an IntegrityError/500. This makes promotion unreliable for valid parent accounts unless child links are handled first or parent onboarding is explicitly blocked.

Useful? React with 👍 / 👎.

hardbyte added 3 commits April 3, 2026 21:11
- Move sqlalchemy text import to top of function (P2 fix)
- Exclude Parent type from raw subclass deletion to avoid FK cascade
  failure on readers table. Parents get a 409 with contact-support message.
- Fix test to use fresh session for promotion verification
POST /v1/onboarding/family promotes PUBLIC user to PARENT and creates
child reader accounts. Tests cover: basic flow, type promotion,
and unauthenticated access.
@hardbyte hardbyte force-pushed the feat/school-onboarding branch from dfcc006 to b2855eb Compare April 3, 2026 08:18
hardbyte added 2 commits April 3, 2026 21:27
- Flow fixture: collects parent name, child name/age/reading level
  via conversational chat interface
- Internal handler for /v1/onboarding/family: creates child reader
  profiles from chatflow api_call actions
- Register flow in seed config
Critical:
- Block WRIVETED/EDUCATOR/PARENT types from promotion (allowlist approach)
- Check for existing admin when selecting existing school (prevent IDOR)

High:
- Cap children list at 10 in both endpoint and internal handler
- Validate/truncate all string inputs in internal handler

Medium:
- Use SchoolLocationInput model instead of raw dict for location
- Add country_code format validation (3-char constraint)
- Add Field constraints on all string/int inputs
- Sanitize event descriptions (no user-supplied data in description)
@hardbyte hardbyte merged commit 5de449b into main Apr 3, 2026
11 checks passed
@hardbyte hardbyte deleted the feat/school-onboarding branch April 3, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant