Skip to content

Conversation

ronjalh
Copy link
Member

@ronjalh ronjalh commented Oct 17, 2026 •

Summary

A logged-in coach can keep gymnasts, their lists of elements and their routines through the API. Closes #73.

Stacked. The base is 70-session-tokens (#82, the session), which is on #78 (codegen), #77 (calculator) and #65 (server). This branch also contains a merge of the branch of #49 (the schema fields it needs), so until #49 is merged into dev the diff of this PR includes #49's changes. Change the base to dev when the others are merged. It does not depend on the Google login (#71, draft): it only needs to know who is asking, which the access token from #82 gives.

What's in it

  • Ownership: every operation needs a login and only reaches the coach's own data. Another coach's gymnast or routine gives NOT_FOUND with the same message as an unknown id, and nothing changes (tested for all ten operations).
  • Gymnasts: create, rename, delete (the routines and the list go with it). Names are trimmed and 1–70 characters (an emoji counts as one). At most 200 per coach.
  • The gymnast's list: adding an item that is already there, or removing one that is not, changes nothing and gives no error. Unknown item: NOT_FOUND. Removed item: cannot be added (INVALID_ITEMS), can be taken out. At most 100.
  • Routines: create, rename, delete, NAME_TAKEN per gymnast and apparatus, at most 50 per gymnast.
  • setRoutineItems: replaces the list in one transaction and sets updatedAt. Items must be on the routine's apparatus, at most one vault on vault, a removed item may stay but not be added. The first statement of the transaction updates the routine row, which locks it, so two saves at the same moment follow each other (last one wins, tested). A failed save changes nothing.
  • Routine.dScore: the calculator's function; null while the routine has a removed item.
  • Loaders for routines, lists, items, gymnast and apparatus, cleared by the mutations. A coach with 25 gymnasts costs the same number of queries as one with 2 (tested).
  • New error classes NOT_FOUND, NAME_TAKEN, LIMIT_REACHED. A DateTime scalar (ISO 8601). The login resolvers moved to resolvers/auth.ts.
  • Docs: "The user data" in docs/API.md replaces "What is not built yet"; README and docs/AI_USAGE.md updated.

Things to know

  • The limits are checked before the row is written, so several requests at the same moment can go over a limit by the number of requests. The unique name is guarded by the database. This is written in docs/API.md.
  • The query count is 10 for the overview (coach, gymnasts, routines, lists, items and their elements/vaults, apparatus), because Prisma sends one query for each include.

Testing

  • Lint, tsc --noEmit and 192 tests pass against a database (0 skipped); 40 are new (userData.test.ts, with two test coaches that are deleted afterwards, and one element that is marked removed and restored).

🤖 Generated with Claude Code

Ronja added 9 commits October 15, 2026 14:48
…he GraphQL schema

Gymnast.items with addGymnastItem and removeGymnastItem, Routine.createdAt and updatedAt, and CatalogItem.removed. API.md describes the rules, errors and limits.
…d items do not count

addGymnastItem throws ALREADY_EXISTS and removeGymnastItem throws NOT_FOUND when nothing would change. A gymnast's list is limited to 100 items. Removed items do not count in the D-score, and calculateDScore rejects them. API.md describes the warning in the UI, the seed report and the other error codes.
Names are trimmed and checked in the backend, BAD_USER_INPUT otherwise.
Resolves the conflicts with the search and schema changes in dev. dev's names are kept (Gymnast.elements, addGymnastElement, removeGymnastElement, difficultyValues, no difficultyLetter, duplicates in a routine). The decisions for #37 are kept: errors for a duplicate add and a missing remove, a limit of 100, names of 70 characters, and removed items do not count.
Keeps both AI_USAGE sections. Puts the one-line descriptions in schema.graphql back as they are in dev: an earlier Prettier run had turned all of them into blocks.
Vault.dValue, CatalogFilter.difficultyValues, createGymnast, renameGymnast
and createRoutine had ended up inside doc comments and were missing from
the schema. Also restores the searchCatalog search description from dev
(80 characters, 8 words, text without searchable words finds nothing).
Adding an item that is already in a gymnast's list, or removing one that
is not, now changes nothing and returns the gymnast, so ALREADY_EXISTS is
gone and NOT_FOUND is only for unknown ids. Routine.dScore is null while
the routine has a removed item, the same rule as calculateDScore.
calculateDScore keeps taking only item ids.
Only the layout of the doc comments changes. printSchema gives the same
result before and after.
@ronjalh ronjalh requested a review from a team as a code owner October 17, 2026 08:26
@ronjalh ronjalh self-assigned this Oct 17, 2026
@ronjalh ronjalh marked this pull request as draft October 17, 2026 09:17
@ronjalh ronjalh force-pushed the 74-graphql-codegen branch from 41053dc to bcd34bd Compare October 17, 2026 09:24
@ronjalh ronjalh force-pushed the 73-user-data-resolvers branch from 7041f84 to 127735f Compare October 17, 2026 09:30
@ronjalh ronjalh changed the base branch from 74-graphql-codegen to 70-session-tokens October 17, 2026 09:30
Ronja added 2 commits October 17, 2026 11:35
A logged-in coach can create, rename and delete gymnasts and routines,
change a gymnast's list one item at a time, and replace the items of a
routine. Every operation only reaches the coach's own data (NOT_FOUND
for anything else). Names are 1-70 characters, the lists have limits,
NAME_TAKEN, INVALID_ITEMS and LIMIT_REACHED are returned in the right
cases, and Routine.dScore is null while the routine has a removed item.
The loaders keep the number of queries the same for any number of gymnasts.
@ronjalh ronjalh force-pushed the 73-user-data-resolvers branch from 127735f to 6392b73 Compare October 17, 2026 09:35
@ronjalh ronjalh marked this pull request as ready for review October 17, 2026 09:37
Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant