Skip to content

Feature/fixes#191

Open
Isti01 wants to merge 9 commits into
masterfrom
feature/fixes
Open

Feature/fixes#191
Isti01 wants to merge 9 commits into
masterfrom
feature/fixes

Conversation

@Isti01
Copy link
Copy Markdown
Collaborator

@Isti01 Isti01 commented May 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Admin and configuration screens now edit users by UID and support direct uploads to the configured asset path.
  • Bug Fixes

    • Fixed item filtering/search URL handling and tightened authentication checks for order actions.
  • Chores

    • Updated project dependencies and removed obsolete finance and stats assets.
  • Refactor

    • Widespread backend DI and null-safety improvements for more robust behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

Refactors most Spring components from field-based @Autowired to constructor injection, replaces lazy proxy lookups with nullable findByIdOrNull usages, stops hashing UIDs (uses raw uid), updates templates/assets, and bumps Maven/plugin/dependency versions; removes FinanceController and .stats CSS.

Changes

Single cohesive refactor (DI, entity fetching, auth, templates, build)

Layer / File(s) Summary
Build metadata
.mvn/wrapper/maven-wrapper.properties, pom.xml
Maven wrapper bumped 3.9.0→3.9.15; Spring Boot parent 4.0.1→4.0.6; Kotlin 2.3.0→2.3.21; micrometer-bom 1.16.1→1.16.5; itextpdf and springdoc versions updated.
Configuration / wiring
src/main/kotlin/.../config/...
Configs converted to constructor injection; ApplicationConfig now takes uploadPath; GlobalExceptionControllerAdvice 404 handler returns ResponseEntity<Void>.
Core services — constructor DI
src/main/kotlin/.../service/*Service.kt
Many services (Circle*, ExtrasService, ItemService, OpeningService, OrderService, ReviewService, UserService, ItemPrecedenceService, CircleMemberService, etc.) moved from field @Autowired to primary-constructor injection; removed messageSource injection where applicable.
Entity access — null-safe fetching
src/main/kotlin/.../service/*Procedure*.kt, *Service.kt
Replaced getReferenceById/non-null assertions with findByIdOrNull and nullable return types; procedures and order/item flows updated to throw appropriate FailedOrderException codes on missing entities.
Auth & utilities
src/main/kotlin/.../web/Utility.kt
Removed DI-backed auth helpers and SHA256 hashing; added Authentication?.getUser(users: UserRepository) and repository-backed cannotEditCircle/cannotEditCircleNoPR/isPR; MultipartFile.uploadFile now takes explicit uploadPath.
Controllers — constructor DI & auth changes
src/main/kotlin/.../web/{Admin,Api,Configure,Deploy,KitchenView,Main,Review}Controller.kt
Controllers converted to constructor injection; UserRepository added where needed; endpoints and handlers now use auth.getUser(userRepository) (stricter auth) and bind uid (not uidHash) for role/user flows; uploads use app.uploadPath.
DTOs & templates
src/main/kotlin/.../dto/RoleEntryDto.kt, src/main/resources/templates/*
RoleEntryDto and CircleRoleEntryDto use raw uid (removed hashing); Thymeleaf templates and forms updated to use uid instead of uidHash in links/inputs.
Frontend & static assets
src/main/resources/static/js/item.js, src/main/resources/static/css/stats.css
item.js: URL/filter construction refactored using URLSearchParams and consistent delimiter handling. stats.css: entire .stats stylesheet removed; minor template content removal in news.
Tests
src/test/kotlin/...
Tests adjusted for Optional/findById patterns; some tests removed (composite, OrderingServiceTest deleted).
Deleted components
src/main/kotlin/.../web/FinanceController.kt
FinanceController removed.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Controller
    participant Auth as Authentication
    participant UserRepo as UserRepository
    participant Service
    participant Repo as EntityRepository

    Client->>Controller: HTTP request (with auth)
    Controller->>Auth: extract auth
    Auth->>UserRepo: lookup user (getUser(userRepo))
    UserRepo-->>Auth: UserEntity? 
    Auth-->>Controller: UserEntity? 
    alt user found
        Controller->>Service: call business operation with user
        Service->>Repo: findByIdOrNull(id)
        Repo-->>Service: Entity? (nullable)
        Service-->>Controller: result / error
        Controller-->>Client: 200 OK / payload
    else user missing
        Controller-->>Client: 403 Forbidden
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🐰 Constructor hugs replace the autowired spree,
No more hashed tails — raw UIDs roam free,
Repositories fetch with nullable care,
Templates and uploads now point to one share,
A tidy hop through code, tidy as can be.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Feature/fixes' is vague and non-descriptive. It does not convey meaningful information about the specific changes made in this substantial changeset. Provide a more specific title that describes the main refactoring or feature. For example: 'Refactor dependency injection to constructor-based approach' or 'Remove user ID hashing and update authentication flow'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/fixes

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt (1)

1-256: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Removed test coverage for updateDetails and the composite end-to-end case.

Per the AI summary, the test that validated updateDetails logic and the composite test at the end of the file were removed. Given that updateDetails now also performs a repository lookup (timeWindowRepo.findByIdOrNull(order.intervalId)!!) and is a likely NPE site, dropping its test reduces confidence in the very paths this PR is changing. Consider re-adding a focused test that mocks timeWindowRepo and asserts both the success and missing-time-window paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt` around lines
1 - 256, Re-add focused unit tests covering CancelOrderProcedure.updateDetails
and a composite end-to-end cancel flow: mock
timeWindowRepo.findByIdOrNull(order.intervalId) to return a TimeWindowEntity for
the success case and to return null to assert the NPE/FailedOrderException path,
and assert that updateDetails invokes the repository lookup and updates
procedure.timeWindow and order fields as expected; specifically target
CancelOrderProcedure.updateDetails and the composite flow that calls
loadOrder(), loadOpening(), loadTimeWindow()/findByIdOrNull,
updateRemainingCounts(), updateExtraCategories() and updateDetails() to ensure
the new repository lookup is covered.
src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt (1)

41-47: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Awkward null handling around order?.openingId!!.

With the migration to nullable lookups, orders.getOne(orderId) and circles.getOne(circleId) likely return nullable types. The current expression order?.openingId!! is contradictory: if order is null, the safe-call returns null and !! then throws an NPE — defeating the purpose of the safe-call. Either fail fast with a clear error if any prerequisite is missing, or short-circuit the review creation.

🛡️ Suggested cleanup
-        val circleId = orders.getCircleIdByOrderId(orderId)!!
-        val order = orders.getOne(orderId)
+        val circleId = orders.getCircleIdByOrderId(orderId)
+            ?: error("Order $orderId has no associated circle")
+        val order = orders.getOne(orderId)
+            ?: error("Order $orderId not found")
 
         val fullReview = ReviewEntity(
                 circle = circles.getOne(circleId),
                 order = order,
-                openingFeeling = openings.getOne(order?.openingId!!)?.feeling,
+                openingFeeling = openings.getOne(order.openingId!!)?.feeling,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt` around lines 41
- 47, The null-handling around order?.openingId!! is unsafe and contradictory;
update ReviewService so you explicitly validate prerequisite lookups
(orders.getOne(orderId), circles.getOne(circleId), openings.getOne(...)) before
constructing the ReviewEntity: retrieve order and circle into non-null vals
using the Elvis operator (or return/throw a clear exception) and then use
order.openingId and openings.getOne(...) safely (also validate the opening
result), so the ReviewEntity constructor never relies on a silent safe-call
followed by !! that can produce unclear NPEs.
src/main/kotlin/hu/kirdev/schpincer/web/ReviewController.kt (1)

55-60: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Require order ownership before accepting a review.

This guard only checks that the caller is logged in and the order is reviewable. It never verifies that orderId belongs to the authenticated user, so any signed-in user can submit a review for another shipped order by guessing its ID. Resolve the user null-safely and reject when the order owner does not match; the same check should gate the GET handler so the form is not exposed either.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/ReviewController.kt` around lines 55
- 60, The current POST handler uses orders.getOne(orderId) and only checks
auth.hasUser(), order.status, and order.reviewId before calling
reviews.createReview; change this to safely resolve the authenticated user via
auth.getUser(userRepository) (null-check) and verify the fetched order's owner
identifier (e.g., order.userId / order.ownerId) equals the authenticated user's
id before allowing review creation—if not, throw an unauthorized/forbidden
exception; apply the same ownership check to the GET handler that renders the
review form so the form is only exposed to the order owner.
src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt (1)

405-435: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Check that the edited item/opening actually belongs to circleId.

These POST handlers authorize the path circleId, then save the loaded itemId / openingId without verifying the record belongs to that circle. Someone who can edit circle A can submit an ID from circle B and modify it. Mirror the membership check already used in editMember and deleteOpeningConfirm before saving.

Also applies to: 514-528

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt` around lines
405 - 435, The handler currently loads the target entity via
items.getOne(itemId) and then saves it without verifying it belongs to the
path's circleId, allowing cross-circle edits; before mutating/saving (after
retrieving original) add a membership/ownership check (e.g. compare
original.circle.id or original.circleId to the incoming circleId or use the same
helper logic as cannotEditCircle/editMember/deleteOpeningConfirm) and if it does
not match, return a redirect like
"redirect:/configure/$circleId?error=invalidPermissions"; apply the same
ownership check to the other similar POST block (the opening handler referenced
at lines 514-528) so both items and openings are verified to belong to the
specified circle before saving.
src/main/kotlin/hu/kirdev/schpincer/web/KitchenViewController.kt (1)

239-244: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Validate orderId against the selected opening before mutating it.

Both handlers authorize the {circleId, openingId} pair, but then update whatever orderId is passed. A user who can edit one kitchen view can change the status or chef comment of an order from another opening/circle by swapping the ID. Load the order and confirm it belongs to the same opening/circle before calling updateStatus or updateChefComment.

Also applies to: 258-263

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/KitchenViewController.kt` around
lines 239 - 244, Load the Order entity by orderId from the orders repository
(e.g. orders.findById / orders.get) and verify its openingId/circleId matches
the requested openingId/circleId (or use the existing openings.isCircleMatches
check against the order's opening) before calling orders.updateStatus or
orders.updateChefComment; if the loaded order is null or its opening/circle
doesn't match, return the redirect without mutating. Apply this same validation
to both the status handler (orders.updateStatus) and the chef-comment handler
(orders.updateChefComment).
🧹 Nitpick comments (6)
src/main/kotlin/hu/kirdev/schpincer/config/GlobalExceptionControllerAdvice.kt (1)

20-20: ⚡ Quick win

Use a lower log level for expected 404 traffic.

Line 20 logs every missing static resource as error, which can flood error monitoring from routine probes/bot traffic.

Suggested tweak
-        logger.error("No static resource {} for request '{}'", ex.resourcePath, ex.message)
+        logger.warn("No static resource {} for request '{}'", ex.resourcePath, ex.message)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/kotlin/hu/kirdev/schpincer/config/GlobalExceptionControllerAdvice.kt`
at line 20, In GlobalExceptionControllerAdvice replace the current error-level
log in the static resource 404 handler (the line using logger.error("No static
resource {} for request '{}'", ex.resourcePath, ex.message)) with a lower level
(e.g., logger.debug or logger.info) so routine missing-resource requests don't
generate error noise; update the call site in the same method to use the chosen
lower log method and keep the same message and placeholders so context
(ex.resourcePath and ex.message) is preserved.
src/main/kotlin/hu/kirdev/schpincer/service/CircleService.kt (1)

51-54: 💤 Low value

Simplify with firstOrNull().

The empty-check + indexed access can be replaced with the idiomatic Kotlin helper, which is also safer if repo.findAllByAlias ever returns an immutable empty list of a non-List type.

♻️ Proposed refactor
     `@Transactional`(readOnly = true)
     open fun findByAlias(alias: String): CircleEntity? {
-        val results = repo.findAllByAlias(alias)
-        return if (results.isEmpty()) null else results[0]
+        return repo.findAllByAlias(alias).firstOrNull()
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/service/CircleService.kt` around lines 51
- 54, In CircleService.findByAlias replace the manual empty-check + indexed
access on results from repo.findAllByAlias(alias) with the idiomatic Kotlin
firstOrNull() (e.g. use repo.findAllByAlias(alias).firstOrNull() or call
firstOrNull() on the results variable) to simplify the code and avoid unsafe
indexing; update the function to return that firstOrNull() result.
src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt (1)

36-48: 💤 Low value

Mock target still works, but consider mocking the actual extension call site.

findByIdOrNull is a Kotlin extension that internally delegates to findById(id).orElse(null), so stubbing findById here correctly drives loadOrder(). This is fine, but it couples the test to the extension's internal implementation. Stubbing findByIdOrNull directly (or using org.mockito.kotlin.any() matchers) would be more explicit about what the procedure actually calls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt` around lines
36 - 48, The test stubs orderRepository.findById(...) but the production call
uses the Kotlin extension findByIdOrNull, so update the mock to target the
actual call site: replace the whenever(orderRepository.findById(4)) stub in
CancelOrderTest with a stub for orderRepository.findByIdOrNull(4) (or use
org.mockito.kotlin.any() matchers) so that CancelOrderProcedure.loadOrder() is
driven explicitly by the same method it calls; keep the same returned
Optional/nullable value and assertions around procedure.order.
src/main/kotlin/hu/kirdev/schpincer/web/DeployController.kt (1)

9-13: 💤 Low value

LGTM with a minor suggestion.

Constructor injection for UserService looks good. For consistency, you could also move systemAdmins into the constructor (Spring supports @Value on constructor parameters), eliminating the lateinit var:

-open class DeployController(private val users: UserService) {
-
-    `@Value`("\${schpincer.sysadmins:}")
-    private lateinit var systemAdmins: String
+open class DeployController(
+    private val users: UserService,
+    `@Value`("\${schpincer.sysadmins:}") private val systemAdmins: String,
+) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/DeployController.kt` around lines 9 -
13, Move the `@Value-injected` field into the class constructor to avoid the
lateinit var: change DeployController to accept systemAdmins as a constructor
parameter annotated with `@Value`("${schpincer.sysadmins:}") alongside the
existing users: UserService parameter so the class uses constructor injection
for both (replace the private lateinit var systemAdmins: String with a
constructor parameter, keeping the existing users property and annotations).
src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt (1)

14-22: ⚡ Quick win

getById and getByIdOrNull are now functionally identical.

Both methods return UserEntity? and look up by ID. Having two identically-behaving public APIs invites confusion at call sites about which to use. Consider consolidating to a single method (e.g., keep getByIdOrNull and remove getById, or vice versa) and migrate the few remaining callers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt` around lines 14 -
22, The two public methods getById and getByIdOrNull in UserService are
identical; remove getById to avoid confusion and keep getByIdOrNull as the
single source of truth: delete the open fun getById(uid: String): UserEntity?
implementation that calls repo.findByIdOrNull(uid), update all callers to use
getByIdOrNull(uid) instead (or add a small delegating deprecated wrapper if
needed for a transitional period), ensure any interface/consumer signatures are
updated, run tests and compile to catch missed call sites, and keep
repo.findById/findByIdOrNull usage consistent within the class.
src/main/kotlin/hu/kirdev/schpincer/web/MainController.kt (1)

76-84: 💤 Low value

Confirm "error" resolves to a real view template.

Returning the string "error" when an alias is not found relies on a Thymeleaf template named error.html (or a configured error mapping) being present. If not, this branch will produce a 500 instead of a clean error page. Also consider returning a proper HTTP status (e.g., 404) rather than rendering an error page with a 200 status, for better client semantics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/MainController.kt` around lines 76 -
84, The branch that handles a missing alias currently returns the literal view
name "error" (see circles.findByAlias(circle) ?: return "error"), which may not
map to a real template and returns a 200 status; instead, either ensure an
error.html template exists or (preferred) replace the early return with a proper
404 by throwing a ResponseStatusException(HttpStatus.NOT_FOUND, "Circle not
found") (or return a ResponseEntity with 404) so that the controller method
(where circle.matches(...) and circles.findByAlias(...) live) produces a correct
HTTP 404 when the alias is missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pom.xml`:
- Around line 123-127: The micrometer BOM declaration (groupId "io.micrometer",
artifactId "micrometer-bom", version "1.16.5", type "pom") is currently in
<dependencies> and missing scope="import"; move this dependency entry into the
<dependencyManagement> section and add <scope>import</scope> so Maven treats it
as a BOM and applies its version constraints; update or create the
<dependencyManagement> block if missing and place the micrometer-bom POM
dependency there with type "pom" and scope "import".

In `@src/main/kotlin/hu/kirdev/schpincer/config/SecurityConfig.kt`:
- Line 55: The security config currently restricts only "/profile" but leaves
the kitchen-view stats endpoints publicly accessible; update the auth matchers
in SecurityConfig (the auth.requestMatchers(...) configuration that uses
Role.USER and anyRequest().permitAll()) to either include the kitchen-view paths
(e.g., add "/kitchen-view/**" and "/api/kitchen-view/**" to the Role.USER
matcher alongside "/profile") so those endpoints remain authenticated, or remove
all references and handlers for the stats endpoints from KitchenViewController,
templates, and kitchen-view.js if you intend to delete the feature; modify the
code in SecurityConfig and then verify KitchenViewController.kt and UI templates
consistently reflect the chosen approach.

In `@src/main/kotlin/hu/kirdev/schpincer/service/CancelOrderProcedure.kt`:
- Around line 34-36: Replace the unsafe non-null assertions with explicit domain
errors: in loadOrder(), change the usage of orderRepository.findByIdOrNull(id)!!
to use the Elvis operator and throw a FailedOrderException with a clear message
if null; do the same for openings.getOne(order.openingId!!) by first
null-checking order.openingId and for
timeWindowRepo.findByIdOrNull(order.intervalId)!!—each missing entity should
result in throw FailedOrderException("...") instead of letting a Kotlin NPE
occur so that loadOrder(), the openings lookup, and the timeWindow lookup all
produce domain-meaningful errors.

In `@src/main/kotlin/hu/kirdev/schpincer/service/ChangeOrderProcedure.kt`:
- Around line 28-30: Replace the unsafe non-null assertion in
ChangeOrderProcedure.loadOrder() (and the equivalent loadOrder() in
CancelOrderProcedure) so that when orderRepository.findByIdOrNull(id) returns
null you throw a FailedOrderException with the appropriate domain response code
instead of letting a Kotlin NPE bubble up; locate both loadOrder() methods,
remove the "!!" usage and construct/throw FailedOrderException carrying the
domain response code used by other validation steps so missing/deleted/invalid
ids produce a consistent failure type.

In `@src/main/kotlin/hu/kirdev/schpincer/service/CircleMemberService.kt`:
- Around line 10-20: CircleMemberService.getOne now returns a nullable
CircleMemberEntity but callers in ConfigureController still use unsafe !!
unwrapping; change those calls to handle nulls safely (for example replace val
original = members.getOne(id)!! with val original = members.getOne(id) ?: return
"redirect:..." and similarly replace val cme = members.getOne(memberId)!! with
val cme = members.getOne(memberId) ?: return "redirect:..."), or use safe
navigation (?.) where appropriate to match the null-handling pattern already
used in the controller at other sites.

In `@src/main/kotlin/hu/kirdev/schpincer/service/CircleService.kt`:
- Around line 41-43: getOne in CircleService (and equivalent OpeningService) now
returns nullable; update all callsites that use getOne(...)!! (AdminController,
KitchenViewController, ConfigureController, CancelOrderProcedure) to handle
nulls explicitly instead of force-unwrapping: check the result of getOne(id)
with the Elvis operator or an if-null branch and return the appropriate 404
error view or redirect with error parameters (mirror
MainController.findByAlias's ?: handling), and for CancelOrderProcedure ensure
order.openingId is checked before using it and handle missing openingId/opening
by aborting/returning an error flow rather than allowing a NullPointerException.

In `@src/main/kotlin/hu/kirdev/schpincer/service/MakeOrderProcedure.kt`:
- Line 57: Replace the unsafe force-unwraps on itemsRepo.findByIdOrNull(id) with
a guarded null check inside MakeOrderProcedure: when the lookup returns null,
throw a FailedOrderException with an appropriate response code (use the
project’s item-not-found response constant or RESPONSE_INTERNAL_ERROR if no
specific constant exists) instead of letting a Kotlin NPE propagate; apply the
same fix for both occurrences where item is assigned from
itemsRepo.findByIdOrNull(id) so all unknown-item cases are translated into
controlled FailedOrderException errors.

In `@src/main/kotlin/hu/kirdev/schpincer/service/OrderService.kt`:
- Around line 95-98: In getCircleIdByOrderId replace the forced-nonnull chain so
missing data propagates as null: stop using orderEntity.openingId!! and the
trailing !! on .id — call
openingRepo.findByIdOrNull(orderEntity.openingId)?.circle?.id inside the
order.map and return that orElse(null); also review any callers such as
ReviewService.submitReview that currently assume a non-null return and adjust
their !! usages accordingly.

In `@src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt`:
- Around line 27-29: The code currently calls
userService.exists(schPincerUser.internalId) and then calls
userService.getById(schPincerUser.internalId)!! which risks a race and a
forced-null crash; change the flow to perform a single read (e.g., call getById
or a findById method once), check the returned user for null, and only then
update fields like user.email = schPincerUser.email and save; remove the use of
the non-null assertion (!!) and handle the absent-user case explicitly (create
or return/throw as appropriate) within SchPincerOidcUserService.

In `@src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt`:
- Around line 15-17: getById was changed to return nullable UserEntity?, so
replace all force-unwraps with explicit null handling: in ConfigureController
where code does users.getById(it.userId)!! inside a .map(), guard against
missing users by filtering out nulls or returning a 404/meaningful error for the
whole request instead of !!; in SchPincerOidcUserService, eliminate the TOCTOU
by loading the user once and handling a null (e.g., use userService.getById(...)
and if null throw/return a controlled authentication error or load-and-create
within a single transactional/atomic operation), do not rely on a prior exists()
check followed by !!; in UserService.setRoom replace getById(userId)!! with an
explicit null check and return an error/throw (or document and enforce a
non-null contract) so callers cannot silently violate the contract. Ensure each
change logs or returns appropriate errors instead of NPEs and reference the
methods getById, setRoom, and the call sites in ConfigureController and
SchPincerOidcUserService when making the fixes.

In `@src/main/kotlin/hu/kirdev/schpincer/web/AdminController.kt`:
- Around line 217-220: The changeCard handler currently calls
CardType.valueOf(card) which throws IllegalArgumentException for invalid inputs;
update the changeCard function to catch IllegalArgumentException (or validate
the input) around the CardType.valueOf(card) call and on error return "failed"
instead of letting the exception propagate; ensure valid enum values are
assigned to user.pekCardType and saved via users.save(user) only on success,
keeping the existing auth.getUser(userRepository) check and return behavior.
- Around line 167-169: The GET handler currently dereferences the nullable
result of users.getByIdOrNull(uid) with user!! which can throw; change the GET
handler to mirror the POST handler's null-check by guarding the result (from
users.getByIdOrNull) and returning "redirect:/admin/?error=invalidUid" when
null, before calling model.addAttribute("name", ...) and
model.addAttribute("sysadmin", ...); update any references to user in that
method accordingly to use the non-null local after the guard.

In `@src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt`:
- Around line 62-64: In the four endpoints getItem, getAllItems,
getAllItemsToday and getAllItemsTomorrow replace the current loggedIn check
(auth?.isAuthenticated == true) with a concrete-user check such as
auth.getUser(userRepository) != null or auth.getUserId() != null so anonymous
AuthenticationTokens aren’t treated as logged-in; update the loggedIn variable
in each of those methods accordingly so the existing visibleWithoutLogin
filtering logic (used just after those assignments) correctly distinguishes real
users from anonymous requests.

In `@src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt`:
- Around line 204-205: The code currently force-unwraps nullable lookups (e.g.
members.getOne(id)!! and original.circle!!.id) which causes 500s for
missing/stale records; replace each `!!` usage with null-safe checks that
redirect on invalid IDs (e.g. use `members.getOne(id) ?: return
"redirect:/configure/$circleId?invalidId"` and similarly ensure
`original.circle` is non-null before accessing its id, returning the same
redirect when null). Apply the same pattern for all other occurrences called out
(the other getOne()/circle accesses around the indicated ranges) so every lookup
boundary returns a redirect on null instead of throwing.

In `@src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt`:
- Around line 56-64: The functions cannotEditCircle and cannotEditCircleNoPR use
fragile positional destructuring from auth.getUser(users); replace that with a
null-checked local variable and explicit named property access (e.g., val user =
auth.getUser(users) ?: return true; val sysadmin = user.sysadmin; val
permissions = user.permissions) and then use those names in the existing
permission logic so the code is robust to changes in UserEntity.

In `@src/main/resources/static/js/item.js`:
- Line 18: The URL builder assembles query params incorrectly by using (filter ?
'&' : '?'), producing strings like "api/items/now&circle=..."; update the
construction for the const url so you first build the base = 'api/items' +
filter and then choose the separator based on whether the base already contains
a '?' (use '&' if it does, otherwise '?') before appending 'circle=' (only when
profile !== 0). Apply the same fix to the other occurrence referenced (line ~81)
so both places use base.indexOf('?')/includes('?') to decide between '?' and '&'
when adding query parameters.
- Line 18: The frontend is appending the circle query param for all endpoints
(see url construction in appendNext()), but the ApiController methods that
handle /api/items/now and /api/items/tomorrow do not accept a circle
RequestParam; update appendNext() so it only adds &circle=... when the base
endpoint is the paginated /api/items list (i.e., when filter is empty or refers
to the list endpoint) and explicitly skip appending circle for paths containing
"/now" or "/tomorrow" (or alternatively detect whether the request path equals
'/api/items'); locate the url construction in appendNext() and conditionally
concatenate the circle param only for the list handler.
- Line 81: The construction of the request URL in the const url declaration uses
encodeURI(keyword) which does not percent-encode reserved query characters;
update both occurrences (the q= parameter building in the const url at the top
and the duplicate at line ~100) to use encodeURIComponent(keyword) so
ampersands, equals, hashes and other reserved chars in the keyword are safely
encoded; search for the const url string and replace encodeURI(keyword) with
encodeURIComponent(keyword).

---

Outside diff comments:
In `@src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt`:
- Around line 41-47: The null-handling around order?.openingId!! is unsafe and
contradictory; update ReviewService so you explicitly validate prerequisite
lookups (orders.getOne(orderId), circles.getOne(circleId), openings.getOne(...))
before constructing the ReviewEntity: retrieve order and circle into non-null
vals using the Elvis operator (or return/throw a clear exception) and then use
order.openingId and openings.getOne(...) safely (also validate the opening
result), so the ReviewEntity constructor never relies on a silent safe-call
followed by !! that can produce unclear NPEs.

In `@src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt`:
- Around line 405-435: The handler currently loads the target entity via
items.getOne(itemId) and then saves it without verifying it belongs to the
path's circleId, allowing cross-circle edits; before mutating/saving (after
retrieving original) add a membership/ownership check (e.g. compare
original.circle.id or original.circleId to the incoming circleId or use the same
helper logic as cannotEditCircle/editMember/deleteOpeningConfirm) and if it does
not match, return a redirect like
"redirect:/configure/$circleId?error=invalidPermissions"; apply the same
ownership check to the other similar POST block (the opening handler referenced
at lines 514-528) so both items and openings are verified to belong to the
specified circle before saving.

In `@src/main/kotlin/hu/kirdev/schpincer/web/KitchenViewController.kt`:
- Around line 239-244: Load the Order entity by orderId from the orders
repository (e.g. orders.findById / orders.get) and verify its openingId/circleId
matches the requested openingId/circleId (or use the existing
openings.isCircleMatches check against the order's opening) before calling
orders.updateStatus or orders.updateChefComment; if the loaded order is null or
its opening/circle doesn't match, return the redirect without mutating. Apply
this same validation to both the status handler (orders.updateStatus) and the
chef-comment handler (orders.updateChefComment).

In `@src/main/kotlin/hu/kirdev/schpincer/web/ReviewController.kt`:
- Around line 55-60: The current POST handler uses orders.getOne(orderId) and
only checks auth.hasUser(), order.status, and order.reviewId before calling
reviews.createReview; change this to safely resolve the authenticated user via
auth.getUser(userRepository) (null-check) and verify the fetched order's owner
identifier (e.g., order.userId / order.ownerId) equals the authenticated user's
id before allowing review creation—if not, throw an unauthorized/forbidden
exception; apply the same ownership check to the GET handler that renders the
review form so the form is only exposed to the order owner.

In `@src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt`:
- Around line 1-256: Re-add focused unit tests covering
CancelOrderProcedure.updateDetails and a composite end-to-end cancel flow: mock
timeWindowRepo.findByIdOrNull(order.intervalId) to return a TimeWindowEntity for
the success case and to return null to assert the NPE/FailedOrderException path,
and assert that updateDetails invokes the repository lookup and updates
procedure.timeWindow and order fields as expected; specifically target
CancelOrderProcedure.updateDetails and the composite flow that calls
loadOrder(), loadOpening(), loadTimeWindow()/findByIdOrNull,
updateRemainingCounts(), updateExtraCategories() and updateDetails() to ensure
the new repository lookup is covered.

---

Nitpick comments:
In
`@src/main/kotlin/hu/kirdev/schpincer/config/GlobalExceptionControllerAdvice.kt`:
- Line 20: In GlobalExceptionControllerAdvice replace the current error-level
log in the static resource 404 handler (the line using logger.error("No static
resource {} for request '{}'", ex.resourcePath, ex.message)) with a lower level
(e.g., logger.debug or logger.info) so routine missing-resource requests don't
generate error noise; update the call site in the same method to use the chosen
lower log method and keep the same message and placeholders so context
(ex.resourcePath and ex.message) is preserved.

In `@src/main/kotlin/hu/kirdev/schpincer/service/CircleService.kt`:
- Around line 51-54: In CircleService.findByAlias replace the manual empty-check
+ indexed access on results from repo.findAllByAlias(alias) with the idiomatic
Kotlin firstOrNull() (e.g. use repo.findAllByAlias(alias).firstOrNull() or call
firstOrNull() on the results variable) to simplify the code and avoid unsafe
indexing; update the function to return that firstOrNull() result.

In `@src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt`:
- Around line 14-22: The two public methods getById and getByIdOrNull in
UserService are identical; remove getById to avoid confusion and keep
getByIdOrNull as the single source of truth: delete the open fun getById(uid:
String): UserEntity? implementation that calls repo.findByIdOrNull(uid), update
all callers to use getByIdOrNull(uid) instead (or add a small delegating
deprecated wrapper if needed for a transitional period), ensure any
interface/consumer signatures are updated, run tests and compile to catch missed
call sites, and keep repo.findById/findByIdOrNull usage consistent within the
class.

In `@src/main/kotlin/hu/kirdev/schpincer/web/DeployController.kt`:
- Around line 9-13: Move the `@Value-injected` field into the class constructor to
avoid the lateinit var: change DeployController to accept systemAdmins as a
constructor parameter annotated with `@Value`("${schpincer.sysadmins:}") alongside
the existing users: UserService parameter so the class uses constructor
injection for both (replace the private lateinit var systemAdmins: String with a
constructor parameter, keeping the existing users property and annotations).

In `@src/main/kotlin/hu/kirdev/schpincer/web/MainController.kt`:
- Around line 76-84: The branch that handles a missing alias currently returns
the literal view name "error" (see circles.findByAlias(circle) ?: return
"error"), which may not map to a real template and returns a 200 status;
instead, either ensure an error.html template exists or (preferred) replace the
early return with a proper 404 by throwing a
ResponseStatusException(HttpStatus.NOT_FOUND, "Circle not found") (or return a
ResponseEntity with 404) so that the controller method (where
circle.matches(...) and circles.findByAlias(...) live) produces a correct HTTP
404 when the alias is missing.

In `@src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt`:
- Around line 36-48: The test stubs orderRepository.findById(...) but the
production call uses the Kotlin extension findByIdOrNull, so update the mock to
target the actual call site: replace the whenever(orderRepository.findById(4))
stub in CancelOrderTest with a stub for orderRepository.findByIdOrNull(4) (or
use org.mockito.kotlin.any() matchers) so that CancelOrderProcedure.loadOrder()
is driven explicitly by the same method it calls; keep the same returned
Optional/nullable value and assertions around procedure.order.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: daeabab1-1aef-4e00-9f1c-f116ec6c0e76

📥 Commits

Reviewing files that changed from the base of the PR and between 865e0fc and dbd7db4.

⛔ Files ignored due to path filters (1)
  • src/main/resources/static/image/stat-bg.svg is excluded by !**/*.svg
📒 Files selected for processing (42)
  • .mvn/wrapper/maven-wrapper.properties
  • pom.xml
  • src/main/kotlin/hu/kirdev/schpincer/config/ApplicationConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/config/GlobalExceptionControllerAdvice.kt
  • src/main/kotlin/hu/kirdev/schpincer/config/SecurityConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/config/TestingConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/config/WebMvcConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/dto/RoleEntryDto.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/CancelOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ChangeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/CircleMemberService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/CircleService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ExtrasService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ItemPrecedenceService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ItemService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/MakeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/OpeningService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/OrderService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/StatisticsService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/AdminController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/DeployController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/FinanceController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/KitchenViewController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/MainController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ReviewController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt
  • src/main/resources/static/css/stats.css
  • src/main/resources/static/js/item.js
  • src/main/resources/templates/admin.html
  • src/main/resources/templates/circleRoleList.html
  • src/main/resources/templates/circleRoleModify.html
  • src/main/resources/templates/news/news_2023_09_06.html
  • src/main/resources/templates/stats.html
  • src/main/resources/templates/userModify.html
  • src/test/kotlin/hu/kirdev/schpincer/service/CancelOrderTest.kt
  • src/test/kotlin/hu/kirdev/schpincer/service/MakeOrderTest.kt
  • src/test/kotlin/hu/kirdev/schpincer/service/OrderingServiceTest.kt
💤 Files with no reviewable changes (8)
  • src/main/resources/templates/stats.html
  • src/test/kotlin/hu/kirdev/schpincer/service/OrderingServiceTest.kt
  • src/main/resources/templates/news/news_2023_09_06.html
  • src/main/kotlin/hu/kirdev/schpincer/service/StatisticsService.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/FinanceController.kt
  • src/main/resources/static/css/stats.css
  • src/main/kotlin/hu/kirdev/schpincer/config/WebMvcConfig.kt
  • src/test/kotlin/hu/kirdev/schpincer/service/MakeOrderTest.kt

Comment thread pom.xml Outdated
Comment thread src/main/kotlin/hu/kirdev/schpincer/config/SecurityConfig.kt Outdated
Comment thread src/main/kotlin/hu/kirdev/schpincer/service/CancelOrderProcedure.kt
Comment thread src/main/kotlin/hu/kirdev/schpincer/service/ChangeOrderProcedure.kt
Comment on lines +10 to 20
open class CircleMemberService(private val repo: CircleMemberRepository) {

@Transactional(readOnly = false)
open fun save(member: CircleMemberEntity) {
repo.save(member)
}

@Transactional(readOnly = true)
open fun getOne(memberId: Long): CircleMemberEntity {
return repo.getReferenceById(memberId)
open fun getOne(memberId: Long): CircleMemberEntity? {
return repo.findByIdOrNull(memberId)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find any remaining references to the removed CircleMemberService.getById, and inspect callers of the new getOne for unsafe `!!`.
rg -nP --type=kotlin -C2 '\bcircleMembers?\.(getById|getOne)\b'
rg -nP --type=kotlin -C2 'CircleMemberService[^\n]*\b(getById|getOne)\b'

Repository: kir-dev/sch-pincer

Length of output: 44


🏁 Script executed:

# Find all references to CircleMemberService
rg -n "CircleMemberService" --type=kotlin

# Search for any .getOne calls regardless of object name
rg -n "\.getOne\(" --type=kotlin -A1 -B1

# Search for the pattern where result might be unsafely unwrapped
rg -n "\.getOne\([^)]*\)\s*!!" --type=kotlin

# Check if CircleMemberService is imported anywhere
rg -n "import.*CircleMemberService" --type=kotlin

Repository: kir-dev/sch-pincer

Length of output: 16628


Fix unsafe unwrapping of nullable getOne() result in CircleMemberService callers.

Migration from getById() to nullable getOne() is complete, but ConfigureController.kt has unsafe !! unwraps that will throw NPE if the member doesn't exist:

  • Line 204: val original = members.getOne(id)!!
  • Line 242: val cme = members.getOne(memberId)!!

Use proper null handling instead (e.g., ?: return "redirect:..." or safe navigation with ?.), following the pattern used elsewhere at lines 189 and 230.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/service/CircleMemberService.kt` around
lines 10 - 20, CircleMemberService.getOne now returns a nullable
CircleMemberEntity but callers in ConfigureController still use unsafe !!
unwrapping; change those calls to handle nulls safely (for example replace val
original = members.getOne(id)!! with val original = members.getOne(id) ?: return
"redirect:..." and similarly replace val cme = members.getOne(memberId)!! with
val cme = members.getOne(memberId) ?: return "redirect:..."), or use safe
navigation (?.) where appropriate to match the null-handling pattern already
used in the controller at other sites.

Comment thread src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt Outdated
Comment thread src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt Outdated
Comment thread src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt
Comment thread src/main/resources/static/js/item.js Outdated

getForJsonObject('api/items' + getFilter('') + (profile !== parseInt(0) ? '?circle=' + profile : ''))
const filter = getFilter('');
const url = 'api/items' + filter + (profile !== parseInt(0) ? (filter ? '&' : '?') + 'circle=' + profile : '');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Query delimiter logic generates invalid filtered URLs

When filter is /now or /tomorrow, using (filter ? '&' : '?') creates invalid URLs like api/items/now&circle=... and api/items/now&q=... (missing ? before first query param). This breaks filtered fetches.

Suggested fix
 function appendNext(profile = 0) {
@@
-    const filter = getFilter('');
-    const url = 'api/items' + filter + (profile !== parseInt(0) ? (filter ? '&' : '?') + 'circle=' + profile : '');
+    const filter = getFilter('');
+    const url = new URL('api/items' + filter, window.location.origin);
+    if (profile !== 0) {
+        url.searchParams.set('circle', String(profile));
+    }
-    getForJsonObject(url)
+    getForJsonObject(url.pathname.replace(/^\//, '') + url.search)
@@
 function searchFor(keyword) {
-    const filter = getFilter('');
-    const url = 'api/items' + filter + (filter ? '' : '') + (keyword ? (filter ? '&' : '?') + 'q=' + encodeURI(keyword) : '');
+    const filter = getFilter('');
+    const url = new URL('api/items' + filter, window.location.origin);
+    if (keyword) {
+        url.searchParams.set('q', keyword);
+    }
-    getForJsonObject(url)
+    getForJsonObject(url.pathname.replace(/^\//, '') + url.search)

Also applies to: 81-81

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/js/item.js` at line 18, The URL builder assembles
query params incorrectly by using (filter ? '&' : '?'), producing strings like
"api/items/now&circle=..."; update the construction for the const url so you
first build the base = 'api/items' + filter and then choose the separator based
on whether the base already contains a '?' (use '&' if it does, otherwise '?')
before appending 'circle=' (only when profile !== 0). Apply the same fix to the
other occurrence referenced (line ~81) so both places use
base.indexOf('?')/includes('?') to decide between '?' and '&' when adding query
parameters.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Filtered circle requests don’t match backend endpoint contract

appendNext() now appends circle even for /api/items/now and /api/items/tomorrow, but backend handlers shown in src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt:105-122 and :187-204 do not declare @RequestParam circle. So filtered+circle requests won’t be applied as intended.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/js/item.js` at line 18, The frontend is appending
the circle query param for all endpoints (see url construction in appendNext()),
but the ApiController methods that handle /api/items/now and /api/items/tomorrow
do not accept a circle RequestParam; update appendNext() so it only adds
&circle=... when the base endpoint is the paginated /api/items list (i.e., when
filter is empty or refers to the list endpoint) and explicitly skip appending
circle for paths containing "/now" or "/tomorrow" (or alternatively detect
whether the request path equals '/api/items'); locate the url construction in
appendNext() and conditionally concatenate the circle param only for the list
handler.

Comment thread src/main/resources/static/js/item.js Outdated
return listOf()

val openingIntervalLength = openings.getOne(openingId).intervalLength
val openingIntervalLength = openings.getOne(openingId)!!.intervalLength
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

openings.getOne(openingId)?.intervalLength ?: return listOf()

maybe, not sure if it is better

Comment thread src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 15 file(s) based on 17 unresolved review comments.

Files modified:

  • pom.xml
  • src/main/kotlin/hu/kirdev/schpincer/config/SecurityConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/CancelOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ChangeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/MakeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/OrderService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/AdminController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/KitchenViewController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt
  • src/main/resources/static/js/item.js

Commit: 707b1fdc3780925565682d34d842b41996b505ba

The changes have been pushed to the feature/fixes branch.

Time taken: 8m 55s

Fixed 15 file(s) based on 17 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/main/resources/static/js/item.js (1)

20-20: 💤 Low value

Simplify redundant parseInt(0) comparison.

The expression profile !== parseInt(0) is unnecessarily verbose since parseInt(0) always evaluates to 0.

♻️ Simplified comparison
-    const url = base + (profile !== parseInt(0) ? separator + 'circle=' + profile : '');
+    const url = base + (profile !== 0 ? separator + 'circle=' + profile : '');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/js/item.js` at line 20, The comparison uses
parseInt(0) unnecessarily; update the url construction so it compares profile to
0 directly (i.e., in the expression that sets the const url) — replace profile
!== parseInt(0) with profile !== 0 (or an equivalent clearer check) in the line
that builds url (variables involved: url, profile, base, separator).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt`:
- Around line 33-35: The comparison uses referential equality (!==) for an enum;
change the check in SchPincerOidcUserService (the if that reads
"existingUser.pekCardType !== card") to use structural equality
("existingUser.pekCardType != card") so CardType enum values are compared
idiomatically before assigning existingUser.pekCardType = card.
- Around line 30-31: The admin membership check uses inconsistent identifiers:
change the check that currently reads existingUser.uid to use
schPincerUser.internalId instead so both admin checks consistently reference the
incoming OIDC identifier; update the block in SchPincerOidcUserService where
admins.contains(existingUser.uid) is evaluated to use
admins.contains(schPincerUser.internalId) and ensure any related comments or
variable assumptions align with schPincerUser.internalId as the canonical admin
identifier.

In `@src/main/kotlin/hu/kirdev/schpincer/web/AdminController.kt`:
- Around line 153-160: In adminDeleteCircleConfirm, verify the circle exists
before deleting related items: call circles.getOne(circleId) first, return the
redirect on null, then call items.deleteByCircle(circleId) and finally
circles.delete(circle); also ensure the sequence (getOne -> deleteByCircle ->
delete) occurs inside the same transaction/context so partial deletes cannot
happen if validation fails.

In `@src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt`:
- Around line 26-27: The current branch in the expression that handles
uploadPath mistakenly prefixes absolute paths with
System.getProperty("user.dir"); swap the branches so that when
uploadPath.startsWith("/") you return uploadPath unchanged, and only prefix
relative paths with System.getProperty("user.dir") + "/" + uploadPath; update
the expression using the uploadPath variable (and the
System.getProperty("user.dir") logic) so absolute paths are treated as absolute.

---

Nitpick comments:
In `@src/main/resources/static/js/item.js`:
- Line 20: The comparison uses parseInt(0) unnecessarily; update the url
construction so it compares profile to 0 directly (i.e., in the expression that
sets the const url) — replace profile !== parseInt(0) with profile !== 0 (or an
equivalent clearer check) in the line that builds url (variables involved: url,
profile, base, separator).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e86aa14a-9ff7-43ae-bdfe-ddc42a3747b5

📥 Commits

Reviewing files that changed from the base of the PR and between dbd7db4 and 707b1fd.

📒 Files selected for processing (15)
  • pom.xml
  • src/main/kotlin/hu/kirdev/schpincer/config/SecurityConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/CancelOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ChangeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/MakeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/OrderService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/AdminController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/KitchenViewController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt
  • src/main/resources/static/js/item.js
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/main/kotlin/hu/kirdev/schpincer/config/SecurityConfig.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ReviewService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/UserService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/ChangeOrderProcedure.kt
  • pom.xml
  • src/main/kotlin/hu/kirdev/schpincer/service/OrderService.kt
  • src/main/kotlin/hu/kirdev/schpincer/service/MakeOrderProcedure.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ApiController.kt
  • src/main/kotlin/hu/kirdev/schpincer/web/ConfigureController.kt

Comment on lines +30 to +31
if (admins.contains(existingUser.uid))
existingUser.sysadmin = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify the relationship between uid and internalId in UserEntity and SchPincerOidcUser

# Find UserEntity definition and uid property
ast-grep --pattern 'class UserEntity(
  $$$
) {
  $$$
}'

# Find SchPincerOidcUser and internalId property  
ast-grep --pattern 'class SchPincerOidcUser {
  $$$
}'

# Check for any transformations or mappings between uid and internalId
rg -n 'uid|internalId' --type=kotlin -C 2

Repository: kir-dev/sch-pincer

Length of output: 20009


Use consistent identifier source for admin check.

Line 30 checks existingUser.uid against the admin list, while Line 52 checks schPincerUser.internalId. Although both should contain the same identifier value, this inconsistent usage reduces clarity and increases maintenance risk. Both checks should reference the same source—preferably schPincerUser.internalId for consistency with the incoming OIDC context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt`
around lines 30 - 31, The admin membership check uses inconsistent identifiers:
change the check that currently reads existingUser.uid to use
schPincerUser.internalId instead so both admin checks consistently reference the
incoming OIDC identifier; update the block in SchPincerOidcUserService where
admins.contains(existingUser.uid) is evaluated to use
admins.contains(schPincerUser.internalId) and ensure any related comments or
variable assumptions align with schPincerUser.internalId as the canonical admin
identifier.

Comment on lines +33 to 35
if (existingUser.pekCardType !== card) {
existingUser.pekCardType = card
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check the type of pekCardType to confirm correct operator usage

# Find the UserEntity class definition and the pekCardType property
ast-grep --pattern 'class UserEntity {
  $$$
}'

# Also check SchPincerOidcUser for cardType property
rg -n 'cardType' --type=kotlin -C 3

Repository: kir-dev/sch-pincer

Length of output: 45534


🏁 Script executed:

rg -n "enum class CardType|class CardType|data class CardType" --type=kotlin

Repository: kir-dev/sch-pincer

Length of output: 162


Use != instead of !== for enum comparison.

CardType is an enum, and in Kotlin the idiomatic approach for comparing enums is to use != (structural equality), not !== (referential equality). This is also consistent with how CardType is compared throughout the rest of the codebase (see CustomComponentType.kt).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/service/SchPincerOidcUserService.kt`
around lines 33 - 35, The comparison uses referential equality (!==) for an
enum; change the check in SchPincerOidcUserService (the if that reads
"existingUser.pekCardType !== card") to use structural equality
("existingUser.pekCardType != card") so CardType enum values are compared
idiomatically before assigning existingUser.pekCardType = card.

Comment on lines 153 to +160
@PostMapping("/circles/delete/{circleId}/confirm")
fun adminDeleteCircleConfirm(
@PathVariable circleId: Long,
request: HttpServletRequest
@PathVariable circleId: Long,
request: HttpServletRequest
): String {
items.deleteByCircle(circleId)
circles.delete(circles.getOne(circleId)!!)
val circle = circles.getOne(circleId) ?: return "redirect:/admin/?error=invalidCircleId"
circles.delete(circle)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate the circle before deleting related items.

items.deleteByCircle(circleId) executes before you prove circleId is valid. An invalid request can therefore delete item rows and still return invalidCircleId.

Suggested fix
     fun adminDeleteCircleConfirm(
         `@PathVariable` circleId: Long,
         request: HttpServletRequest
     ): String {
-        items.deleteByCircle(circleId)
         val circle = circles.getOne(circleId) ?: return "redirect:/admin/?error=invalidCircleId"
+        items.deleteByCircle(circleId)
         circles.delete(circle)
         return REDIRECT_TO_ADMIN
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping("/circles/delete/{circleId}/confirm")
fun adminDeleteCircleConfirm(
@PathVariable circleId: Long,
request: HttpServletRequest
@PathVariable circleId: Long,
request: HttpServletRequest
): String {
items.deleteByCircle(circleId)
circles.delete(circles.getOne(circleId)!!)
val circle = circles.getOne(circleId) ?: return "redirect:/admin/?error=invalidCircleId"
circles.delete(circle)
`@PostMapping`("/circles/delete/{circleId}/confirm")
fun adminDeleteCircleConfirm(
`@PathVariable` circleId: Long,
request: HttpServletRequest
): String {
val circle = circles.getOne(circleId) ?: return "redirect:/admin/?error=invalidCircleId"
items.deleteByCircle(circleId)
circles.delete(circle)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/AdminController.kt` around lines 153
- 160, In adminDeleteCircleConfirm, verify the circle exists before deleting
related items: call circles.getOne(circleId) first, return the redirect on null,
then call items.deleteByCircle(circleId) and finally circles.delete(circle);
also ensure the sequence (getOne -> deleteByCircle -> delete) occurs inside the
same transaction/context so partial deletes cannot happen if validation fails.

Comment on lines +26 to +27
if (uploadPath.startsWith("/")) System.getProperty("user.dir") + "/" + uploadPath
else uploadPath
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Treat absolute upload paths as absolute.

This branch currently rewrites an absolute path like /var/uploads to ${user.dir}//var/uploads, so uploads land in the wrong directory when app.uploadPath is configured with an absolute path.

Suggested fix
-    var path =
-        if (uploadPath.startsWith("/")) System.getProperty("user.dir") + "/" + uploadPath
-        else uploadPath
+    var path =
+        if (File(uploadPath).isAbsolute) uploadPath
+        else File(System.getProperty("user.dir"), uploadPath).path
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (uploadPath.startsWith("/")) System.getProperty("user.dir") + "/" + uploadPath
else uploadPath
var path =
if (File(uploadPath).isAbsolute) uploadPath
else File(System.getProperty("user.dir"), uploadPath).path
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/hu/kirdev/schpincer/web/Utility.kt` around lines 26 - 27, The
current branch in the expression that handles uploadPath mistakenly prefixes
absolute paths with System.getProperty("user.dir"); swap the branches so that
when uploadPath.startsWith("/") you return uploadPath unchanged, and only prefix
relative paths with System.getProperty("user.dir") + "/" + uploadPath; update
the expression using the uploadPath variable (and the
System.getProperty("user.dir") logic) so absolute paths are treated as absolute.

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.

2 participants