Skip to content

Mongo db driver update#176

Open
kschneider84 wants to merge 39 commits into
case-framework:mainfrom
kschneider84:mongo-db-driver-update
Open

Mongo db driver update#176
kschneider84 wants to merge 39 commits into
case-framework:mainfrom
kschneider84:mongo-db-driver-update

Conversation

@kschneider84

@kschneider84 kschneider84 commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Update mongodb driver: v1 → v2

For detailed changes and test protocol see test/mongodb-driver-update.md

Summary by CodeRabbit

  • Chores
    • Upgraded MongoDB Go driver to v2 across the codebase: standardized BSON/ObjectID usage, updated connection/ping flows, modernized options builders, and simplified index creation/drop and error handling. Added test/run-all.sh to .gitignore.
  • Documentation
    • Added a migration guide with step-by-step verification and a manual test protocol for the MongoDB driver upgrade.

…y instance of primitive.ObjectID to bson.ObjectId.
…exes by using statically defined default indexes, so default indexes are dropped reliably even when create was not run beforehand.
@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Migration to the MongoDB Go driver v2: import paths and BSON/ObjectID types updated, fluent options APIs adopted, index names centralized into constants with simplified drop logic, and mongo.Connect/context callsites adjusted across jobs, DB packages, services, models, and tests.

Changes

Cohort / File(s) Summary
Dependency & gitignore
go.mod, .gitignore
Bumped MongoDB driver to go.mongodb.org/mongo-driver/v2 and updated indirect deps in go.mod; added test/run-all.sh to .gitignore.
Core DB services & utils
pkg/db/.../db.go, pkg/db/utils.go, pkg/db/*/db.go
Switched imports to /v2 for bson, mongo, options; removed passing a timeout context into mongo.Connect and retained separate Ping timeout contexts; types now reference v2 packages.
Index name centralization & drop logic
pkg/db/**/*.go (e.g., global-infos/..., management-user/*, messaging/*, participant-user/*, study/*)
Introduced named index constants and default*IndexNames slices; replaced runtime inspection of IndexModel.Options.Name with deterministic DropOne-by-name loops; simplified DropAll/DropOne callsites and removed fmt-based nil-name handling.
BSON/ObjectID model updates
pkg/*/types/*, pkg/*/*/*.go, services/*/apihandlers/*, pkg/study/*, pkg/messaging/types/*, pkg/user-management/types/*
Replaced primitive.ObjectID, primitive.NewObjectID, primitive.ObjectIDFromHex, primitive.NilObjectID with v2 equivalents (bson.ObjectID, bson.NewObjectID, bson.ObjectIDFromHex, bson.NilObjectID); updated casts and struct field types across models, handlers, and tests.
Options builder & API shape changes
various DB files (messaging, scheduled-email, sms-templates, participant-user, study)
Converted pointer-based options struct literals to v2 fluent builders (e.g., options.FindOneAndReplace().SetUpsert(...)); changed some session callback signatures to context.Context; adjusted Distinct decoding to Decode into typed slices.
Jobs & scripts
jobs/... (jobs/db-migration/main.go, jobs/messaging/*, jobs/study-timer/*, jobs/user-management/*)
Updated BSON imports and ObjectID handling to v2; no behavioral changes beyond import/type updates.
API handlers & service layers
services/participant-api/..., services/management-api/...
Swapped BSON imports to v2 and updated ObjectID conversions/NilObjectID usages in handlers; business logic unchanged.
Docs & migration guide / tests
test/mongodb-driver-update.md, tests under pkg/*/*_test.go
Added migration documentation and manual test protocol; updated tests to use v2 bson ObjectID generators and imports.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped from primitive into bson bright,
I tucked index names in constants at night,
Fluent builders hum while contexts realign,
IDs march tidy in v2’s straight line,
I nibble merge conflicts — carrot-fueled delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Mongo db driver update' clearly and directly summarizes the main change: updating the MongoDB driver from v1 to v2 across the entire codebase.
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 unit tests (beta)
  • Create PR with unit tests

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.

@greptile-apps

greptile-apps Bot commented Apr 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR upgrades the MongoDB Go driver from v1 (go.mongodb.org/mongo-driver) to v2 (go.mongodb.org/mongo-driver/v2) across the entire codebase (89 files). The migration covers import path updates, primitive.ObjectIDbson.ObjectID renaming, options API modernisation (method chaining instead of struct literals), removal of the context argument from mongo.Connect, the new Distinct.Decode pattern, and the WithSession callback type change.

  • Import paths, ObjectID types, and options builders have been consistently updated throughout; index names are now correctly extracted as named constants.
  • Several DeleteMany/UpdateMany/DeleteOne call sites return res.Field without first checking err, which will panic when the driver returns nil, err — these were flagged in the previous review round and remain open.
  • The stray nil variadic argument in outgoing-emails.go was already resolved in the current diff.

Confidence Score: 4/5

Safe to merge once the open nil-pointer dereference issues on DeleteMany/UpdateMany/DeleteOne result access are resolved; the remainder of the v2 migration is mechanically correct.

Multiple DB write functions across the study package return res.DeletedCount or res.ModifiedCount without first checking err; when the driver call fails it returns a nil result, so any DB error on those paths triggers a runtime panic rather than a graceful error return. These were flagged in the prior review round and remain unaddressed in the current HEAD.

pkg/db/study/confidential-responses.go, pkg/db/study/responses.go, pkg/db/study/reports.go, pkg/db/study/study-rules.go, pkg/db/study/researcher-messages.go

Important Files Changed

Filename Overview
pkg/db/study/confidential-responses.go New file from v1→v2 migration; DeleteConfidentialResponses and UpdateParticipantIDonConfidentialResponses access res.DeletedCount/res.ModifiedCount before checking err — nil pointer panic when DB call fails.
pkg/db/study/responses.go UpdateParticipantIDonResponses accesses res.ModifiedCount before checking err from UpdateMany — nil pointer panic on DB failure.
pkg/db/study/study-rules.go New file; DeleteStudyRulesByID accesses res.DeletedCount before checking err from DeleteOne — nil pointer panic on DB failure.
pkg/db/study/reports.go UpdateParticipantIDonReports accesses res.ModifiedCount before checking err from UpdateMany; misleading error log in DropIndex path already flagged.
pkg/db/study/researcher-messages.go DeleteResearcherMessages accesses res.DeletedCount before checking err from DeleteMany — nil pointer panic on DB failure.
pkg/db/messaging/outgoing-emails.go Stray nil variadic arg on DeleteOne was fixed; remaining changes correctly updated to v2 API.
pkg/db/study/surveys.go Options builder and Distinct API correctly migrated to v2; pre-existing DeleteSurveyVersion nil-dereference pattern not touched in this diff.
pkg/db/participant-user/users.go AddUser and all FindOneAndReplace calls correctly migrated; options struct replaced with method-chaining API.
pkg/db/participant-user/otps.go WithSession callback correctly updated from func(SessionContext) to func(context.Context) for v2 compatibility.
go.mod Driver dependency bumped from v1.17.4 to v2.5.0; indirect dependencies updated accordingly.

Reviews (8): Last reviewed commit: "Update mongo driver version" | Re-trigger Greptile

Comment thread pkg/db/study/reports.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (5)
pkg/db/study/researcher-messages.go (1)

65-66: ⚠️ Potential issue | 🟡 Minor

Check err before reading DeletedCount.

Although DeleteMany returns a DeleteResult value (not a pointer), you should check the error before attempting to use the result. This aligns with error handling patterns used elsewhere in the codebase (see renew-tokens.go and responses.go). If the operation fails, err will be non-nil and you should return it rather than proceeding with an empty or invalid count.

🐛 Proposed fix
 	res, err := dbService.collectionResearcherMessages(instanceID, studyKey).DeleteMany(ctx, filter)
-	return res.DeletedCount, err
+	if err != nil {
+		return 0, err
+	}
+	return res.DeletedCount, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/researcher-messages.go` around lines 65 - 66, The code calls
dbService.collectionResearcherMessages(...).DeleteMany and immediately returns
res.DeletedCount without checking err; change the logic in the function that
performs this DeleteMany (referencing DeleteMany, collectionResearcherMessages
and the returned res.DeleteResult) to first check if err != nil and return 0,
err (or return the error) before reading res.DeletedCount; only return
res.DeletedCount when err is nil to match error-handling used in renew-tokens.go
and responses.go.
pkg/db/participant-user/otps.go (1)

76-108: ⚠️ Potential issue | 🟠 Major

WithSession without a transaction — count-then-insert still races.

The callback signature is correct for v2, but mongo.WithSession alone only scopes a session to sessCtx; it does not start a transaction. The CountDocumentsInsertOne sequence (lines 89–104) is not atomic—two concurrent calls can both observe count < maxOTPCount and each insert, exceeding the cap.

To enforce maxOTPCount, wrap the callback in session.WithTransaction:

Transactional fix
-	return mongo.WithSession(ctx, session, createOTPIfLimitNotReached)
+	_, err = session.WithTransaction(ctx, func(sessCtx context.Context) (interface{}, error) {
+		return nil, createOTPIfLimitNotReached(sessCtx)
+	})
+	return err

Alternatively, use a unique index or atomic $inc-guarded upsert. Without an explicit transaction, the session adds overhead without benefit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/participant-user/otps.go` around lines 76 - 108, The CreateOTP flow
currently opens a session with StartSession and calls mongo.WithSession but does
not start a transaction, so the CountDocuments -> InsertOne sequence (in the
anonymous createOTPIfLimitNotReached callback using collectionOTPs,
CountDocuments and InsertOne) can race and exceed maxOTPCount; change the
implementation to call session.WithTransaction(ctx, txnFunc) (or use
session.StartTransaction + session.WithTransaction pattern) and perform the
CountDocuments and InsertOne inside that transaction, or alternatively replace
the check+insert with an atomic approach (unique index or an $inc-guarded
upsert) to enforce maxOTPCount without races.
pkg/db/messaging/db.go (1)

33-40: ⚠️ Potential issue | 🟡 Minor

Remove dead context.WithTimeout — not accepted by mongo.Connect in driver v2.

The timeout created on lines 33–34 is discarded (assigned to _) and never applied to the connection setup, since mongo.Connect in driver v2 no longer accepts a context parameter. The resulting defer cancel() is a no-op. The timeout is properly applied to Ping on line 46 instead, so this first block should be removed.

♻️ Proposed cleanup
 func NewMessagingDBService(configs db.DBConfig) (*MessagingDBService, error) {
-	_, cancel := context.WithTimeout(context.Background(), time.Duration(configs.Timeout)*time.Second)
-	defer cancel()
-
 	dbClient, err := mongo.Connect(
 		options.Client().ApplyURI(configs.URI),
 		options.Client().SetMaxConnIdleTime(time.Duration(configs.IdleConnTimeout)*time.Second),
 		options.Client().SetMaxPoolSize(configs.MaxPoolSize),
 	)

Apply the same cleanup to the sibling DB service files: pkg/db/participant-user/db.go, pkg/db/study/db.go, pkg/db/management-user/db.go, and pkg/db/global-infos/db.go.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/messaging/db.go` around lines 33 - 40, Remove the pointless
context.WithTimeout call and its defer cancel in pkg/db/messaging/db.go because
the returned context is discarded and mongo.Connect no longer accepts a context;
leave the mongo.Connect(...) call as-is and rely on the existing Ping call
(which already uses a proper context timeout) to enforce connection timeout.
Specifically delete the context.WithTimeout(...) and defer cancel() lines
surrounding the mongo.Connect invocation (symbols: context.WithTimeout, defer
cancel, mongo.Connect, Ping) and run the same cleanup in the sibling DB files
pkg/db/participant-user/db.go, pkg/db/study/db.go, pkg/db/management-user/db.go,
and pkg/db/global-infos/db.go.
pkg/db/participant-user/renew-tokens.go (1)

120-120: ⚠️ Potential issue | 🟡 Minor

Remove trailing nil arguments from DeleteOne/DeleteMany calls to match v2 driver idioms.

In MongoDB Go Driver v2, DeleteOne and DeleteMany accept variadic opts parameters. When no options are needed, omit the argument entirely rather than passing nil. The standard practice shown in driver documentation is to call these methods with just ctx and filter.

♻️ Proposed fix
-	res, err := dbService.collectionRenewTokens(instanceID).DeleteOne(ctx, filter, nil)
+	res, err := dbService.collectionRenewTokens(instanceID).DeleteOne(ctx, filter)
@@
-	res, err := dbService.collectionRenewTokens(instanceID).DeleteMany(ctx, filter, nil)
+	res, err := dbService.collectionRenewTokens(instanceID).DeleteMany(ctx, filter)
@@
-	res, err := dbService.collectionRenewTokens(instanceID).DeleteMany(ctx, filter, nil)
+	res, err := dbService.collectionRenewTokens(instanceID).DeleteMany(ctx, filter)
@@
-	res, err := dbService.collectionOutgoingEmails(instanceID).DeleteOne(ctx, filter, nil)
+	res, err := dbService.collectionOutgoingEmails(instanceID).DeleteOne(ctx, filter)

Also applies to: 135-135, 147-147, and pkg/db/messaging/outgoing-emails.go:114.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/participant-user/renew-tokens.go` at line 120, Remove the unnecessary
trailing nil options from Mongo delete calls: update calls like
dbService.collectionRenewTokens(instanceID).DeleteOne(ctx, filter, nil) and any
DeleteMany(ctx, filter, nil) to call the v2 driver idiom without the nil options
(e.g., DeleteOne(ctx, filter)); apply the same change to the other locations
mentioned (lines calling DeleteOne/DeleteMany and
pkg/db/messaging/outgoing-emails.go:114) so only ctx and filter are passed.
pkg/db/management-user/sessions.go (1)

27-48: ⚠️ Potential issue | 🟠 Major

Suppress IndexNotFound (code 27) errors when dropping individual indexes.

The PR decouples DropIndexFor* from CreateDefaultIndexesFor*, allowing drops to run independently. This means DropOne will encounter missing indexes and return a CommandError with code 27 / IndexNotFound, which is then logged as ERROR. On fresh deployments or after DropAll has already run, every default index name logs a spurious error.

This pattern is repeated across 28+ files in the codebase (not limited to the five mentioned): permissions.go, service-users.go, sent-emails.go, study-rules.go, study-code-lists.go, and many others across study/, messaging/, participant-user/, and global-infos/ packages.

The codebase already uses errors.As() with mongo.CommandError in pkg/db/utils.go (line ~26, checking code 26). Apply the same pattern to filter code 27:

err := dbService.collectionSessions(instanceID).Indexes().DropOne(ctx, indexName)
if err != nil {
    var cmdErr mongo.CommandError
    if errors.As(err, &cmdErr) && cmdErr.Code == 27 { // IndexNotFound
        continue
    }
    slog.Error("Error dropping index for sessions", slog.String("error", err.Error()), slog.String("indexName", indexName))
}

Consider extracting a small helper function (e.g., dropIndexIfExists) in pkg/db/utils.go to avoid duplicating this across the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/management-user/sessions.go` around lines 27 - 48,
DropIndexForSessionsCollection currently logs errors when DropOne returns a
CommandError for missing indexes; update it to ignore mongo.CommandError with
Code == 27 (IndexNotFound) by using errors.As to type-assert to
mongo.CommandError and continue instead of logging, when iterating
defaultSessionIndexNames and calling
collectionSessions(instanceID).Indexes().DropOne; to avoid duplication across
the codebase, add a helper dropIndexIfExists(ctx, indexer, indexName) in
pkg/db/utils.go that encapsulates the DropOne call and the
errors.As/mongo.CommandError Code==27 check, then call that helper from
DropIndexForSessionsCollection (and other similar DropIndexFor* functions).
🧹 Nitpick comments (6)
go.mod (1)

11-11: Consider bumping to the latest v2 patch or documenting the pin.

go.mongodb.org/mongo-driver/v2 is pinned to v2.5.0, while pkg.go.dev and MongoDB’s release announcement currently list v2.5.1 as available. Since this PR is already doing the driver-wide migration, consider taking the latest patch unless v2.5.0 is intentional.

Sources: pkg.go.dev module page, MongoDB v2.5.1 release announcement.

🔧 Proposed dependency bump
-	go.mongodb.org/mongo-driver/v2 v2.5.0
+	go.mongodb.org/mongo-driver/v2 v2.5.1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@go.mod` at line 11, The go.mod currently pins the MongoDB driver to
go.mongodb.org/mongo-driver/v2 v2.5.0; update the version to the latest v2 patch
(v2.5.1) or add a comment documenting why v2.5.0 is intentionally pinned. Locate
the module line for go.mongodb.org/mongo-driver/v2 in go.mod and either change
the version token to v2.5.1 or add a short inline comment explaining the
intentional pin to prevent accidental upgrades.
pkg/db/messaging/scheduled-email.go (2)

108-114: Avoid shadowing the options package with a local variable.

options := options.FindOneAndReplace()... shadows the imported options package inside this scope. It compiles today only because the RHS is evaluated before the short-var declaration enters scope, so options.After on the last chained call still resolves to the package. Any future maintenance that adds another options.* reference below this line in the same block will silently bind to the local variable and fail in confusing ways. Other files in this PR (e.g. study-rules.go) already use opts — prefer consistency.

♻️ Proposed rename
-		options := options.FindOneAndReplace().
-			SetUpsert(false).
-			SetReturnDocument(options.After)
+		opts := options.FindOneAndReplace().
+			SetUpsert(false).
+			SetReturnDocument(options.After)
 		elem := messagingTypes.ScheduledEmail{}
 		err := dbService.collectionEmailSchedules(instanceID).
-			FindOneAndReplace(ctx, filter, scheduledEmail, options).
+			FindOneAndReplace(ctx, filter, scheduledEmail, opts).
 			Decode(&elem)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/messaging/scheduled-email.go` around lines 108 - 114, The local
variable named options shadows the imported options package in the
FindOneAndReplace call; rename the local var to opts (or similar) to avoid
shadowing so package symbols like options.After continue to refer to the
imported package. Update the chain that currently does options :=
options.FindOneAndReplace()... to use opts := options.FindOneAndReplace()... and
ensure subsequent references in this block (e.g., the call in
dbService.collectionEmailSchedules(instanceID).FindOneAndReplace(ctx, filter,
scheduledEmail, opts).Decode(&elem)) use the new opts variable.

17-25: Minor: dropAll=false branch is a no-op warning.

Since the email-schedules collection has no default indexes, calling DropIndexForEmailSchedulesCollection(instanceID, false) just logs a warning and returns. Consistent with the PR pattern, but consider whether callers should be surfaced this via return value instead of a log, or simply have the function early-return silently. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/messaging/scheduled-email.go` around lines 17 - 25, The no-op branch
in DropIndexForEmailSchedulesCollection currently only logs a warning when
dropAll is false; change this to either silently early-return or, preferably,
surface the no-op to callers by returning a boolean (or error) indicating
“nothing to drop” so callers can react instead of relying on logs—update the
DropIndexForEmailSchedulesCollection signature and callers accordingly, and keep
logging only for unexpected errors (retain the slog.Error in the err path and
remove/replace the slog.Warn in the no-op branch); refer to
DropIndexForEmailSchedulesCollection and
dbService.collectionEmailSchedules(instanceID) when locating the code to change.
pkg/db/study/confidential-responses.go (1)

106-110: Avoid passing explicit nil as variadic opts

In v2, Collection.Find's opts parameter is ...options.Lister[options.FindOptions]. Passing nil explicitly results in a one-element slice containing a nil interface. The driver currently skips nil entries in mongoutil.NewOptions, so this doesn't crash today, but it's non-idiomatic and fragile across driver updates — simply omit the argument.

♻️ Proposed fix
-	cur, err := dbService.collectionConfidentialResponses(instanceID, studyKey).Find(
-		ctx,
-		filter,
-		nil,
-	)
+	cur, err := dbService.collectionConfidentialResponses(instanceID, studyKey).Find(ctx, filter)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/confidential-responses.go` around lines 106 - 110, The Find call
passing an explicit nil as the variadic opts is non-idiomatic and can produce a
one-element slice with a nil interface; update the call to
collectionConfidentialResponses(instanceID, studyKey).Find(ctx, filter) (i.e.,
remove the trailing nil variadic argument) so no nil entry is created; also scan
for other usages of collectionConfidentialResponses(...).Find or similar
Collection.Find calls and remove explicit nil variadic opts to rely on the
zero-argument form.
pkg/db/messaging/email-templates.go (1)

119-128: Redundant ID assignment in the insert path

emailTemplate.ID = bson.NewObjectID() is set on the struct right before InsertOne, then the exact same value is read back via res.InsertedID.(bson.ObjectID) and reassigned. You can either drop the pre-assignment and rely on the driver-generated _id, or drop the post-assertion since you already know the ID you set. Not a functional bug, just dead work.

♻️ Suggested simplification
 	if emailTemplate.ID.IsZero() {
-		emailTemplate.ID = bson.NewObjectID()
 		// new email template
 		res, err := messagingDBService.collectionEmailTemplates(instanceID).InsertOne(ctx, emailTemplate)
 		if err != nil {
 			return messagingTypes.EmailTemplate{}, err
 		}
 		emailTemplate.ID = res.InsertedID.(bson.ObjectID)
 		return emailTemplate, nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/messaging/email-templates.go` around lines 119 - 128, The code
redundantly sets emailTemplate.ID = bson.NewObjectID() before InsertOne and then
reassigns emailTemplate.ID = res.InsertedID.(bson.ObjectID) after insert; choose
one approach and remove the dead work: either remove the pre-insert assignment
(leave ID generation to the driver and keep using res.InsertedID from InsertOne)
or keep the pre-insert bson.NewObjectID() and drop the post-insert reassignment.
Update the block around InsertOne (symbols: emailTemplate.ID,
bson.NewObjectID(), InsertOne, res.InsertedID) accordingly so only one
assignment remains.
pkg/db/management-user/db.go (1)

38-45: Remove the unused timeout context on lines 38–39.

In MongoDB Go driver v2, mongo.Connect() does not accept a context parameter. The context created on line 38 is discarded, and the timeout is only applied to the Ping() call on line 51 (which creates its own context). The dead code allocation can be safely removed.

♻️ Proposed cleanup
 func NewManagementUserDBService(configs db.DBConfig) (*ManagementUserDBService, error) {
-	_, cancel := context.WithTimeout(context.Background(), time.Duration(configs.Timeout)*time.Second)
-	defer cancel()
-
 	dbClient, err := mongo.Connect(
 		options.Client().ApplyURI(configs.URI),
 		options.Client().SetMaxConnIdleTime(time.Duration(configs.IdleConnTimeout)*time.Second),
 		options.Client().SetMaxPoolSize(configs.MaxPoolSize),
 	)

This pattern also appears in pkg/db/study/db.go, pkg/db/messaging/db.go, pkg/db/participant-user/db.go, and pkg/db/global-infos/db.go.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/management-user/db.go` around lines 38 - 45, Remove the unused timeout
context allocation: eliminate the call to context.WithTimeout(...) and the
following defer cancel() since the returned context and cancel are never used
around the mongo.Connect(...) call; keep the mongo.Connect(...) invocation as-is
and ensure the existing Ping(...) call (which creates its own context) remains
untouched. Specifically, remove the variables created by context.WithTimeout and
the defer cancel near the mongo.Connect call (symbols: context.WithTimeout,
cancel, mongo.Connect, configs) and apply the same cleanup to the identical
pattern in the other DB files that create but never use that context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/db/messaging/outgoing-emails.go`:
- Around line 94-97: In the three functions ResetLastSendAttemptForOutgoing,
DeleteOutgoingEmail (pkg/db/messaging/outgoing-emails.go) and
SavePasswordResetTrigger (pkg/db/participant-user/users.go) stop discarding the
error returned by bson.ObjectIDFromHex: validate the emailID/input by checking
the returned error, and if non-nil return a clear validation error to the caller
instead of building a filter with the NilObjectID; only construct the filter
(e.g., bson.M{"_id": _id}) and call UpdateOne/DeleteOne/other DB ops when
ObjectIDFromHex succeeded so malformed IDs never generate NilObjectID queries.

In `@pkg/db/participant-user/users.go`:
- Around line 201-204: The code currently ignores the error from
bson.ObjectIDFromHex(userID), which can produce a zero ObjectID and make
UpdateOne report success for no-op updates; modify the logic around
bson.ObjectIDFromHex in the function using userID (the _id variable) to check
the returned err immediately, and if non-nil return or propagate that error
instead of continuing; only build the filter (bson.M{"_id": _id}) and call
dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)
when the ObjectID parse succeeded.

In `@pkg/db/study/confidential-responses.go`:
- Around line 86-91: The local variable named options shadows the imported
options package; rename the local to something like opts or replaceOpts (e.g.,
replaceOpts := options.Replace().SetUpsert(upsert)) and update the ReplaceOne
call to pass replaceOpts so the package symbol options remains available in this
function (locate the code around ReplaceOne/collectionConfidentialResponses and
the upsert/options assignment).
- Around line 72-75: The code calls
dbService.collectionConfidentialResponses(...).InsertOne and immediately does
res.InsertedID.(bson.ObjectID) before checking err, which can panic if InsertOne
failed; update the InsertConfidentialResponse (or the function containing this
snippet) to first check if err != nil and return an empty id with the error,
then safely type-assert res.InsertedID (use the comma-ok form, e.g., idObj, ok
:= res.InsertedID.(bson.ObjectID)) and handle the unexpected type by returning a
clear error; only call idObj.Hex() when the assertion succeeds.

In `@pkg/db/study/participant-file-infos.go`:
- Around line 3-9: The DropOne loop that currently logs all errors should be
changed to treat Mongo error codes 26 (NamespaceNotFound) and 27 (IndexNotFound)
as benign no-ops: after each DropOne call, if err != nil, attempt to type-assert
to *mongo.ServerError and call HasErrorCode(26) or HasErrorCode(27); if
HasErrorCode returns true, skip logging/continue, otherwise log the error at
error-level; update the const declarations (currently defining the numeric
codes) to keep named constants for 26 and 27 and use those constants in the
HasErrorCode checks so the check is clear and only real failures produce error
logs.

In `@pkg/db/study/reports.go`:
- Around line 72-80: The DropOne error handling in the loop over
defaultReportIndexNames should ignore MongoDB error codes 26 (NamespaceNotFound)
and 27 (IndexNotFound) instead of logging them; update the error branch after
collection.Indexes().DropOne(ctx, indexName) to use errors.As to check for a
*mongo.CommandError and inspect its Code field, and only call slog.Error for
errors whose Code is not 26 or 27 (fall back to logging for non-CommandError
types); keep references to defaultReportIndexNames,
collection.Indexes().DropOne, instanceID and studyKey when logging unexpected
failures.

In `@pkg/db/study/responses.go`:
- Around line 108-111: Guard against a nil response before accessing InsertedID:
after calling dbService.collectionResponses(...).InsertOne(ctx, response) check
if err != nil and return an empty id with the error immediately instead of
unconditionally evaluating res.InsertedID; also perform a safe type/assertion
for the inserted ID (handle the correct driver type used in this diff—e.g.,
bson.ObjectID or primitive.ObjectID) so the code in collectionResponses /
InsertOne does not panic when the insert fails.

In `@services/participant-api/apihandlers/authentication.go`:
- Line 312: The call newUser.ID, _ = bson.ObjectIDFromHex(id) ignores the parse
error; change this to capture the error (idObj, err := bson.ObjectIDFromHex(id))
and handle it the same way other ObjectIDFromHex uses do in this codebase: if
err != nil, log/return an appropriate error response and stop further processing
(e.g., return a 400/invalid-id or propagate the error from the handler), then
assign newUser.ID = idObj; ensure you use the same error wrapping/logging style
as surrounding handlers to keep behavior consistent with AddUser and other
callers.

---

Outside diff comments:
In `@pkg/db/management-user/sessions.go`:
- Around line 27-48: DropIndexForSessionsCollection currently logs errors when
DropOne returns a CommandError for missing indexes; update it to ignore
mongo.CommandError with Code == 27 (IndexNotFound) by using errors.As to
type-assert to mongo.CommandError and continue instead of logging, when
iterating defaultSessionIndexNames and calling
collectionSessions(instanceID).Indexes().DropOne; to avoid duplication across
the codebase, add a helper dropIndexIfExists(ctx, indexer, indexName) in
pkg/db/utils.go that encapsulates the DropOne call and the
errors.As/mongo.CommandError Code==27 check, then call that helper from
DropIndexForSessionsCollection (and other similar DropIndexFor* functions).

In `@pkg/db/messaging/db.go`:
- Around line 33-40: Remove the pointless context.WithTimeout call and its defer
cancel in pkg/db/messaging/db.go because the returned context is discarded and
mongo.Connect no longer accepts a context; leave the mongo.Connect(...) call
as-is and rely on the existing Ping call (which already uses a proper context
timeout) to enforce connection timeout. Specifically delete the
context.WithTimeout(...) and defer cancel() lines surrounding the mongo.Connect
invocation (symbols: context.WithTimeout, defer cancel, mongo.Connect, Ping) and
run the same cleanup in the sibling DB files pkg/db/participant-user/db.go,
pkg/db/study/db.go, pkg/db/management-user/db.go, and pkg/db/global-infos/db.go.

In `@pkg/db/participant-user/otps.go`:
- Around line 76-108: The CreateOTP flow currently opens a session with
StartSession and calls mongo.WithSession but does not start a transaction, so
the CountDocuments -> InsertOne sequence (in the anonymous
createOTPIfLimitNotReached callback using collectionOTPs, CountDocuments and
InsertOne) can race and exceed maxOTPCount; change the implementation to call
session.WithTransaction(ctx, txnFunc) (or use session.StartTransaction +
session.WithTransaction pattern) and perform the CountDocuments and InsertOne
inside that transaction, or alternatively replace the check+insert with an
atomic approach (unique index or an $inc-guarded upsert) to enforce maxOTPCount
without races.

In `@pkg/db/participant-user/renew-tokens.go`:
- Line 120: Remove the unnecessary trailing nil options from Mongo delete calls:
update calls like dbService.collectionRenewTokens(instanceID).DeleteOne(ctx,
filter, nil) and any DeleteMany(ctx, filter, nil) to call the v2 driver idiom
without the nil options (e.g., DeleteOne(ctx, filter)); apply the same change to
the other locations mentioned (lines calling DeleteOne/DeleteMany and
pkg/db/messaging/outgoing-emails.go:114) so only ctx and filter are passed.

In `@pkg/db/study/researcher-messages.go`:
- Around line 65-66: The code calls
dbService.collectionResearcherMessages(...).DeleteMany and immediately returns
res.DeletedCount without checking err; change the logic in the function that
performs this DeleteMany (referencing DeleteMany, collectionResearcherMessages
and the returned res.DeleteResult) to first check if err != nil and return 0,
err (or return the error) before reading res.DeletedCount; only return
res.DeletedCount when err is nil to match error-handling used in renew-tokens.go
and responses.go.

---

Nitpick comments:
In `@go.mod`:
- Line 11: The go.mod currently pins the MongoDB driver to
go.mongodb.org/mongo-driver/v2 v2.5.0; update the version to the latest v2 patch
(v2.5.1) or add a comment documenting why v2.5.0 is intentionally pinned. Locate
the module line for go.mongodb.org/mongo-driver/v2 in go.mod and either change
the version token to v2.5.1 or add a short inline comment explaining the
intentional pin to prevent accidental upgrades.

In `@pkg/db/management-user/db.go`:
- Around line 38-45: Remove the unused timeout context allocation: eliminate the
call to context.WithTimeout(...) and the following defer cancel() since the
returned context and cancel are never used around the mongo.Connect(...) call;
keep the mongo.Connect(...) invocation as-is and ensure the existing Ping(...)
call (which creates its own context) remains untouched. Specifically, remove the
variables created by context.WithTimeout and the defer cancel near the
mongo.Connect call (symbols: context.WithTimeout, cancel, mongo.Connect,
configs) and apply the same cleanup to the identical pattern in the other DB
files that create but never use that context.

In `@pkg/db/messaging/email-templates.go`:
- Around line 119-128: The code redundantly sets emailTemplate.ID =
bson.NewObjectID() before InsertOne and then reassigns emailTemplate.ID =
res.InsertedID.(bson.ObjectID) after insert; choose one approach and remove the
dead work: either remove the pre-insert assignment (leave ID generation to the
driver and keep using res.InsertedID from InsertOne) or keep the pre-insert
bson.NewObjectID() and drop the post-insert reassignment. Update the block
around InsertOne (symbols: emailTemplate.ID, bson.NewObjectID(), InsertOne,
res.InsertedID) accordingly so only one assignment remains.

In `@pkg/db/messaging/scheduled-email.go`:
- Around line 108-114: The local variable named options shadows the imported
options package in the FindOneAndReplace call; rename the local var to opts (or
similar) to avoid shadowing so package symbols like options.After continue to
refer to the imported package. Update the chain that currently does options :=
options.FindOneAndReplace()... to use opts := options.FindOneAndReplace()... and
ensure subsequent references in this block (e.g., the call in
dbService.collectionEmailSchedules(instanceID).FindOneAndReplace(ctx, filter,
scheduledEmail, opts).Decode(&elem)) use the new opts variable.
- Around line 17-25: The no-op branch in DropIndexForEmailSchedulesCollection
currently only logs a warning when dropAll is false; change this to either
silently early-return or, preferably, surface the no-op to callers by returning
a boolean (or error) indicating “nothing to drop” so callers can react instead
of relying on logs—update the DropIndexForEmailSchedulesCollection signature and
callers accordingly, and keep logging only for unexpected errors (retain the
slog.Error in the err path and remove/replace the slog.Warn in the no-op
branch); refer to DropIndexForEmailSchedulesCollection and
dbService.collectionEmailSchedules(instanceID) when locating the code to change.

In `@pkg/db/study/confidential-responses.go`:
- Around line 106-110: The Find call passing an explicit nil as the variadic
opts is non-idiomatic and can produce a one-element slice with a nil interface;
update the call to collectionConfidentialResponses(instanceID,
studyKey).Find(ctx, filter) (i.e., remove the trailing nil variadic argument) so
no nil entry is created; also scan for other usages of
collectionConfidentialResponses(...).Find or similar Collection.Find calls and
remove explicit nil variadic opts to rely on the zero-argument form.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cfbc1fe3-622c-45f1-9bd2-c7a9bba98831

📥 Commits

Reviewing files that changed from the base of the PR and between 08cdbde and 70507e4.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (87)
  • .gitignore
  • go.mod
  • jobs/db-migration/main.go
  • jobs/messaging/participant-messages.go
  • jobs/messaging/scheduled-messages.go
  • jobs/study-daily-data-export/response-exporter.go
  • jobs/study-timer/main.go
  • jobs/user-management/generate-id-lookup.go
  • jobs/user-management/main.go
  • pkg/apihelpers/parse-paginated-query.go
  • pkg/db/global-infos/blocked-jwts.go
  • pkg/db/global-infos/db.go
  • pkg/db/global-infos/temptoken.go
  • pkg/db/management-user/app-roles.go
  • pkg/db/management-user/db.go
  • pkg/db/management-user/management-users.go
  • pkg/db/management-user/permissions.go
  • pkg/db/management-user/service-users.go
  • pkg/db/management-user/sessions.go
  • pkg/db/management-user/types.go
  • pkg/db/messaging/db.go
  • pkg/db/messaging/email-templates.go
  • pkg/db/messaging/outgoing-emails.go
  • pkg/db/messaging/scheduled-email.go
  • pkg/db/messaging/sent-emails.go
  • pkg/db/messaging/sent-sms.go
  • pkg/db/messaging/sms-templates.go
  • pkg/db/participant-user/db.go
  • pkg/db/participant-user/failedOtpAttempts.go
  • pkg/db/participant-user/otps.go
  • pkg/db/participant-user/renew-tokens.go
  • pkg/db/participant-user/user-attributes.go
  • pkg/db/participant-user/users.go
  • pkg/db/study/confidential-id-map.go
  • pkg/db/study/confidential-responses.go
  • pkg/db/study/db.go
  • pkg/db/study/participant-file-infos.go
  • pkg/db/study/participants.go
  • pkg/db/study/reports.go
  • pkg/db/study/researcher-messages.go
  • pkg/db/study/responses.go
  • pkg/db/study/study-code-lists.go
  • pkg/db/study/study-counters.go
  • pkg/db/study/study-infos.go
  • pkg/db/study/study-rules.go
  • pkg/db/study/study-variables.go
  • pkg/db/study/surveys.go
  • pkg/db/study/task-queue.go
  • pkg/db/utils.go
  • pkg/messaging/types/email-template.go
  • pkg/messaging/types/outgoing_email.go
  • pkg/messaging/types/scheduledEmails.go
  • pkg/messaging/types/sms.go
  • pkg/study/participant-data.go
  • pkg/study/study-service.go
  • pkg/study/studyengine/actions.go
  • pkg/study/studyengine/expressions.go
  • pkg/study/studyengine/expressions_test.go
  • pkg/study/studyengine/types.go
  • pkg/study/types/assigned-survey.go
  • pkg/study/types/localised-object.go
  • pkg/study/types/participant-file-info.go
  • pkg/study/types/participant.go
  • pkg/study/types/report.go
  • pkg/study/types/study-code-list.go
  • pkg/study/types/study-message.go
  • pkg/study/types/study-rules.go
  • pkg/study/types/study-variables.go
  • pkg/study/types/study.go
  • pkg/study/types/survey-item.go
  • pkg/study/types/survey-response.go
  • pkg/study/types/survey.go
  • pkg/study/types/task.go
  • pkg/study/utils/participantID_test.go
  • pkg/user-management/types/contact-info.go
  • pkg/user-management/types/profile.go
  • pkg/user-management/types/temptoken.go
  • pkg/user-management/types/user-attributes.go
  • pkg/user-management/types/user.go
  • pkg/user-management/utils/new-user-init.go
  • pkg/user-management/utils/profiles_test.go
  • services/management-api/apihandlers/study-management.go
  • services/participant-api/apihandlers/authentication.go
  • services/participant-api/apihandlers/password-reset.go
  • services/participant-api/apihandlers/study-service.go
  • services/participant-api/apihandlers/user-management.go
  • test/mongodb-driver-update.md

Comment on lines +94 to 97
_id, _ := bson.ObjectIDFromHex(emailID)
filter := bson.M{"_id": _id}
update := bson.M{"$set": bson.M{"lastSendAttempt": 0}}
res, err := dbService.collectionOutgoingEmails(instanceID).UpdateOne(ctx, filter, update)

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '(_id|[a-zA-Z0-9_]+)\s*,\s*_\s*:=\s*bson\.ObjectIDFromHex\(' -C2

Repository: case-framework/case-backend

Length of output: 1058


🏁 Script executed:

# Read the exact code from the file to confirm the issue
head -120 pkg/db/messaging/outgoing-emails.go | tail -35

Repository: case-framework/case-backend

Length of output: 1058


🏁 Script executed:

# Check the context around the users.go instance
head -210 pkg/db/participant-user/users.go | tail -20

Repository: case-framework/case-backend

Length of output: 645


🏁 Script executed:

# Verify if there are any other instances not caught by the regex
rg 'ObjectIDFromHex' -A 1 --no-heading

Repository: case-framework/case-backend

Length of output: 7123


Handle invalid ObjectID input before building the Mongo filter.

bson.ObjectIDFromHex errors are currently discarded in three locations, allowing malformed IDs to convert into NilObjectID filters. Return a validation error instead of issuing operations with invalid IDs.

Affected functions:

  • ResetLastSendAttemptForOutgoing (pkg/db/messaging/outgoing-emails.go:94)
  • DeleteOutgoingEmail (pkg/db/messaging/outgoing-emails.go:111)
  • SavePasswordResetTrigger (pkg/db/participant-user/users.go:201)
🛡️ Proposed fixes
 func (dbService *MessagingDBService) ResetLastSendAttemptForOutgoing(instanceID string, emailID string) error {
 	ctx, cancel := dbService.getContext()
 	defer cancel()
 
-	_id, _ := bson.ObjectIDFromHex(emailID)
+	_id, err := bson.ObjectIDFromHex(emailID)
+	if err != nil {
+		return errors.New("invalid outgoing email id")
+	}
 	filter := bson.M{"_id": _id}
 	update := bson.M{"$set": bson.M{"lastSendAttempt": 0}}
 	res, err := dbService.collectionOutgoingEmails(instanceID).UpdateOne(ctx, filter, update)
 func (dbService *MessagingDBService) DeleteOutgoingEmail(instanceID string, id string) error {
 	ctx, cancel := dbService.getContext()
 	defer cancel()
 
-	_id, _ := bson.ObjectIDFromHex(id)
+	_id, err := bson.ObjectIDFromHex(id)
+	if err != nil {
+		return errors.New("invalid outgoing email id")
+	}
 	filter := bson.M{"_id": _id}
 func (dbService *ParticipantUserDBService) SavePasswordResetTrigger(instanceID string, userID string) error {
 	ctx, cancel := dbService.getContext()
 	defer cancel()
 
-	_id, _ := bson.ObjectIDFromHex(userID)
+	_id, err := bson.ObjectIDFromHex(userID)
+	if err != nil {
+		return errors.New("invalid user id")
+	}
 	filter := bson.M{"_id": _id}
📝 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
_id, _ := bson.ObjectIDFromHex(emailID)
filter := bson.M{"_id": _id}
update := bson.M{"$set": bson.M{"lastSendAttempt": 0}}
res, err := dbService.collectionOutgoingEmails(instanceID).UpdateOne(ctx, filter, update)
_id, err := bson.ObjectIDFromHex(emailID)
if err != nil {
return errors.New("invalid outgoing email id")
}
filter := bson.M{"_id": _id}
update := bson.M{"$set": bson.M{"lastSendAttempt": 0}}
res, err := dbService.collectionOutgoingEmails(instanceID).UpdateOne(ctx, filter, update)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/messaging/outgoing-emails.go` around lines 94 - 97, In the three
functions ResetLastSendAttemptForOutgoing, DeleteOutgoingEmail
(pkg/db/messaging/outgoing-emails.go) and SavePasswordResetTrigger
(pkg/db/participant-user/users.go) stop discarding the error returned by
bson.ObjectIDFromHex: validate the emailID/input by checking the returned error,
and if non-nil return a clear validation error to the caller instead of building
a filter with the NilObjectID; only construct the filter (e.g., bson.M{"_id":
_id}) and call UpdateOne/DeleteOne/other DB ops when ObjectIDFromHex succeeded
so malformed IDs never generate NilObjectID queries.

Comment on lines +201 to 204
_id, _ := bson.ObjectIDFromHex(userID)
filter := bson.M{"_id": _id}
update := bson.M{"$push": bson.M{"account.passwordResetTriggers": time.Now().Unix()}}
_, err := dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)

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

Return the ObjectID parse error instead of silently using the zero ID.

Line 201 ignores ObjectIDFromHex errors, so malformed userID values can become _id: ObjectID("000...") and report success after updating nothing.

🐛 Proposed fix
-	_id, _ := bson.ObjectIDFromHex(userID)
+	_id, err := bson.ObjectIDFromHex(userID)
+	if err != nil {
+		return err
+	}
 	filter := bson.M{"_id": _id}
 	update := bson.M{"$push": bson.M{"account.passwordResetTriggers": time.Now().Unix()}}
-	_, err := dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)
+	_, err = dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)
📝 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
_id, _ := bson.ObjectIDFromHex(userID)
filter := bson.M{"_id": _id}
update := bson.M{"$push": bson.M{"account.passwordResetTriggers": time.Now().Unix()}}
_, err := dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)
_id, err := bson.ObjectIDFromHex(userID)
if err != nil {
return err
}
filter := bson.M{"_id": _id}
update := bson.M{"$push": bson.M{"account.passwordResetTriggers": time.Now().Unix()}}
_, err = dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/participant-user/users.go` around lines 201 - 204, The code currently
ignores the error from bson.ObjectIDFromHex(userID), which can produce a zero
ObjectID and make UpdateOne report success for no-op updates; modify the logic
around bson.ObjectIDFromHex in the function using userID (the _id variable) to
check the returned err immediately, and if non-nil return or propagate that
error instead of continuing; only build the filter (bson.M{"_id": _id}) and call
dbService.collectionParticipantUsers(instanceID).UpdateOne(ctx, filter, update)
when the ObjectID parse succeeded.

Comment thread pkg/db/study/confidential-responses.go
Comment thread pkg/db/study/confidential-responses.go Outdated
Comment thread pkg/db/study/participant-file-infos.go
Comment thread pkg/db/study/reports.go
Comment thread pkg/db/study/responses.go

}
newUser.ID, _ = primitive.ObjectIDFromHex(id)
newUser.ID, _ = bson.ObjectIDFromHex(id)

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

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the authentication.go file
fd -t f "authentication.go" | head -5

Repository: case-framework/case-backend

Length of output: 125


🏁 Script executed:

# Find the file and check its line count
find . -name "authentication.go" -path "*/participant-api/apihandlers/*" -type f

Repository: case-framework/case-backend

Length of output: 127


🏁 Script executed:

# Get the file size and read around line 312
wc -l ./services/participant-api/apihandlers/authentication.go

Repository: case-framework/case-backend

Length of output: 132


🏁 Script executed:

# Read lines around 312 to see the context
sed -n '300,340p' ./services/participant-api/apihandlers/authentication.go

Repository: case-framework/case-backend

Length of output: 1242


🏁 Script executed:

# Search for the AddUser method to understand what it returns
rg "func.*AddUser" --type go -A 5 | head -40

Repository: case-framework/case-backend

Length of output: 1731


🏁 Script executed:

# Check for other usages of ObjectIDFromHex in the codebase to see error handling patterns
rg "ObjectIDFromHex" --type go -B 2 -A 2 | head -60

Repository: case-framework/case-backend

Length of output: 3160


🏁 Script executed:

# Get more of AddUser implementation
sed -n '1,50p' ./pkg/db/participant-user/users.go | head -30

Repository: case-framework/case-backend

Length of output: 1021


🏁 Script executed:

# Find the AddUser function and see full implementation
rg -A 30 "func.*ParticipantUserDBService.*AddUser" ./pkg/db/participant-user/users.go

Repository: case-framework/case-backend

Length of output: 896


🏁 Script executed:

# Verify if there's any documentation or type hints about AddUser return format
rg -B 5 "func.*AddUser.*string.*error" ./pkg/db/participant-user/users.go

Repository: case-framework/case-backend

Length of output: 322


🏁 Script executed:

# Check if there are any tests for AddUser that show expected behavior
fd -t f "*test*.go" ./pkg/db/participant-user/ -x grep -l "AddUser" {}

Repository: case-framework/case-backend

Length of output: 359


🏁 Script executed:

# Search for tests with correct glob syntax
fd --glob "*test.go" ./pkg/db/participant-user/ -x grep -l "AddUser" {}

Repository: case-framework/case-backend

Length of output: 53


🏁 Script executed:

# Also check if AddUser is tested inline or in another location
rg "AddUser" --type go -l ./pkg/db/participant-user/ | grep -i test

Repository: case-framework/case-backend

Length of output: 53


🏁 Script executed:

# Check what zero ObjectID.Hex() produces and verify downstream impact
# Look at the email verification and JWT generation functions
rg -A 10 "prepAndSendEmailVerification" ./services/participant-api/apihandlers/authentication.go | head -20

Repository: case-framework/case-backend

Length of output: 561


🏁 Script executed:

# Check GenerateNewParticipantUserToken to see if it validates the ID
rg "GenerateNewParticipantUserToken" --type go -B 2 -A 8 | head -40

Repository: case-framework/case-backend

Length of output: 2809


Add error handling for ObjectIDFromHex to match codebase patterns.

Line 312 discards the parse error from bson.ObjectIDFromHex(id). While AddUser returns a valid hex string (derived directly from MongoDB's ObjectID.Hex()), this is the only place in the codebase where this error is ignored. All other ObjectIDFromHex calls check and handle the error. This inconsistency should be corrected for consistency, defensive programming, and to prevent silent failures if the source of id changes.

Proposed fix
-	newUser.ID, _ = bson.ObjectIDFromHex(id)
+	newUser.ID, err = bson.ObjectIDFromHex(id)
+	if err != nil {
+		slog.Error("failed to parse new user id", slog.String("id", id), slog.String("error", err.Error()))
+		c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
+		return
+	}
📝 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
newUser.ID, _ = bson.ObjectIDFromHex(id)
newUser.ID, err = bson.ObjectIDFromHex(id)
if err != nil {
slog.Error("failed to parse new user id", slog.String("id", id), slog.String("error", err.Error()))
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/participant-api/apihandlers/authentication.go` at line 312, The call
newUser.ID, _ = bson.ObjectIDFromHex(id) ignores the parse error; change this to
capture the error (idObj, err := bson.ObjectIDFromHex(id)) and handle it the
same way other ObjectIDFromHex uses do in this codebase: if err != nil,
log/return an appropriate error response and stop further processing (e.g.,
return a 400/invalid-id or propagate the error from the handler), then assign
newUser.ID = idObj; ensure you use the same error wrapping/logging style as
surrounding handlers to keep behavior consistent with AddUser and other callers.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
pkg/db/study/confidential-responses.go (1)

72-74: ⚠️ Potential issue | 🟠 Major

Check InsertOne errors before reading InsertedID.

Line 73 still dereferences res before Line 74 returns err; if InsertOne fails, this can panic instead of returning the database error. This is the same unresolved issue noted earlier.

🐛 Proposed fix
 	res, err := dbService.collectionConfidentialResponses(instanceID, studyKey).InsertOne(ctx, response)
-	id := res.InsertedID.(bson.ObjectID)
-	return id.Hex(), err
+	if err != nil {
+		return "", err
+	}
+	id, ok := res.InsertedID.(bson.ObjectID)
+	if !ok {
+		return "", errors.New("unexpected InsertedID type")
+	}
+	return id.Hex(), nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/confidential-responses.go` around lines 72 - 74, The InsertOne
call's result is dereferenced before checking for errors; in the method using
dbService.collectionConfidentialResponses(...).InsertOne(ctx, response) you must
check err immediately after the call and return the error (e.g., return "", err)
if non-nil, only then access res.InsertedID and convert it to a hex string;
update the code around the InsertOne call so you don't do
res.InsertedID.(bson.ObjectID) when err != nil to avoid a panic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/db/study/confidential-responses.go`:
- Around line 72-74: The InsertOne call's result is dereferenced before checking
for errors; in the method using
dbService.collectionConfidentialResponses(...).InsertOne(ctx, response) you must
check err immediately after the call and return the error (e.g., return "", err)
if non-nil, only then access res.InsertedID and convert it to a hex string;
update the code around the InsertOne call so you don't do
res.InsertedID.(bson.ObjectID) when err != nil to avoid a panic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60306ab1-9d43-458e-a971-89ce333c9978

📥 Commits

Reviewing files that changed from the base of the PR and between 70507e4 and 986e3b2.

📒 Files selected for processing (1)
  • pkg/db/study/confidential-responses.go

Comment thread pkg/db/study/responses.go Outdated
Comment thread pkg/db/messaging/outgoing-emails.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
pkg/db/study/reports.go (1)

72-76: Dead empty-name guard.

defaultReportIndexNames is a package-level slice of non-empty string constants, so indexName == "" can never be true here. The check (and its error log) can be removed to simplify the loop.

♻️ Proposed simplification
 		for _, indexName := range defaultReportIndexNames {
-			if indexName == "" {
-				slog.Error("Index name is empty for reports collection")
-				continue
-			}
 			err := collection.Indexes().DropOne(ctx, indexName)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/reports.go` around lines 72 - 76, The empty-name guard inside
the loop over defaultReportIndexNames is dead code because
defaultReportIndexNames contains non-empty constants; remove the conditional
that checks indexName == "" and the accompanying slog.Error call to simplify the
loop in the code that builds indexes for the reports collection (the loop
iterating over defaultReportIndexNames in reports.go).
pkg/db/messaging/scheduled-email.go (1)

101-125: LGTM — v2 options builder and bson.ObjectID usage.

The fluent options.FindOneAndReplace().SetUpsert(false).SetReturnDocument(options.After) and bson.NewObjectID() / res.InsertedID.(bson.ObjectID) cast all match the v2 API surface. Call sites (e.g., jobs/messaging/scheduled-messages.go:56-58) already consume the (ScheduledEmail, error) return shape unchanged, so this migration is transparent to callers.

Minor nit (optional): scheduledEmail.ID = bson.NewObjectID() on line 117 is redundant because it's immediately overwritten by the InsertedID cast on line 122. You could drop one of the two assignments, but behavior is equivalent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/messaging/scheduled-email.go` around lines 101 - 125, Remove the
redundant temporary ID assignment in SaveScheduledEmail: the call
scheduledEmail.ID = bson.NewObjectID() before InsertOne is unnecessary because
the inserted ID is read back from res.InsertedID and assigned to
scheduledEmail.ID; delete the bson.NewObjectID() assignment in the else branch
of MessagingDBService.SaveScheduledEmail so only the InsertOne result is used to
set scheduledEmail.ID.
pkg/db/global-infos/db.go (1)

39-45: Minor: defer conCancel() placed after Ping.

Convention is to register defer cancel() immediately after context.WithTimeout so the cancel is guaranteed to run even if the intervening call panics. Here Ping is synchronous and can't actually leave the timer dangling, so it's not a real bug — just a readability nit.

♻️ Suggested reorder
-	ctx, conCancel := context.WithTimeout(context.Background(), time.Duration(configs.Timeout)*time.Second)
-	err = dbClient.Ping(ctx, nil)
-	defer conCancel()
+	ctx, conCancel := context.WithTimeout(context.Background(), time.Duration(configs.Timeout)*time.Second)
+	defer conCancel()
+	err = dbClient.Ping(ctx, nil)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/global-infos/db.go` around lines 39 - 45, Move the defer call to
ensure the cancel runs even if a later call panics: after creating the context
with context.WithTimeout (the ctx, conCancel := context.WithTimeout(...) line)
immediately add defer conCancel() and then call dbClient.Ping(ctx, nil); this
updates the ordering around ctx/conCancel and dbClient.Ping to register the
cancel right away.
test/mongodb-driver-update.md (1)

376-376: Optional style adjustment (split infinitive).

Static analysis flagged "used to manually assign" as a split infinitive. While grammatically acceptable in modern technical writing, you could optionally rephrase to "used to assign a survey manually" if you prefer to avoid split infinitives.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/mongodb-driver-update.md` at line 376, Replace the phrase "used to
manually assign a survey to a participant" in the sentence "Opened the CASE
management UI and checked the survey dropdown used to manually assign a survey
to a participant – verified that all available survey keys were listed
correctly." with a non–split-infinitive phrasing such as "used to assign a
survey manually to a participant" (or "used to assign a survey manually") so the
sentence reads without the split infinitive while preserving the original
meaning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/mongodb-driver-update.md`:
- Around line 1-73: Update all incorrect type identifiers: replace occurrences
of primitive.ObjectID and the mistaken bson.ObjectID with the correct MongoDB Go
v2 type name bson.ObjectId throughout the codebase (any code that previously
migrated primitive.ObjectID → bson.ObjectID must instead use bson.ObjectId).
Search for uses in constructors, struct fields, function signatures and
conversions (e.g., places referenced by names like SaveParticipantState,
GetSurveyVersions, ReplaceConfidentialResponse, AddUser, CreateOTP) and update
imports if necessary so the bson package is used where these types appear.

---

Nitpick comments:
In `@pkg/db/global-infos/db.go`:
- Around line 39-45: Move the defer call to ensure the cancel runs even if a
later call panics: after creating the context with context.WithTimeout (the ctx,
conCancel := context.WithTimeout(...) line) immediately add defer conCancel()
and then call dbClient.Ping(ctx, nil); this updates the ordering around
ctx/conCancel and dbClient.Ping to register the cancel right away.

In `@pkg/db/messaging/scheduled-email.go`:
- Around line 101-125: Remove the redundant temporary ID assignment in
SaveScheduledEmail: the call scheduledEmail.ID = bson.NewObjectID() before
InsertOne is unnecessary because the inserted ID is read back from
res.InsertedID and assigned to scheduledEmail.ID; delete the bson.NewObjectID()
assignment in the else branch of MessagingDBService.SaveScheduledEmail so only
the InsertOne result is used to set scheduledEmail.ID.

In `@pkg/db/study/reports.go`:
- Around line 72-76: The empty-name guard inside the loop over
defaultReportIndexNames is dead code because defaultReportIndexNames contains
non-empty constants; remove the conditional that checks indexName == "" and the
accompanying slog.Error call to simplify the loop in the code that builds
indexes for the reports collection (the loop iterating over
defaultReportIndexNames in reports.go).

In `@test/mongodb-driver-update.md`:
- Line 376: Replace the phrase "used to manually assign a survey to a
participant" in the sentence "Opened the CASE management UI and checked the
survey dropdown used to manually assign a survey to a participant – verified
that all available survey keys were listed correctly." with a
non–split-infinitive phrasing such as "used to assign a survey manually to a
participant" (or "used to assign a survey manually") so the sentence reads
without the split infinitive while preserving the original meaning.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 889b2009-6500-48ea-b1a5-18bb1eb68e85

📥 Commits

Reviewing files that changed from the base of the PR and between 986e3b2 and fe7ea79.

📒 Files selected for processing (10)
  • pkg/db/global-infos/db.go
  • pkg/db/management-user/db.go
  • pkg/db/messaging/db.go
  • pkg/db/messaging/scheduled-email.go
  • pkg/db/participant-user/db.go
  • pkg/db/participant-user/renew-tokens.go
  • pkg/db/study/confidential-responses.go
  • pkg/db/study/db.go
  • pkg/db/study/reports.go
  • test/mongodb-driver-update.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/db/study/db.go
  • pkg/db/participant-user/db.go
  • pkg/db/participant-user/renew-tokens.go

Comment thread test/mongodb-driver-update.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
pkg/db/study/confidential-responses.go (1)

42-46: Dead-code guard on hardcoded constants.

Since defaultConfidentialResponseIndexNames is populated exclusively from the non-empty constant idxConfidentialResponsesParticipantIDKey, the if indexName == "" branch is unreachable. Safe to drop for clarity (or keep if you want symmetry with other collections using the same template).

♻️ Proposed simplification
 		for _, indexName := range defaultConfidentialResponseIndexNames {
-			if indexName == "" {
-				slog.Error("Index name is empty for confidential responses collection")
-				continue
-			}
 			err := collection.Indexes().DropOne(ctx, indexName)
 			if err != nil {
 				slog.Error("Error dropping index for confidential responses", slog.String("error", err.Error()), slog.String("instanceID", instanceID), slog.String("studyKey", studyKey), slog.String("indexName", indexName))
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/confidential-responses.go` around lines 42 - 46, The loop over
defaultConfidentialResponseIndexNames contains an unreachable guard checking
indexName == "" because the slice is built only from the non-empty constant
idxConfidentialResponsesParticipantIDKey; remove the dead if branch and its
slog.Error call to simplify the code in the loop that constructs/confirms
indexes (the loop using defaultConfidentialResponseIndexNames), or alternatively
leave a short comment explaining symmetry if you prefer to keep it—either remove
the unreachable check or replace it with a clarifying comment.
pkg/db/study/responses.go (2)

284-288: Nit: final return err is always nil here (and in DeleteResponses at line 318).

err has already been checked and returned on the error path above, and the DeletedCount == 0 branch returns mongo.ErrNoDocuments directly, so the trailing return err is guaranteed to be nil. Prefer an explicit return nil to make intent unambiguous; the same applies to DeleteResponses (line 318).

🧹 Proposed tidy-up
 	if res.DeletedCount == 0 {
 		return mongo.ErrNoDocuments
 	}
-
-	return err
+	return nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/responses.go` around lines 284 - 288, The final "return err" is
misleading because err is always nil at that point; update the functions
DeleteResponse (the block checking res.DeletedCount == 0) and DeleteResponses to
explicitly return nil instead of returning err so the intent is clear and there
is no confusion about returning a non-nil error value.

77-86: Remove dead empty-name guard in DropIndexForResponsesCollection.

Every entry in defaultResponseIndexNames is a non-empty string constant, so the if indexName == "" check at lines 78–81 is unreachable. Remove it.

The v2 driver signatures for IndexView.DropOne and IndexView.DropAll have been confirmed: both return error only (no bson.Raw), matching the code's usage at lines 72 and 82.

Proposed cleanup
 	} else {
 		for _, indexName := range defaultResponseIndexNames {
-			if indexName == "" {
-				slog.Error("Index name is empty for responses collection")
-				continue
-			}
 			err := dbService.collectionResponses(instanceID, studyKey).Indexes().DropOne(ctx, indexName)
 			if err != nil {
 				slog.Error("Error dropping index for responses", slog.String("error", err.Error()), slog.String("instanceID", instanceID), slog.String("studyKey", studyKey), slog.String("indexName", indexName))
 			}
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/study/responses.go` around lines 77 - 86, Remove the unreachable
empty-name guard in DropIndexForResponsesCollection: delete the if indexName ==
"" { slog.Error(...); continue } block inside the loop over
defaultResponseIndexNames and leave the existing call to
dbService.collectionResponses(instanceID, studyKey).Indexes().DropOne(ctx,
indexName) and its error logging intact; this keeps behavior unchanged while
removing dead code around the defaultResponseIndexNames check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/db/study/confidential-responses.go`:
- Around line 42-46: The loop over defaultConfidentialResponseIndexNames
contains an unreachable guard checking indexName == "" because the slice is
built only from the non-empty constant idxConfidentialResponsesParticipantIDKey;
remove the dead if branch and its slog.Error call to simplify the code in the
loop that constructs/confirms indexes (the loop using
defaultConfidentialResponseIndexNames), or alternatively leave a short comment
explaining symmetry if you prefer to keep it—either remove the unreachable check
or replace it with a clarifying comment.

In `@pkg/db/study/responses.go`:
- Around line 284-288: The final "return err" is misleading because err is
always nil at that point; update the functions DeleteResponse (the block
checking res.DeletedCount == 0) and DeleteResponses to explicitly return nil
instead of returning err so the intent is clear and there is no confusion about
returning a non-nil error value.
- Around line 77-86: Remove the unreachable empty-name guard in
DropIndexForResponsesCollection: delete the if indexName == "" {
slog.Error(...); continue } block inside the loop over defaultResponseIndexNames
and leave the existing call to dbService.collectionResponses(instanceID,
studyKey).Indexes().DropOne(ctx, indexName) and its error logging intact; this
keeps behavior unchanged while removing dead code around the
defaultResponseIndexNames check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a795838-0104-495e-8722-208c0b9c810d

📥 Commits

Reviewing files that changed from the base of the PR and between fe7ea79 and 0800e7e.

📒 Files selected for processing (3)
  • pkg/db/messaging/outgoing-emails.go
  • pkg/db/study/confidential-responses.go
  • pkg/db/study/responses.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
test/mongodb-driver-update.md (1)

7-10: ⚠️ Potential issue | 🟠 Major

Fix MongoDB Go v2 BSON ObjectId casing in migration notes.

Your doc says: “primitive.ObjectID → bson.ObjectID”, but the Go driver v2 type is bson.ObjectId (capital O, lowercase “id”). This casing mistake can mislead implementers and cause compilation failures if followed. citeturn0search0

🛠️ Proposed doc-only fix
- - **primitive package**: The bson/primitive package has been merged into the bson package —> changed any instance of primitive.ObjectID to bson.ObjectID.
+ - **primitive package**: The bson/primitive package has been merged into the bson package —> changed any instance of primitive.ObjectID to bson.ObjectId.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/mongodb-driver-update.md` around lines 7 - 10, The migration note
incorrectly shows the Go BSON type as "bson.ObjectID"; update the doc to use the
correct Go driver v2 casing "bson.ObjectId" (and show the original
"primitive.ObjectID" → "bson.ObjectId" mapping) so readers don’t follow a typo
that causes compile errors; search for occurrences of "primitive.ObjectID" and
"bson.ObjectID" in the doc and replace the latter with "bson.ObjectId" while
leaving the explanatory text intact.
🧹 Nitpick comments (1)
test/mongodb-driver-update.md (1)

372-377: Optional readability: simplify the “Opened … – verified …” sentence.

The GetSurveyKeysForStudy “Scope/Test execution” bullet is a bit clunky with the dash + “verified …”. Consider a smoother phrasing for readability.

💡 Suggested wording
- 1. Opened the CASE management UI and checked the survey dropdown used to manually assign a survey to a participant – verified that all available survey keys were listed correctly.
+ 1. Opened the CASE management UI and checked the survey dropdown used to manually assign a survey to a participant, verifying that all available survey keys were listed correctly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/mongodb-driver-update.md` around lines 372 - 377, Simplify the clunky
“Opened … – verified …” sentence under the GetSurveyKeysForStudy Scope/Test
execution bullet: replace the dash+verification phrasing with a single smoother
sentence such as “Opened the CASE management UI and confirmed the survey
dropdown used to manually assign a survey to a participant lists all available
survey keys.” Keep the second step about exporting the study configuration
unchanged; update the Scope/Test execution bullet for readability only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@test/mongodb-driver-update.md`:
- Around line 7-10: The migration note incorrectly shows the Go BSON type as
"bson.ObjectID"; update the doc to use the correct Go driver v2 casing
"bson.ObjectId" (and show the original "primitive.ObjectID" → "bson.ObjectId"
mapping) so readers don’t follow a typo that causes compile errors; search for
occurrences of "primitive.ObjectID" and "bson.ObjectID" in the doc and replace
the latter with "bson.ObjectId" while leaving the explanatory text intact.

---

Nitpick comments:
In `@test/mongodb-driver-update.md`:
- Around line 372-377: Simplify the clunky “Opened … – verified …” sentence
under the GetSurveyKeysForStudy Scope/Test execution bullet: replace the
dash+verification phrasing with a single smoother sentence such as “Opened the
CASE management UI and confirmed the survey dropdown used to manually assign a
survey to a participant lists all available survey keys.” Keep the second step
about exporting the study configuration unchanged; update the Scope/Test
execution bullet for readability only.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6f29b1b-669c-41ca-8f4e-d02a51ab2c9f

📥 Commits

Reviewing files that changed from the base of the PR and between 0800e7e and 456f2e1.

📒 Files selected for processing (1)
  • test/mongodb-driver-update.md

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.

1 participant