Skip to content

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Oct 23, 2025

What does this PR do?

This PR simplifies casting aggregated json data to concrete types. Also, replaces []byte override with json.RawMessage because it represents our JSON Db columns better. Regarding dynamically casting interface{} to associated type, I tried to override them in sqlc.json, but since those values are aggregated and not really a column in the db, override fails. I also tried to do plugin but cons are overweighed the pros and finally I tried to do replace them using a script then I ended up with too much magic and very error prone code. UnmarshalJSONArray this is the most idiomatic way to solve this.

https://linear.app/unkey/issue/ENG-1991/improve-type-safety-for-json-columns-in-database-structs

Fixes #3789

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Make sure tests are passing

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@linear
Copy link

linear bot commented Oct 23, 2025

@changeset-bot
Copy link

changeset-bot bot commented Oct 23, 2025

⚠️ No Changeset found

Latest commit: b4c29d6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Oct 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
dashboard Ignored Ignored Preview Oct 28, 2025 1:46pm
engineering Ignored Ignored Preview Oct 28, 2025 1:46pm

@ogzhanolguncu ogzhanolguncu changed the title refactor: make marshalling easier and override json to RawMessage refactor: make unmarshalling easier and override json to RawMessage Oct 23, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

📝 Walkthrough

Walkthrough

Adds a generic helper UnmarshalNullableJSONTo[T any] to centralize defensive JSON decoding and threads a logger through KeyData construction; replaces many inline json.Unmarshal calls in DB and API handlers with the helper and updates ToKeyData and its callers to accept a logger parameter.

Changes

Cohort / File(s) Summary
JSON helper
go/pkg/db/custom_types.go
Added exported generic UnmarshalNullableJSONTo[T any](data any, logger logging.Logger) T that accepts any, handles nil/non-[]byte inputs, logs warnings/errors, and unmarshals JSON bytes into T returning the zero value on problems.
DB: KeyData construction
go/pkg/db/key_data.go
Updated ToKeyData signature to accept logger logging.Logger and added logger to buildKeyData* helpers; replaced inline json.Unmarshal for roles, permissions, role_permissions, ratelimits with UnmarshalNullableJSONTo[...] calls; removed encoding/json import and added logging import.
API handlers: meta decoding & ToKeyData callers
go/apps/api/routes/.../handler.go
go/apps/api/routes/v2_apis_list_keys/handler.go, .../v2_identities_get_identity/handler.go, .../v2_identities_list_identities/handler.go, .../v2_keys_get_key/handler.go, .../v2_keys_verify_key/handler.go, .../v2_keys_whoami/handler.go
Replaced per-site json.Unmarshal of identity/key meta with db.UnmarshalNullableJSONTo[map[string]any](..., h.Logger) and updated db.ToKeyData call sites to pass h.Logger; small formatting/type-alias grouping changes in several handlers.
Tests: adapt ToKeyData and meta unmarshalling
go/apps/api/routes/v2_identities_create_identity/200_test.go, go/apps/api/routes/v2_keys_reroll_key/200_test.go, go/pkg/db/key_data_test.go
Replaced json.Unmarshal usages in tests with db.UnmarshalNullableJSONTo[...] and updated ToKeyData call sites to pass nil for the new logger parameter where applicable.
Test util rename
go/pkg/testutil/seed/seed.go
Renamed local variable identityIdidentityID inside CreateIdentity; updated usages; no behavioral change.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant API as API Handler
  participant DB as db package
  rect rgba(214,234,248,0.6)
    API->>DB: Call ToKeyData(row, logger)
    DB->>DB: For each JSON column\ncall UnmarshalNullableJSONTo[T](field, logger)
    DB-->>API: Return KeyData (decoded fields)
  end
  rect rgba(233,245,227,0.6)
    API->>DB: UnmarshalNullableJSONTo[map[string]any](metaBytes, logger)
    DB-->>API: map[string]any or zero-value (logs on issues)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review go/pkg/db/custom_types.go for correct nil/non-[]byte handling and logging behavior.
  • Verify go/pkg/db/key_data.go uses the helper for all JSON-scanned fields without losing data.
  • Check API handler call sites for correct logger propagation and test behavior when nil is passed.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "refactor: make unmarshalling easier and override json to RawMessage" captures the primary objective of the pull request. The first part—"make unmarshalling easier"—directly reflects the main change: introducing the UnmarshalNullableJSONTo helper function and applying it throughout the codebase to simplify JSON unmarshalling. The second part about RawMessage refers to the design approach of using compatible types (the function works with []byte and any types, addressing JSON data handling). The title accurately summarizes the refactoring intent even though the actual implementation uses a helper function with []byte rather than fully transitioning to json.RawMessage due to technical constraints noted in the PR description.
Linked Issues Check ✅ Passed The PR implementation directly addresses the objectives defined in issue #3789 by solving the type safety problem for JSON columns in database structs. The changes introduce UnmarshalNullableJSONTo[T any] to provide type-safe unmarshalling of JSON data, and apply this helper to exactly the affected fields mentioned in the issue (roles, permissions, role_permissions, ratelimits) across the database package and handler files. The solution improves ergonomics and reduces runtime type assertion errors by centralizing JSON unmarshalling logic, which aligns with the desired outcome of making JSON decoding more ergonomic and type-safe. All call sites that were manually unmarshalling JSON data have been updated to use the new helper function.

[pre_merge_check_pass]

The implementation successfully translates the requirements from issue #3789 into working code without requiring RawMessage or sqlc plugin modifications, making it a practical solution to the underlying problem.

The vast majority of changes are directly related to the PR's stated objectives of simplifying JSON unmarshalling and improving type safety. All modifications to database packages and handler files involve either introducing the UnmarshalNullableJSONTo helper or updating call sites to use it. Minor formatting changes include refactoring type declarations to use grouped type blocks in a few handlers (v2_identities_list_identities, v2_keys_verify_key), which are stylistic improvements and not out of scope. A minimal variable rename in seed.go (identityId to identityID) follows Go naming conventions and is inconsequential. |
| Description Check | ✅ Passed | The pull request description is mostly complete and follows the required template structure. It includes a clear explanation of what the PR does (simplifies JSON data casting and improves type safety), the reasoning behind implementation choices (why RawMessage was not used and why a helper approach was chosen), references the linked issue (#3789) and linear ticket (ENG-1991), specifies the change type as "Enhancement," provides testing guidance, and includes a comprehensive checklist with most items marked as complete. While the "How should this be tested?" section is minimal, it is present and sufficient to indicate the test approach. |

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ENG-1991

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bda915d and ad94fb0.

📒 Files selected for processing (4)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (3 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • go/apps/api/routes/v2_keys_verify_key/handler.go
  • go/apps/api/routes/v2_keys_whoami/handler.go
🧰 Additional context used
🧬 Code graph analysis (1)
go/apps/api/routes/v2_identities_list_identities/handler.go (2)
go/apps/api/openapi/gen.go (3)
  • V2IdentitiesListIdentitiesRequestBody (708-714)
  • V2IdentitiesListIdentitiesResponseBody (717-726)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: autofix
🔇 Additional comments (3)
go/pkg/db/custom_types.go (2)

3-9: LGTM! Imports support the new helper function.

The added imports are necessary and correctly used: encoding/json for unmarshaling, fmt for type formatting in warnings, and logging for structured error visibility.


36-90: Well-designed helper that addresses all past review concerns.

The implementation successfully resolves previous issues:

  1. Nil logger safety (lines 66, 81): Guards prevent panics in test contexts where logger may be nil
  2. Error visibility (lines 67-70, 82-84): Structured logging captures type assertion failures and unmarshal errors, addressing Flo4604's concern about dropping errors silently
  3. Clear documentation (lines 36-56): Comprehensive doc comment accurately describes behavior, parameters, fail-safe semantics, and provides usage examples

The fail-safe pattern (returning zero value with logging on errors) is appropriate for the list/aggregate query use case and prevents single corrupt JSON fields from cascading into request failures.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

139-142: Correct usage of the helper that addresses past review concerns.

The implementation properly resolves previous issues:

  1. Resilient list endpoint (line 139): The helper logs unmarshal errors and returns an empty map rather than propagating an error, preventing a single corrupt metadata field from failing the entire list request
  2. Honors omitempty (lines 140-142): The length check ensures Meta is only assigned when the map contains data, preventing "meta": {} from appearing in responses for NULL or empty database values

The pattern correctly balances error visibility (via logging) with user experience (continuing to return valid identities).


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.

@ogzhanolguncu ogzhanolguncu marked this pull request as ready for review October 23, 2025 14:21
@Flo4604
Copy link
Member

Flo4604 commented Oct 23, 2025

Using json.rawmessage with inserts is a pain, that's why we are using []byte in the first place

Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34a8f42 and a0230f0.

📒 Files selected for processing (9)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/key_data.go (2 hunks)
  • go/pkg/db/key_find_for_verification.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_hash.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/models_generated.go (4 hunks)
  • go/pkg/db/sqlc.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
go/pkg/db/key_data.go (1)
go/pkg/db/custom_types.go (4)
  • UnmarshalJSONArray (51-66)
  • RoleInfo (11-15)
  • PermissionInfo (17-22)
  • RatelimitInfo (24-32)
go/pkg/db/key_find_live_by_hash.sql_generated.go (1)
go/pkg/db/models_generated.go (1)
  • Workspace (814-832)
go/pkg/db/key_list_by_key_auth_id.sql_generated.go (1)
go/pkg/db/models_generated.go (2)
  • Key (640-664)
  • EncryptedKey (609-616)
go/pkg/db/key_find_live_by_id.sql_generated.go (1)
go/pkg/db/models_generated.go (4)
  • Api (514-525)
  • KeyAuth (666-677)
  • Workspace (814-832)
  • EncryptedKey (609-616)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
🔇 Additional comments (18)
go/pkg/db/sqlc.json (1)

36-43: LGTM - Idiomatic JSON handling.

The override mapping from JSON database type to json.RawMessage is the idiomatic Go approach for representing JSON data. While json.RawMessage is essentially []byte, it explicitly signals JSON intent and integrates better with the encoding/json package.

go/pkg/db/key_find_for_verification.sql_generated.go (2)

8-12: Generated code aligns with configuration.

The addition of encoding/json import and the updated IdentityMeta field type to json.RawMessage correctly reflect the sqlc configuration override.


93-120: Type mapping correctly applied.

The struct correctly uses json.RawMessage for IdentityMeta (line 116) while aggregated fields like Roles, Permissions, and Ratelimits remain as interface{} since they're computed columns from subqueries, not actual database columns. This aligns with the PR's stated approach.

go/pkg/db/key_find_live_by_id.sql_generated.go (2)

8-12: Consistent type handling.

Import and type changes align with the project-wide JSON handling refactor.


106-142: Struct types correctly updated.

The IdentityMeta field now uses json.RawMessage (line 135), consistent with the sqlc override. Aggregated relationship fields (Roles, Permissions, RolePermissions, Ratelimits) appropriately remain as interface{} per the design constraints.

go/pkg/db/key_list_by_key_auth_id.sql_generated.go (2)

8-12: Import added for JSON handling.

Consistent with the JSON type refactor across generated files.


41-48: Type mapping applied correctly.

The IdentityMeta field uses json.RawMessage (line 45), matching the global sqlc override for JSON columns.

go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2)

8-12: Import addition supports JSON handling.

Consistent with other generated files in this refactor.


117-150: Field types correctly mapped.

The IdentityMeta field (line 143) correctly uses json.RawMessage. Aggregated fields remain interface{} as expected for computed JSON columns.

go/pkg/db/key_find_live_by_hash.sql_generated.go (2)

8-12: JSON import added.

Supports the json.RawMessage type used in the struct.


105-141: Type mappings consistent.

The IdentityMeta field (line 134) uses json.RawMessage. Aggregated JSON columns appropriately remain interface{}.

go/pkg/db/custom_types.go (1)

3-7: Import added for JSON unmarshalling.

The encoding/json package supports the new UnmarshalJSONArray helper function.

go/pkg/db/key_data.go (2)

84-88: Cleaner JSON array deserialization.

Replacing nil initialization with UnmarshalJSONArray calls centralizes the unmarshalling logic and ensures consistent handling of JSON-aggregated fields. The helper always returns a valid (non-nil) slice, eliminating potential nil pointer issues.


134-138: Consistent pattern applied.

Both buildKeyData and buildKeyDataFromKeyAuth now use the same UnmarshalJSONArray approach for deserializing relationship fields, improving maintainability.

go/pkg/db/models_generated.go (4)

555-567: Change from []byte to json.RawMessage for AuditLogTarget.Meta is verified as safe.

Downstream analysis found only three references to AuditLogTarget in test code (in go/apps/api/routes/v2_ratelimit_limit/200_test.go), accessing .Type, .ID, and .Name.String fields—not the Meta field. This confirms no breaking changes or required updates. The json.RawMessage type is already established in the codebase as the standard pattern for JSON columns, making this change safe and correct.


629-638: LGTM! Type change verified across all query result structs.

The change to json.RawMessage for Identity.Meta is correct and consistently applied. All query result struct IdentityMeta fields have been updated to match (verified in 5 generated SQL query files), and all downstream usages in handlers and tests are compatible since json.RawMessage is a standard Go type alias for []byte.


527-543: LGTM! No breaking changes found.

The change from []byte to json.RawMessage is compatible with all downstream code. The type flow is correct:

  • Source auditlog.AuditLog.ActorMeta is map[string]any
  • Converted via json.Marshal() to []byte in insert.go:69
  • Implicitly assigned to db.AuditLog.ActorMeta as json.RawMessage (which is defined as type RawMessage []byte)

This is the idiomatic pattern for JSON database columns. All SQL-generated code and service code handle the field correctly without modification.


814-832: No breaking changes—field is unused in codebase.

The Subscriptions field is not referenced anywhere in the application code, tests, or migrations. Comprehensive searches across all Go files (excluding generated files) returned zero results for field access, struct initialization, or serialization patterns. The change from []byte to json.RawMessage is safe and idiomatic—both types are compatible at the database level, and json.RawMessage is the Go standard for deferred JSON parsing. Since this is a sqlc-generated model file, the type change likely originates from an updated type mapping in the generation configuration, not manual code modification.

Likely an incorrect or invalid review comment.

@ogzhanolguncu
Copy link
Contributor Author

Using json.rawmessage with inserts is a pain, that's why we are using []byte in the first place

Wanna elaborate?

Copy link
Member

Flo4604 commented Oct 23, 2025

See the error messages in the failing tests

sql: Scan error on column index 11, name "subscriptions": unsupported Scan, storing driver.Value type into type *json.RawMessage

@github-actions
Copy link
Contributor

github-actions bot commented Oct 23, 2025

Thank you for following the naming conventions for pull request titles! 🙏

@ogzhanolguncu
Copy link
Contributor Author

ogzhanolguncu commented Oct 23, 2025

See the error messages in the failing tests

sql: Scan error on column index 11, name "subscriptions": unsupported Scan, storing driver.Value type into type *json.RawMessage

That's not what I meant why don't we stick to that after fixing those errors? Whats the trade off of []byte vs raw.JsonMessage?

Btw, I'm not saying you are wrong, but if its more idiomatic to use raw.Json thingy I say let's fix the issues and roll with this one

Copy link
Member

Flo4604 commented Oct 23, 2025

ah I remember why we use []byte json.rawmessage doesn't support null JSON columns

json.RawMessage does not support NULL · Issue #129 · sqlc-dev/sqlc

So you will have to work around this

@ogzhanolguncu
Copy link
Contributor Author

@Flo4604 I'll check again if its really causing too much headache i'll revert it back to []byte and let you know 🫡 . Thanks for the explanation

Copy link
Contributor

@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: 3

🧹 Nitpick comments (5)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

103-118: Consider adding explicit NULL metadata test coverage.

Given the PR's focus on improving NULL JSON column handling (see PR comments about json.RawMessage NULL support issues), consider adding a test that creates an identity without metadata and explicitly verifies that identity.Meta can be safely unmarshaled or is properly represented as NULL/empty.

Currently, this test creates an identity without metadata but doesn't verify the Meta field behavior.

Add a test case that verifies NULL metadata handling:

t.Run("create identity without metadata and verify NULL handling", func(t *testing.T) {
	externalTestID := uid.New("test_external_id")
	req := handler.Request{ExternalId: externalTestID}
	res := testutil.CallRoute[handler.Request, handler.Response](h, route, headers, req)

	require.Equal(t, 200, res.Status)

	identity, err := db.Query.FindIdentityByExternalID(ctx, h.DB.RO(), db.FindIdentityByExternalIDParams{
		WorkspaceID: h.Resources().UserWorkspace.ID,
		ExternalID:  externalTestID,
		Deleted:     false,
	})
	require.NoError(t, err)
	
	// Verify NULL or empty metadata is handled correctly
	dbMeta, err := db.UnmarshalNullableJSONTo[map[string]any](&identity.Meta)
	require.NoError(t, err)
	// Assert expected behavior for NULL/empty metadata
	// (adjust assertion based on your NullJSON implementation)
})
go/pkg/db/sqlc.json (1)

36-55: Consider a second override for non-nullable JSON → json.RawMessage.

To avoid type fragmentation and keep non-nullable JSON fields strongly typed, add an override for nullable=false mapping to encoding/json.RawMessage.

Apply:

           "overrides": [
             {
               "db_type": "json",
               "go_type": {
                 "type": "NullJSON",
                 "package": "dbtype",
                 "import": "github.com/unkeyed/unkey/go/pkg/db/types"
               },
               "nullable": true
             },
+            {
+              "db_type": "json",
+              "go_type": {
+                "type": "RawMessage",
+                "package": "json",
+                "import": "encoding/json"
+              },
+              "nullable": false
+            },
             {
               "column": "permissions.description",
               "go_type": {
                 "type": "NullString",
                 "package": "dbtype",
                 "import": "github.com/unkeyed/unkey/go/pkg/db/types"
               },
               "nullable": true
             }
           ]
go/pkg/db/key_data.go (1)

84-87: LGTM! Cleaner JSON unmarshalling with centralized helpers.

The refactoring replaces inline JSON unmarshalling with UnmarshalJSONArrayTo, which provides consistent error handling (returning empty slices on failure) across all relationship fields. This improves maintainability and reduces code duplication.

Also applies to: 134-137

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

24-27: LGTM! Improved code organization.

Grouping type aliases into a type block is more idiomatic and cleaner.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

19-22: LGTM! Improved code organization.

Grouping type aliases is more idiomatic and consistent with other handlers in this PR.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0230f0 and f968048.

📒 Files selected for processing (21)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/200_test.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (1 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/identity_find_with_ratelimits.sql_generated.go (2 hunks)
  • go/pkg/db/key_data.go (2 hunks)
  • go/pkg/db/key_data_test.go (2 hunks)
  • go/pkg/db/key_find_for_verification.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_hash.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/models_generated.go (4 hunks)
  • go/pkg/db/sqlc.json (1 hunks)
  • go/pkg/db/types/null_json.go (1 hunks)
  • go/pkg/testutil/seed/seed.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • go/pkg/db/key_find_live_by_id.sql_generated.go
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go
  • go/pkg/db/custom_types.go
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
  • go/pkg/db/models_generated.go
🧰 Additional context used
🧬 Code graph analysis (13)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/key_data.go (1)
go/pkg/db/custom_types.go (4)
  • UnmarshalJSONArrayTo (51-66)
  • RoleInfo (11-15)
  • PermissionInfo (17-22)
  • RatelimitInfo (24-32)
go/apps/api/routes/v2_keys_verify_key/handler.go (3)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/fault/wrap.go (1)
  • Wrap (25-67)
go/apps/api/routes/v2_apis_list_keys/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/apps/api/routes/v2_identities_list_identities/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/pkg/db/identity_find_with_ratelimits.sql_generated.go (1)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/apps/api/routes/v2_identities_get_identity/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/pkg/db/key_data_test.go (2)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/pkg/db/key_find_live_by_hash.sql_generated.go (1)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/apps/api/routes/v2_keys_get_key/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/testutil/seed/seed.go (3)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/pkg/db/identity_insert.sql_generated.go (1)
  • InsertIdentityParams (31-38)
go/pkg/db/models_generated.go (2)
  • Environment (618-627)
  • Identity (629-638)
go/apps/api/routes/v2_keys_whoami/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/key_find_for_verification.sql_generated.go (1)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

141-143: LGTM! Correct usage of the centralized JSON unmarshal helper.

The replacement of direct json.Unmarshal with db.UnmarshalNullableJSONTo is correct and follows the new pattern introduced in this PR. Error handling is properly preserved.

go/apps/api/routes/v2_identities_list_identities/200_test.go (1)

372-377: LGTM; aligns with NullJSON shape.

Using Meta.Valid and comparing against NullJSON.Data with JSONEq is correct and order‑insensitive.

go/pkg/db/identity_find_with_ratelimits.sql_generated.go (1)

12-13: Verification complete—NullJSON integration is correct.

The struct confirms Meta is dbtype.NullJSON (line 73) and Scan properly handles NULL (returns Valid=false) and valid JSON (with a defensive buffer copy to prevent reuse corruption). Ratelimits correctly remains interface{} as a non-database field. All requirements are met.

go/pkg/db/sqlc.json (1)

40-42: NullJSON properly implements Scanner/Valuer; no issues found.

Verification confirms that dbtype.NullJSON correctly implements both database/sql.Scanner and driver.Valuer interfaces with proper NULL handling. The Scan() method includes a defensive buffer copy to prevent corruption from driver buffer reuse—a critical safeguard. The sqlc override configuration is sound: nullable JSON columns map to NullJSON for reads, while insert parameters use json.RawMessage for explicit values. This design is intentional and working correctly.

go/pkg/db/key_find_live_by_hash.sql_generated.go (1)

12-13: LGTM; NullJSON properly handles LEFT JOIN NULLs.

The NullJSON type has Data and Valid fields, and its Scan() method correctly sets Valid=false when the database value is NULL. The UnmarshalTo() method returns nil without attempting to unmarshal when !Valid, preventing panics on NULL identity metadata from LEFT JOINs. All handlers safely use UnmarshalNullableJSONTo(), which wraps this behavior. The import change is correct.

go/pkg/db/key_find_for_verification.sql_generated.go (1)

12-12: LGTM! Generated code reflects the new NullJSON type.

The sqlc-generated code correctly imports the dbtype package and updates IdentityMeta to use dbtype.NullJSON, which properly handles nullable JSON columns.

Also applies to: 117-117

go/pkg/testutil/seed/seed.go (2)

380-380: LGTM! Naming convention improved.

The rename from identityId to identityID follows Go naming conventions for acronyms, and all usages are correctly updated.

Also applies to: 392-392, 397-397


372-387: Type inconsistency confirmed: verify if this is intended design.

The verification confirms that InsertIdentityParams.Meta is json.RawMessage while Identity.Meta is dbtype.NullJSON. Since both files are auto-generated, this pattern likely reflects intentional design: simpler raw types for insertion parameters and wrapped nullable types for model retrieval. However, the architectural intention needs confirmation—specifically whether the codebase applies this pattern consistently across other insert operations versus model definitions, and whether it introduces any conversion overhead or maintenance complexity that should be addressed at the code generator level.

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

232-238: LGTM! Consistent error handling with centralized helper.

The refactoring replaces manual JSON unmarshalling with db.UnmarshalNullableJSONTo, maintaining identical error handling behavior while improving code consistency across the codebase.

go/pkg/db/types/null_json.go (3)

15-40: LGTM! Critical defensive copy prevents corruption.

The defensive copy in the Scan method is essential to prevent data corruption from driver buffer reuse. This is a well-known issue with json.RawMessage and the implementation correctly addresses it with clear documentation.


42-47: LGTM! Correct SQL driver Value implementation.

The Value method correctly returns nil for SQL NULL when the field is invalid, and properly converts the RawMessage to []byte when valid.


60-65: The silent no-op behavior is safe and works as intended.

The UnmarshalTo method is only called through the UnmarshalNullableJSONTo wrapper function (custom_types.go:79-83), which properly propagates the error to callers. All production usages check the returned error value. Returning nil when Valid is false is intentional and semantically correct: no error occurred, the field is null, and the caller receives a zero value—a reasonable default for nullable fields. The design properly distinguishes between actual unmarshaling errors (from json.Unmarshal) and null values.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

139-146: LGTM! Consistent error handling pattern.

The refactoring uses db.UnmarshalNullableJSONTo with proper error wrapping. Unlike the list_keys handler which only logs errors, this returns the error to the caller, which is more appropriate for an API that lists identities where metadata is expected.

go/pkg/db/key_data_test.go (1)

9-9: LGTM! Tests updated for new type.

The test correctly uses the new dbtype.NullJSON type for IdentityMeta, ensuring the type change is properly validated.

Also applies to: 125-125, 133-133

go/apps/api/routes/v2_apis_list_keys/handler.go (1)

306-311: Behavior change is intentional and consistent across handlers.

The old implementation checked both len(keyData.Identity.Meta) > 0 and identityMeta != nil before assigning, while the new code assigns &identityMeta whenever unmarshaling succeeds. This means empty JSON objects {} now result in a pointer to an empty map instead of nil.

However, this change is part of the broader refactor commit "refactor: use NullJSON for mysql json type" and follows the identical pattern already established in v2_keys_whoami and v2_keys_get_key handlers. The refactor also improves error handling by logging failures instead of silently ignoring them.

Confirm whether this behavior change is acceptable for the API contract (nil vs. empty map in the response).

@ogzhanolguncu
Copy link
Contributor Author

@Flo4604 I think I finally landed on a good solution, but there is some caveat

	// CRITICAL: A defensive copy to prevent buffer reuse corruption
	// json.RawMessage holds a reference to the underlying byte slice.
	// When the driver reuses that buffer for the next row, your NullJSON.Data suddenly points to corrupted memory.
	// This consumes more memory compared to copyless version and also guarantees data integrity.
	clone := make([]byte, len(buf))
	copy(clone, buf)

When scanning, I actually noticed this only during tests, data is getting corrupted due to how json.RawMessage handles the byte. wdyt? But overall usage is so much nicer, less clutter less noise.

image

Copy link
Member

Flo4604 commented Oct 24, 2025

why don't we then just use our byte[] version and add a helper that does the same thing?

Copy link
Member

Flo4604 commented Oct 24, 2025

db.Unmarshal[T any] (data []byte) (T, error) or so?

and if we wanted to copy the data we can just do identity.Meta instead of &identity.Meta

@ogzhanolguncu
Copy link
Contributor Author

I guess, yeah. copy also makes me feel uncomfortable

Copy link
Contributor

@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: 2

♻️ Duplicate comments (2)
go/apps/api/routes/v2_keys_whoami/handler.go (1)

158-162: Honor omitempty by checking data presence before unmarshalling and setting Meta.

When keyData.Identity.Meta is nil or empty, the helper returns a zero-value map; taking &identityMeta creates a pointer to that nil map, which serializes as "meta": null instead of being omitted. Check for data presence first and only set response.Identity.Meta when valid data exists.

Apply the fix suggested in the previous review:

-		if identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta); err != nil {
+		if len(keyData.Identity.Meta) > 0 {
+			identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta)
+			if err != nil {
-			h.Logger.Error("failed to unmarshal identity meta", "error", err)
-		} else {
+				h.Logger.Error("failed to unmarshal identity meta", "error", err)
+			} else {
-			response.Identity.Meta = &identityMeta
+				response.Identity.Meta = &identityMeta
+			}
		}
go/apps/api/routes/v2_identities_get_identity/handler.go (1)

99-105: Log meta unmarshal errors instead of failing the request, and honor omitempty.

Failing the entire request when identity metadata cannot be parsed degrades UX. Other endpoints (e.g., v2_keys_whoami) log the error and continue. Additionally, always setting Meta: &metaMap produces "meta": null even when the DB column is empty or NULL; gate assignment on data presence to respect the omitempty tag.

Apply the fix suggested in the previous review:

-	metaMap, err := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta)
-	if err != nil {
-		return fault.Wrap(err,
-			fault.Internal("unable to unmarshal metadata"),
-			fault.Public("We're unable to parse the identity's metadata."),
-		)
-	}
+	var metaPtr *map[string]any
+	if len(identity.Meta) > 0 {
+		metaMap, err := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta)
+		if err != nil {
+			h.Logger.Error("failed to unmarshal identity meta", "error", err)
+		} else {
+			metaPtr = &metaMap
+		}
+	}

And update the response assignment:

		Data: openapi.Identity{
			Id:         identity.ID,
			ExternalId: identity.ExternalID,
-			Meta:       &metaMap,
+			Meta:       metaPtr,
			Ratelimits: &responseRatelimits,
		},
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f968048 and aef7136.

📒 Files selected for processing (9)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (1 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/testutil/seed/seed.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • go/pkg/testutil/seed/seed.go
  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/pkg/db/custom_types.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
🧰 Additional context used
🧬 Code graph analysis (5)
go/apps/api/routes/v2_keys_verify_key/handler.go (4)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/apps/api/routes/v2_keys_whoami/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/apps/api/routes/v2_identities_list_identities/handler.go (3)
go/apps/api/openapi/gen.go (3)
  • V2IdentitiesListIdentitiesRequestBody (708-714)
  • V2IdentitiesListIdentitiesResponseBody (717-726)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/apps/api/routes/v2_identities_get_identity/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
🔇 Additional comments (3)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

141-141: LGTM!

The test code correctly uses the new unmarshalling helper. The pattern is consistent across all test cases and appropriately validates metadata handling.

Also applies to: 241-241, 316-316, 425-425

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

24-27: LGTM!

Grouping related type aliases in a single type block is idiomatic Go and improves readability.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

19-22: LGTM!

Grouping related type aliases in a single type block is idiomatic Go and improves readability.

Copy link
Member

Flo4604 commented Oct 24, 2025

yeah also we shouldn't fail when json parsing the meta object, as we discussed in slack the object should be valid since its validated as object when sending via the API and the frontend also validates it afaik.

Us failing this would block the user until we fix it which would be bad esp. if we do this in verify key and other endpoints

@ogzhanolguncu
Copy link
Contributor Author

yeah also we shouldn't fail when json parsing the meta object, as we discussed in slack the object should be valid since its validated as object when sending via the API and the frontend also validates it afaik.Us failing this would block the user until we fix it which would be bad esp. if we do this in verify key and other endpoints

Yeah I didn't change the existing behavior. We just log errors move on

Copy link
Contributor

@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: 1

♻️ Duplicate comments (1)
go/pkg/db/custom_types.go (1)

51-66: Critical: Silent error handling issue remains unaddressed from previous review.

The function still silently discards JSON unmarshal errors at line 62-63, returning an empty slice instead. This prevents callers from distinguishing between legitimate empty arrays and corrupted/malformed data, masking data integrity issues.

The past review provided evidence showing that other code paths (e.g., go/apps/api/routes/v2_keys_set_roles/handler.go:286-289 and go/internal/services/keys/get.go:161-185) log or propagate unmarshal failures. This inconsistency is problematic.

Note that your new UnmarshalNullableJSONTo function correctly returns errors (line 92-93), demonstrating the right pattern.

Refactor UnmarshalJSONArrayTo to match:

-func UnmarshalJSONArrayTo[T any](data any) []T {
+func UnmarshalJSONArrayTo[T any](data any) ([]T, error) {
 	if data == nil {
-		return []T{}
+		return []T{}, nil
 	}
 
 	bytes, ok := data.([]byte)
 	if !ok || len(bytes) == 0 {
-		return []T{}
+		return []T{}, nil
 	}
 
 	var result []T
 	if err := json.Unmarshal(bytes, &result); err != nil {
-		return []T{}
+		return nil, fmt.Errorf("failed to unmarshal JSON array: %w", err)
 	}
-	return result
+	return result, nil
 }

Then update all call sites in go/pkg/db/key_data.go to handle the error appropriately (log with context or propagate).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aef7136 and ebe923b.

📒 Files selected for processing (1)
  • go/pkg/db/custom_types.go (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test

Copy link
Member

@Flo4604 Flo4604 left a comment

Choose a reason for hiding this comment

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

otherwise lgtm

Copy link
Contributor

@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: 8

♻️ Duplicate comments (1)
go/apps/api/routes/v2_identities_get_identity/handler.go (1)

99-121: Honor omitempty for Meta; set pointer only when meta is present.

Current code always sets Meta: &metaMap, yielding "meta": null when absent/invalid. Gate it so Meta is omitted unless parsed.

- metaMap := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta, h.Logger)
+ var metaMap map[string]any
+ if identity.Meta != nil { // or check specific NullString/Valid shape if applicable
+     metaMap = db.UnmarshalNullableJSONTo[map[string]any](identity.Meta, h.Logger)
+ }
@@
-            Meta:       &metaMap,
+            Meta:       func() *map[string]any { if metaMap == nil { return nil }; return &metaMap }(),

Optional: also use the same helper for ratelimits for consistency/logging.

- var ratelimits []db.RatelimitInfo
- if ratelimitBytes, ok := identity.Ratelimits.([]byte); ok && ratelimitBytes != nil {
-     _ = json.Unmarshal(ratelimitBytes, &ratelimits)
- }
+ ratelimits := db.UnmarshalNullableJSONTo[[]db.RatelimitInfo](identity.Ratelimits, h.Logger)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebe923b and bda915d.

📒 Files selected for processing (12)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (2 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_reroll_key/200_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_reroll_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (2 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/key_data.go (5 hunks)
  • go/pkg/db/key_data_test.go (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
🧰 Additional context used
🧬 Code graph analysis (9)
go/apps/api/routes/v2_keys_verify_key/handler.go (3)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/apps/api/routes/v2_keys_reroll_key/200_test.go (1)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/pkg/db/key_data.go (4)
go/pkg/db/key_find_live_by_hash.sql_generated.go (1)
  • FindLiveKeyByHashRow (104-140)
go/pkg/db/key_find_live_by_id.sql_generated.go (1)
  • FindLiveKeyByIDRow (105-141)
go/pkg/db/key_list_live_by_auth_id.sql_generated.go (1)
  • ListLiveKeysByKeyAuthIDRow (116-149)
go/pkg/db/custom_types.go (4)
  • UnmarshalNullableJSONTo (57-86)
  • RoleInfo (13-17)
  • PermissionInfo (19-24)
  • RatelimitInfo (26-34)
go/apps/api/routes/v2_identities_get_identity/handler.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/pkg/db/key_data_test.go (1)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/apps/api/routes/v2_identities_list_identities/handler.go (2)
go/apps/api/openapi/gen.go (3)
  • V2IdentitiesListIdentitiesRequestBody (708-714)
  • V2IdentitiesListIdentitiesResponseBody (717-726)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/apps/api/routes/v2_keys_reroll_key/handler.go (1)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/apps/api/routes/v2_keys_whoami/handler.go (4)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
🔇 Additional comments (1)
go/apps/api/routes/v2_keys_reroll_key/handler.go (1)

82-82: LGTM: pass logger into ToKeyData.

Signature updated correctly; aligns with helper-based JSON handling.

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.

Improve type safety for JSON columns in database structs

3 participants