Skip to content

feat: add subscription groups server contract - #76

Open
kapdon wants to merge 2 commits into
TypeType-Video:devfrom
kapdon:feat/subscription-groups-server
Open

feat: add subscription groups server contract#76
kapdon wants to merge 2 commits into
TypeType-Video:devfrom
kapdon:feat/subscription-groups-server

Conversation

@kapdon

@kapdon kapdon commented Aug 7, 2026

Copy link
Copy Markdown

Summary

Adds the backend contract for named subscription groups as the first Server-only part of TypeType-Video/TypeType#172.

  • creates, renames, lists, and deletes account-scoped groups;
  • supports many-to-many membership for existing subscribed channels;
  • filters GET /subscriptions and GET /subscriptions/feed by groupId or ungrouped=true;
  • preserves the existing global subscription list/feed when no filter is supplied;
  • keeps filtered feeds on the existing global snapshot while binding cursors to their selected view;
  • clears memberships safely when a subscription or replacement import/restore removes the underlying channel.

API changes

  • GET/POST /subscriptions/groups
  • PUT/DELETE /subscriptions/groups/{groupId}
  • PUT/DELETE /subscriptions/groups/{groupId}/channels
  • GET /subscriptions?groupId=...
  • GET /subscriptions?ungrouped=true
  • GET /subscriptions/feed?groupId=...
  • GET /subscriptions/feed?ungrouped=true

The handwritten OpenAPI contract now also documents the pre-existing /subscriptions operations.

Data and isolation

Adds subscription_groups and subscription_group_memberships. Every group and membership lookup is scoped to the authenticated account. Group names are trimmed, limited to 100 characters, and case-insensitively unique per account. Membership creation requires both the group and subscription to belong to that account.

Verification

Using the required JDK 25 toolchain:

./gradlew check shadowJar
BUILD SUCCESSFUL
998 tests, 0 failures, 0 errors, 0 skipped

This includes OpenAPI validation, coverage verification, focused route/service regressions, cursor/filter isolation, and the shadow JAR build.

Component follow-up

A separate TypeType-Frontend PR is required to add group management, channel assignment, and filtering UI. No Token, Downloader, or Player change is required.

Introduce account-scoped named groups without changing the existing flat
subscription record or global feed contract. Filtered feeds project from one
shared snapshot and bind cursors to the selected group view, keeping refresh
and cache behavior focused while preserving ungrouped subscriptions.

Constraint: Start from dev and keep the first contribution Server-only
Constraint: Preserve the existing global feed and ungrouped subscriptions
Rejected: Per-group feed snapshots | adds cache invalidation and refresh fanout
Confidence: high
Scope-risk: moderate
Directive: Keep filtered cursors bound to their subscription selection
Tested: ./gradlew check shadowJar (997 tests, OpenAPI, coverage, fat JAR)
Not-tested: Live frontend integration, deferred to the follow-up Frontend PR
Prune only memberships whose channels disappear from a replacement import or
restore. This avoids orphaned rows without discarding assignments for channels
that remain subscribed.

Constraint: Existing backup formats do not carry subscription-group metadata
Rejected: Clear every membership during restore | loses still-valid assignments
Confidence: high
Scope-risk: narrow
Directive: Canonicalize restored URLs before pruning group memberships
Tested: ./gradlew check shadowJar (998 tests, OpenAPI, coverage, fat JAR)
Not-tested: Restoring group definitions into a different account

@Priveetee Priveetee left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hey, first thx a lot for taking the time to work on this!

I went through the database changes, routes, account isolation, feed filtering, restore behavior, OpenAPI and tests.

Honestly the base is really good. Groups are correctly isolated per account, one channel can be in multiple groups, the ungrouped feed is handled properly, and reusing the existing subscription feed snapshot makes sense.

I found a few things that need to be fixed before merge:

  • The important one is backups. Groups and their channel memberships are not exported right now. Restoring on a fresh instance would restore the subscriptions, but all the organization created by the user would be lost. Both the groups and their memberships need to be included in TypeType backups.

  • There is also an edge case with pagination. The cursor only remembers the group ID and filter, while the group membership is loaded again for every page. If someone edits the group between two pages, the next page can skip or repeat videos. The cursor needs to keep the same group membership snapshot for the full pagination session.

  • Small OpenAPI mismatch: subscribedAt is required in the subscription creation schema, but Server actually generates it and ignores the submitted value. It would be cleaner to have a separate request schema where this field is not required.

Could u also split the first commit please? It currently contains 1,174 insertions, and I try to keep commits below 290 insertions so it stays easy to understand later why each part changed. Separating the database/model work, group API, feed filtering, backup support and tests would make the history much easier to follow.

I ran the full validation: 998 tests passed, including OpenAPI validation, coverage and the production jar build. I also checked it together with the current Server changes and all 1,036 tests passed there.

So yeah, no need to rebuild everything from scratch at all. The base is good, it just needs these points fixed and I will gladly review it again :)

Thx again, and have a great day!

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.

3 participants