feat(user) ✨ implement country preference API#24
Merged
Mattic77 merged 7 commits intoJun 5, 2026
Conversation
- add UpdateCountryPreferenceDto for preference toggle - add updateCountryPreference method to UserService - add PATCH /users/preferences/country endpoint to UserController - allow selecting specific country or all to reset preference Closes #25
- delete redundant update-country-preference.dto.ts - move UpdateCountryPreferenceDto to update-user.dto.ts - update UserController imports
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a user “preferred country” setting (persisted on User) and wires it into game session creation so that omitting countrySelection automatically filters questions by the user’s saved preference. It also adds optional categoryId filtering when starting a game session.
Changes:
- Add
PATCH /users/preferences/countryplus service logic to store/resetpreferredCountryId. - Extend Prisma schema with a
User.preferredCountryId -> Countryrelation. - Update
GameService.startSessionto use the saved preference whencountrySelectionis omitted and to supportcategoryIdfiltering.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/user/user.service.ts | Adds updateCountryPreference to persist/reset preferredCountryId. |
| src/user/user.controller.ts | Exposes PATCH /users/preferences/country for authenticated users. |
| src/user/dto/update-user.dto.ts | Adds preferredCountryId to profile updates and introduces UpdateCountryPreferenceDto. |
| src/game/game.service.ts | Applies preferred country when countrySelection is omitted; adds category filtering to quiz selection. |
| src/game/dto/start-game.dto.ts | Adds categoryId to StartGameDto and updates country selection docs. |
| prisma/schema.prisma | Adds preferredCountryId column and relation between User and Country. |
| PR_DESCRIPTION.md | Updates PR description to match the new feature scope and linked issue. |
Comments suppressed due to low confidence (1)
src/game/game.service.ts:63
- This uses
$queryRawUnsafewith string interpolation forcountryId,categoryId, anddifficulty. SincecategoryIdandcountrySelectioncome directly from the request, this introduces a SQL injection vector (and it will only get worse as more filters are added).
const categoryId = dto.categoryId;
// Fetch 15 random quiz IDs
const quizzes = await this.prisma.$queryRawUnsafe<any[]>(`
SELECT id FROM quiz
WHERE 1=1
${countryId ? `AND country_id = '${countryId}'` : ''}
${categoryId ? `AND category_id = '${categoryId}'` : ''}
${difficulty ? `AND difficulty = '${difficulty}'` : ''}
ORDER BY RANDOM()
LIMIT 15
`);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+20
to
+23
| @ApiProperty({ example: 'uuid-of-country', required: false }) | ||
| @IsOptional() | ||
| @IsString() | ||
| preferredCountryId?: string; |
| @@ -1,4 +1,4 @@ | |||
| import { IsString, IsOptional, IsEmail, IsDate } from 'class-validator'; | |||
| import { IsString, IsOptional, IsEmail, IsDate, IsNotEmpty } from 'class-validator'; | |||
Comment on lines
+32
to
+34
| @IsString() | ||
| @IsNotEmpty() | ||
| countryId: string; |
Comment on lines
+16
to
+18
| @IsOptional() | ||
| @IsString() | ||
| categoryId?: string; |
Comment on lines
+50
to
+51
| preferredCountryId String? @map("preferred_country_id") | ||
| preferredCountry Country? @relation("UserPreferredCountry", fields: [preferredCountryId], references: [id]) |
- replace queryRawUnsafe with parameterized queryRaw - use Prisma.sql and Prisma.join for safe dynamic filtering - eliminate risk of string interpolation in SQL queries
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ry preferences - add IsUUID validation to preferredCountryId in UpdateUserDto - add regex-based validation to countryId in UpdateCountryPreferenceDto - allow only valid UUIDs or the string all to ensure fail-fast 400 errors
Comment on lines
+262
to
+266
| const normalizedCountryId = countryId.trim(); | ||
| const data = | ||
| normalizedCountryId.toLowerCase() === 'all' | ||
| ? { preferredCountryId: null } | ||
| : { preferredCountryId: normalizedCountryId }; |
Comment on lines
+28
to
+31
| @ApiProperty({ example: 'uuid-of-country', required: false }) | ||
| @IsOptional() | ||
| @IsUUID() | ||
| preferredCountryId?: string; |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Comment on lines
+262
to
+266
| const normalizedCountryId = countryId.trim(); | ||
| const data = | ||
| normalizedCountryId.toLowerCase() === 'all' | ||
| ? { preferredCountryId: null } | ||
| : { preferredCountryId: normalizedCountryId }; |
Comment on lines
+27
to
+32
|
|
||
| @ApiProperty({ example: 'uuid-of-country', required: false }) | ||
| @IsOptional() | ||
| @IsUUID() | ||
| preferredCountryId?: string; | ||
| } |
Comment on lines
+1
to
+9
| import { | ||
| IsString, | ||
| IsOptional, | ||
| IsEmail, | ||
| IsDate, | ||
| IsNotEmpty, | ||
| IsUUID, | ||
| Matches, | ||
| } from 'class-validator'; |
| @@ -0,0 +1,5 @@ | |||
| -- AlterTable | |||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GitHub Issue
Closes GH-25
Description 📝
This PR implements a dedicated API endpoint for users to manage their country preferences. This preference is used by the game engine to automatically filter questions without requiring the user to select their country manually for every session.
Type of Change
Changes
UpdateCountryPreferenceDtoto handle "all" or specific UUID inputs.updateCountryPreferenceinUserServiceto manage thepreferredCountryIdfield.PATCH /users/preferences/countrytoUserControllerwith full Swagger documentation.GameService.startSession.categoryIdfiltering inGameService.startSession.Screenshots 📸 (N/A)