Skip to content

Add UserMediaList entity, migrations, and GraphQL mutations#91

Merged
vitorhugo-java merged 5 commits intodevfrom
75-the-heart-of-the-tracker
Mar 31, 2026
Merged

Add UserMediaList entity, migrations, and GraphQL mutations#91
vitorhugo-java merged 5 commits intodevfrom
75-the-heart-of-the-tracker

Conversation

@vitorhugo-java
Copy link
Copy Markdown
Owner

This pull request introduces several new features and improvements related to user media libraries, custom status management, and error handling in the API. The most significant changes include new controllers for managing user media lists and custom statuses, enhancements to error handling with new exception types and standardized codes, and the introduction of new models and enums for media categories and statuses.

API and Controller Additions:

  • Added UserMediaListController to handle querying, adding, updating, and removing media in a user's library, including privacy checks and support for various filters.
  • Added UserStatusController to allow users to create, update, delete, and list their custom media tracking statuses.

Error Handling Improvements:

  • Introduced new exception classes: AccessDeniedException, NotFoundException, and MediaAlreadyInLibraryException for more specific error reporting. [1] [2] [3]
  • Updated GenericExceptionResolver to map these exceptions to standardized error codes (3403 for access denied, 3404 for not found, 2005 for media already in library) and included them in the documentation and error mapping logic. [1] [2] [3] [4]

Model and Enum Enhancements:

  • Added CategoryType and StatusType enums for stronger typing of media categories and statuses. [1] [2]
  • Updated MediaCategoryModel to use the new CategoryType enum instead of a string for the category name. [1] [2]
  • Introduced MediaStatusModel entity for representing media production statuses, linked to MediaModel. [1] [2]
  • Added UserCustomStatusModel entity for user-defined tracking statuses.

Minor Adjustments:

  • Updated MovieAPIImpl and TvSeriesApiImpl to account for the new mediaStatus field in MediaModel. [1] [2]

These changes collectively enhance the flexibility and robustness of the user media library and status management features, while improving API error consistency and data modeling.


References:

Copilot AI and others added 5 commits March 28, 2026 18:39
* Initial plan

* Create UserMediaList entity, migration V20, and repository

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/89ca0c56-ce40-4c65-acba-470f45c07fc0

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Address review feedback: UUID v7 PK, remove media_type, add start/finish date, time_spent and note fields

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/f4eedc95-d767-47f2-9b4b-bdd6a2a7ebcc

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Update src/main/resources/db/migration/V20__create_user_media_list_table.sql

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/main/resources/db/migration/V20__create_user_media_list_table.sql

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add @table uniqueConstraints to UserMediaListModel mirroring DB unique key

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/ebac41f8-5141-4192-9a6b-7b4b159c7e68

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Add UserMediaListRepositoryTest @DataJpaTest and fix repository ID type to UUID

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/8bd217c1-398c-45a6-b66b-7e28bdd3e4b6

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>
Co-authored-by: Vitor Hugo <vitorhugoalvesferreira@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…diaCategoryModel name field (#87)

* Initial plan

* Add MediaStatus entity, CategoryType and StatusType enums, refactor MediaCategoryModel

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/351aff6c-9b13-4dca-aef3-e683b0ea4838

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
* Initial plan

* Implement GraphQL queries for MediaStatus

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/966cc80e-e90b-49a2-9d9a-b85e965a0d61

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

* Replace findAllMediaStatuses with findUserMediaLibrary query

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/84a154b0-59cd-4de9-a7f8-41a3a6ae9ce6

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

* Add statusId, genreId, mediaId, categoryName filters to findUserMediaLibrary

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/b6f2c08a-c3ac-49e3-a959-88a442a3d3fe

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

* Change MediaStatusModel.id to Integer, add V22 migration, fix Javadoc

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/bbdd08fd-8299-45d3-9df2-a15df46d6d4d

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Fix CAST enum comparison and trim string filters in controller

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/431e8586-ce41-497c-8ad3-9f342e15e524

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>
…nt (#89)

* Initial plan

* Implement GraphQL mutations addMediaToUserLibrary and removeMediaFromUserLibrary with tests

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/18f5c822-c6fb-49ea-9fe5-aae0c61b4a39

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Update src/main/java/com/espacogeek/geek/services/impl/UserLibraryServiceImpl.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Merge 75-the-heart-of-the-tracker: refactor to UserMediaListModel, add private_list, add mutations and privacy-aware userId support

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/05f2c046-5eb1-4e38-b149-5c9d578ba82c

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Update src/main/resources/graphql/mutation.graphqls

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add AccessDeniedException (3403) and NotFoundException (3404); update resolver, README, and tests

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/f1182947-602f-4761-9667-4bfd55668bab

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>
Co-authored-by: Vitor Hugo <vitorhugoalvesferreira@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
…Status CRUD, link custom statuses to media library entries, and add new UserMediaList fields (#90)

* Initial plan

* Implement GraphQL mutation for user media progress and custom status CRUD

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/ad983dcd-9d2c-4f14-b46f-713fd081da3b

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/main/java/com/espacogeek/geek/services/impl/UserCustomStatusServiceImpl.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: update Javadoc status example from WATCHING to IN_PROGRESS

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/26a2018d-bdf3-431b-a7f1-8a7807dfebba

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

* fix: use InputValidationException for validation errors; validate custom status name; atomic delete

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/5e4e945f-eb96-4619-8347-1da8d030d410

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>

* feat: add custom status list relationship to UserMediaList and update userMediaProgress

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/95adaabd-b150-4134-a469-96ce8b907866

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Rename mutations, add StatusType enum, add UserMediaList fields

- Rename userMediaProgress → upsertUserMedia in controller, service, schema, and tests
- Rename UserStatus CRUD mutations/query → UserCustomStatus variants
- Change UpdateUserMediaInput.status from String to StatusType enum
  - Create StatusType.graphqls enum definition
  - Remove validateStatus() (GraphQL schema now enforces enum validity)
  - Use .name() instead of .toUpperCase() in applyUpdates()
- Add rewatchCount, isPrivate, personalNotes to UserMediaListModel
  - V26 migration to add columns to user_media_list
  - Add fields to UserMediaList.graphqls type and UpdateUserMediaInput
  - Handle new fields in UserMediaListServiceImpl.applyUpdates()
- Update tests: unquote enum values, add new-fields test, replace invalid-status test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Fix personalNotes column type: use VARCHAR(2000) to match model length constraint

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>

* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/main/resources/graphql/entity/UserMediaList.graphqls

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>
Co-authored-by: Vitor Hugo <vitorhugoalvesferreira@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
@vitorhugo-java vitorhugo-java requested a review from Copilot March 31, 2026 21:47
@vitorhugo-java vitorhugo-java self-assigned this Mar 31, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation backend tests database configuration labels Mar 31, 2026
@vitorhugo-java vitorhugo-java merged commit 3858703 into dev Mar 31, 2026
4 checks passed
@vitorhugo-java vitorhugo-java deleted the 75-the-heart-of-the-tracker branch March 31, 2026 21:47
vitorhugo-java added a commit that referenced this pull request Mar 31, 2026
* Add UserMediaList entity, Flyway migration, and persistence tests (#86)

* Initial plan

* Create UserMediaList entity, migration V20, and repository

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/89ca0c56-ce40-4c65-acba-470f45c07fc0



* Address review feedback: UUID v7 PK, remove media_type, add start/finish date, time_spent and note fields

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/f4eedc95-d767-47f2-9b4b-bdd6a2a7ebcc



* Update src/main/resources/db/migration/V20__create_user_media_list_table.sql



* Update src/main/resources/db/migration/V20__create_user_media_list_table.sql



* Add @table uniqueConstraints to UserMediaListModel mirroring DB unique key

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/ebac41f8-5141-4192-9a6b-7b4b159c7e68



* Add UserMediaListRepositoryTest @DataJpaTest and fix repository ID type to UUID

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/8bd217c1-398c-45a6-b66b-7e28bdd3e4b6



---------






* Add MediaStatus entity and CategoryType/StatusType enums; refactor MediaCategoryModel name field (#87)

* Initial plan

* Add MediaStatus entity, CategoryType and StatusType enums, refactor MediaCategoryModel

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/351aff6c-9b13-4dca-aef3-e683b0ea4838



---------




* feat: GraphQL query for user media library (findUserMediaLibrary) (#88)

* Initial plan

* Implement GraphQL queries for MediaStatus

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/966cc80e-e90b-49a2-9d9a-b85e965a0d61



* Replace findAllMediaStatuses with findUserMediaLibrary query

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/84a154b0-59cd-4de9-a7f8-41a3a6ae9ce6



* Add statusId, genreId, mediaId, categoryName filters to findUserMediaLibrary

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/b6f2c08a-c3ac-49e3-a959-88a442a3d3fe



* Change MediaStatusModel.id to Integer, add V22 migration, fix Javadoc

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/bbdd08fd-8299-45d3-9df2-a15df46d6d4d



* Fix CAST enum comparison and trim string filters in controller

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/431e8586-ce41-497c-8ad3-9f342e15e524



---------





* Implement GraphQL mutations and query for user media library management (#89)

* Initial plan

* Implement GraphQL mutations addMediaToUserLibrary and removeMediaFromUserLibrary with tests

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/18f5c822-c6fb-49ea-9fe5-aae0c61b4a39



* Update src/main/java/com/espacogeek/geek/services/impl/UserLibraryServiceImpl.java



* Merge 75-the-heart-of-the-tracker: refactor to UserMediaListModel, add private_list, add mutations and privacy-aware userId support

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/05f2c046-5eb1-4e38-b149-5c9d578ba82c



* Update src/main/resources/graphql/mutation.graphqls



* Add AccessDeniedException (3403) and NotFoundException (3404); update resolver, README, and tests

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/f1182947-602f-4761-9667-4bfd55668bab



---------







* Implement upsertUserMedia mutation, update StatusType, add UserCustomStatus CRUD, link custom statuses to media library entries, and add new UserMediaList fields (#90)

* Initial plan

* Implement GraphQL mutation for user media progress and custom status CRUD

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/ad983dcd-9d2c-4f14-b46f-713fd081da3b



* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java



* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java



* Update src/main/java/com/espacogeek/geek/services/impl/UserCustomStatusServiceImpl.java



* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java



* fix: update Javadoc status example from WATCHING to IN_PROGRESS

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/26a2018d-bdf3-431b-a7f1-8a7807dfebba



* fix: use InputValidationException for validation errors; validate custom status name; atomic delete

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/5e4e945f-eb96-4619-8347-1da8d030d410



* feat: add custom status list relationship to UserMediaList and update userMediaProgress

Agent-Logs-Url: https://github.com/EspacoGeek-Teams/SpringAPI_EspacoGeek/sessions/95adaabd-b150-4134-a469-96ce8b907866



* Rename mutations, add StatusType enum, add UserMediaList fields

- Rename userMediaProgress → upsertUserMedia in controller, service, schema, and tests
- Rename UserStatus CRUD mutations/query → UserCustomStatus variants
- Change UpdateUserMediaInput.status from String to StatusType enum
  - Create StatusType.graphqls enum definition
  - Remove validateStatus() (GraphQL schema now enforces enum validity)
  - Use .name() instead of .toUpperCase() in applyUpdates()
- Add rewatchCount, isPrivate, personalNotes to UserMediaListModel
  - V26 migration to add columns to user_media_list
  - Add fields to UserMediaList.graphqls type and UpdateUserMediaInput
  - Handle new fields in UserMediaListServiceImpl.applyUpdates()
- Update tests: unquote enum values, add new-fields test, replace invalid-status test





* Fix personalNotes column type: use VARCHAR(2000) to match model length constraint





* Update src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java



* Update src/main/resources/graphql/entity/UserMediaList.graphqls



---------







---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AbigailGeovana <142514517+AbigailGeovana@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

Qodana for JVM

5 new problems were found

Inspection name Severity Problems
Constant values 🔶 Warning 3
Unnecessary 'null' check before method call 🔶 Warning 1
Unused assignment 🔶 Warning 1
View the detailed Qodana report

To be able to view the detailed Qodana report, you can either:

To get *.log files or any other Qodana artifacts, run the action with upload-result option set to true,
so that the action will upload the files as the job artifacts:

      - name: 'Qodana Scan'
        uses: JetBrains/qodana-action@v2025.3.2
        with:
          upload-result: true
Contact Qodana team

Contact us at qodana-support@jetbrains.com

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-class support for user media libraries and user-defined tracking statuses across the persistence layer (Flyway + JPA), service/controller layer, and the GraphQL schema, along with new repository and GraphQL tests.

Changes:

  • Introduces user_media_list + user_custom_statuses tables and expands them with planning date, custom-status linking, and privacy/notes fields.
  • Adds new JPA models/repositories/services/controllers for UserMediaList and UserCustomStatus, and extends Media with a mediaStatus relation.
  • Expands the GraphQL schema with new types, queries, and mutations and adds corresponding GraphQL/repository tests.

Reviewed changes

Copilot reviewed 49 out of 49 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/test/java/com/espacogeek/geek/services/MediaServicePersistenceIntegrationTest.java Updates category handling in persistence test to use CategoryType.
src/test/java/com/espacogeek/geek/repositories/UserMediaListRepositoryTest.java Adds JPA repository tests for UserMediaListModel (UUID + unique constraint).
src/test/java/com/espacogeek/geek/query/userstatus/UserStatusQueryTest.java Adds GraphQL query tests for custom statuses.
src/test/java/com/espacogeek/geek/query/usermedialist/UserMediaLibraryQueryTest.java Adds GraphQL query tests for user media library + privacy + filters.
src/test/java/com/espacogeek/geek/mutation/userstatus/UserStatusMutationTest.java Adds GraphQL mutation tests for create/update/delete custom statuses.
src/test/java/com/espacogeek/geek/mutation/usermedialist/UserMediaListMutationTest.java Adds GraphQL mutation tests for add/remove/upsert library entries.
src/main/resources/graphql/query.graphqls Adds findUserMediaLibrary and getUserCustomStatuses queries.
src/main/resources/graphql/mutation.graphqls Adds library and custom-status mutations.
src/main/resources/graphql/entity/UserStatus.graphqls Adds UserStatus GraphQL type.
src/main/resources/graphql/entity/UserMediaList.graphqls Adds UserMediaList GraphQL type and UpdateUserMediaInput input.
src/main/resources/graphql/entity/user.graphqls Exposes privateList on User.
src/main/resources/graphql/entity/StatusType.graphqls Adds StatusType GraphQL enum for tracking status.
src/main/resources/graphql/entity/MediaStatus.graphqls Adds MediaStatus GraphQL type.
src/main/resources/graphql/entity/Media.graphqls Exposes mediaStatus on Media.
src/main/resources/db/migration/V20__create_user_media_list_table.sql Creates user_media_list table (UUID PK + unique constraint).
src/main/resources/db/migration/V21__add_media_status.sql Adds media_status table and medias.status_id FK; normalizes category strings.
src/main/resources/db/migration/V22__change_media_status_id_to_int.sql Changes media_status.id + FK types from BIGINT to INT.
src/main/resources/db/migration/V23__add_private_list_to_users.sql Adds users.private_list.
src/main/resources/db/migration/V24__update_status_types_and_add_date_planned.sql Adds date_planned, migrates status strings, creates user_custom_statuses.
src/main/resources/db/migration/V25__add_custom_status_to_user_media_list.sql Adds custom_status_id FK to user_media_list.
src/main/resources/db/migration/V26__add_rewatch_count_is_private_personal_notes.sql Adds rewatch_count, is_private, personal_notes to user_media_list.
src/main/java/com/espacogeek/geek/types/UpdateUserMediaInput.java Adds Java DTO backing the GraphQL upsert input.
src/main/java/com/espacogeek/geek/services/UserMediaListService.java Adds service interface for library management + filtered queries.
src/main/java/com/espacogeek/geek/services/UserCustomStatusService.java Adds service interface for custom status labels.
src/main/java/com/espacogeek/geek/services/MediaStatusService.java Adds service interface for media statuses.
src/main/java/com/espacogeek/geek/services/impl/UserMediaListServiceImpl.java Implements add/remove/upsert + filter delegation for user library entries.
src/main/java/com/espacogeek/geek/services/impl/UserCustomStatusServiceImpl.java Implements CRUD for user custom status labels with validation.
src/main/java/com/espacogeek/geek/services/impl/MediaStatusServiceImpl.java Implements MediaStatusService.
src/main/java/com/espacogeek/geek/repositories/UserMediaListRepository.java Adds repository with a JPQL filter query + upsert helpers.
src/main/java/com/espacogeek/geek/repositories/UserLibraryRepository.java Adds missing @Repository annotation.
src/main/java/com/espacogeek/geek/repositories/UserCustomStatusRepository.java Adds repository for custom status CRUD scoped by userId.
src/main/java/com/espacogeek/geek/repositories/MediaStatusRepository.java Adds repository for MediaStatusModel.
src/main/java/com/espacogeek/geek/models/UserModel.java Adds privateList column mapping.
src/main/java/com/espacogeek/geek/models/UserMediaListModel.java Adds UserMediaListModel entity (UUID PK + new columns).
src/main/java/com/espacogeek/geek/models/UserCustomStatusModel.java Adds UserCustomStatusModel entity.
src/main/java/com/espacogeek/geek/models/StatusType.java Adds Java enum for tracking statuses.
src/main/java/com/espacogeek/geek/models/MediaStatusModel.java Adds MediaStatusModel entity.
src/main/java/com/espacogeek/geek/models/MediaModel.java Adds mediaStatus relation to MediaModel.
src/main/java/com/espacogeek/geek/models/MediaCategoryModel.java Converts category column to CategoryType enum.
src/main/java/com/espacogeek/geek/models/CategoryType.java Adds Java enum for media categories.
src/main/java/com/espacogeek/geek/exception/resolver/GenericExceptionResolver.java Maps new exceptions to standardized error codes.
src/main/java/com/espacogeek/geek/exception/NotFoundException.java Adds domain exception for 3404.
src/main/java/com/espacogeek/geek/exception/MediaAlreadyInLibraryException.java Adds domain exception for 2005.
src/main/java/com/espacogeek/geek/exception/AccessDeniedException.java Adds domain exception for 3403.
src/main/java/com/espacogeek/geek/data/api/impl/TvSeriesApiImpl.java Updates MediaModel construction to include mediaStatus placeholder.
src/main/java/com/espacogeek/geek/data/api/impl/MovieAPIImpl.java Updates MediaModel construction to include mediaStatus placeholder.
src/main/java/com/espacogeek/geek/controllers/UserStatusController.java Adds GraphQL controller for custom statuses.
src/main/java/com/espacogeek/geek/controllers/UserMediaListController.java Adds GraphQL controller for library query + mutations + privacy checks.
README.md Documents new standardized error codes/exceptions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +86 to +93
String categoryNameTrimmed = categoryName == null ? null : categoryName.trim();
CategoryType categoryType = null;
if (categoryNameTrimmed != null) {
try {
categoryType = CategoryType.valueOf(categoryNameTrimmed.toUpperCase());
} catch (IllegalArgumentException e) {
throw new GenericException("Invalid categoryName: " + categoryNameTrimmed);
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Invalid categoryName is treated as an internal error: throwing GenericException here will be mapped to errorCode 5000. Since this is client input validation, it should throw InputValidationException (errorCode 2004) with a helpful message, consistent with GenericExceptionResolver's mapping.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +37
@Enumerated(EnumType.STRING)
@Column(name = "type_category")
private String typeCategory;
private CategoryType name;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

MediaCategoryModel was changed from a String typeCategory to CategoryType name, but the GraphQL schema still exposes MediaCategory.typeCategory. With the current model, typeCategory will resolve to null (no getTypeCategory()), breaking existing GraphQL clients. Either update the GraphQL schema field to match the new property, or add a compatibility getter/setter (getTypeCategory delegating to the enum) so the existing schema continues to work.

Copilot uses AI. Check for mistakes.
Comment on lines +79 to +83
@Column(name = "rewatch_count", columnDefinition = "INT DEFAULT 0")
private Integer rewatchCount;

@Column(name = "is_private", columnDefinition = "TINYINT(1) DEFAULT 0")
private Boolean isPrivate;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

rewatch_count and is_private are added as NOT NULL with defaults in V26, but the entity fields allow null and have no default initialization. Hibernate inserts often include null values explicitly, which can violate the DB constraint at runtime when an entry is persisted without these fields set. Initialize defaults in the entity (e.g., rewatchCount = 0, isPrivate = false) and/or enforce non-null via @Column(nullable = false) / @PrePersist to align with the migration.

Suggested change
@Column(name = "rewatch_count", columnDefinition = "INT DEFAULT 0")
private Integer rewatchCount;
@Column(name = "is_private", columnDefinition = "TINYINT(1) DEFAULT 0")
private Boolean isPrivate;
@Column(name = "rewatch_count", nullable = false, columnDefinition = "INT DEFAULT 0")
private Integer rewatchCount = 0;
@Column(name = "is_private", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
private Boolean isPrivate = false;

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +41
@Enumerated(EnumType.STRING)
@Column(name = "name")
private StatusType name;

@OneToMany(mappedBy = "mediaStatus")
@Transient
private List<MediaModel> medias;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

@OneToMany(mappedBy = "mediaStatus") combined with @Transient means this association is not mapped/persisted at all, which is confusing and likely not what you want. Either remove @Transient (and, if needed, handle serialization recursion via Jackson annotations) or remove the relationship annotation entirely.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +75
public UserMediaListModel addMedia(Integer userId, Integer mediaId) {
if (userMediaListRepository.existsByUserIdAndMediaId(userId, mediaId)) {
throw new MediaAlreadyInLibraryException();
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

addMedia() uses an exists-check followed by an insert. With the unique constraint on (user_id, media_id), concurrent requests can still race and trigger a DB unique-constraint violation, which will currently surface as a database error (5001) instead of the intended 2005. Consider relying on the unique constraint and catching DataIntegrityViolationException (or re-checking on failure) to consistently translate it to MediaAlreadyInLibraryException.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +122
Optional<UserMediaListModel> existing = userMediaListRepository
.findByUserIdAndMediaId(userId, input.getMediaId());

UserMediaListModel entry;
if (existing.isPresent()) {
entry = existing.get();
} else {
UserModel user = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException("User not found"));
MediaModel media = mediaRepository.findById(input.getMediaId())
.orElseThrow(() -> new NotFoundException("Media not found"));

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

upsertUserMedia() does a read-then-insert when no existing entry is found. Under concurrent requests for the same (userId, mediaId), this can race and hit the unique constraint, surfacing as a DB error (5001). Consider handling DataIntegrityViolationException on save() (e.g., retry by loading the existing row and applying updates) so the operation is robust under concurrency.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend configuration database documentation Improvements or additions to documentation tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants