From c064fcfb048b49a986e0e3d3bd7084a26a078082 Mon Sep 17 00:00:00 2001 From: Allisson Azevedo Date: Sat, 7 Mar 2026 20:37:39 -0300 Subject: [PATCH] feat(tokenization): delete tokenization keys by name Update the Delete method in TokenizationKeyUseCase and TokenizationKeyRepository to accept a key name instead of a UUID. This change ensures that when a tokenization key is deleted by name, all its versions are correctly soft-deleted simultaneously. Key changes: - Changed Delete method signature to accept name string in both UseCase and Repository interfaces. - Updated MySQL and PostgreSQL repository implementations to soft-delete all versions by name. - Changed HTTP route from DELETE /v1/tokenization/keys/:id to DELETE /v1/tokenization/keys/:name. - Updated TokenizationKeyHandler.DeleteHandler to extract the name from the path. - Updated OpenAPI specification and docs/engines/tokenization.md to reflect the new API contract. - Updated unit tests for handler, use case, and both repository implementations. - Updated integration tests in tokenization_flow_test.go to verify name-based deletion. - Cleaned up unused uuid imports and variables introduced by the change. --- docs/engines/tokenization.md | 2 +- docs/openapi.yaml | 48 ++++++++----------- internal/http/server.go | 2 +- .../http/tokenization_key_handler.go | 15 +++--- .../http/tokenization_key_handler_test.go | 41 +++++----------- .../repository/mysql/mysql_repository.go | 11 ++--- .../repository/mysql/mysql_repository_test.go | 2 +- .../postgresql/postgresql_repository.go | 6 +-- .../postgresql/postgresql_repository_test.go | 2 +- internal/tokenization/usecase/interface.go | 6 +-- internal/tokenization/usecase/mocks/mocks.go | 44 ++++++++--------- .../tokenization_key_metrics_decorator.go | 6 +-- ...tokenization_key_metrics_decorator_test.go | 22 ++++----- .../usecase/tokenization_key_usecase.go | 6 +-- .../usecase/tokenization_key_usecase_test.go | 12 ++--- test/integration/tokenization_flow_test.go | 10 +--- 16 files changed, 98 insertions(+), 137 deletions(-) diff --git a/docs/engines/tokenization.md b/docs/engines/tokenization.md index 7ce6605..f28a1a3 100644 --- a/docs/engines/tokenization.md +++ b/docs/engines/tokenization.md @@ -172,7 +172,7 @@ Example response (`200 OK`): #### Delete Tokenization Key -- **Endpoint**: `DELETE /v1/tokenization/keys/:id` +- **Endpoint**: `DELETE /v1/tokenization/keys/:name` - **Capability**: `delete` - **Success**: `204 No Content` diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 5f88c48..48f8454 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -811,31 +811,14 @@ paths: $ref: "#/components/schemas/ErrorResponse" "429": $ref: "#/components/responses/TooManyRequests" - /v1/tokenization/keys/{name}/rotate: - post: + delete: tags: [tokenization] - summary: Rotate tokenization key + summary: Delete tokenization key security: - bearerAuth: [] - parameters: - - name: name - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TokenizationKeyRotateRequest" responses: - "201": - description: New tokenization key version created - content: - application/json: - schema: - $ref: "#/components/schemas/TokenizationKeyResponse" + "204": + description: Deleted "401": $ref: "#/components/responses/Unauthorized" "403": @@ -850,22 +833,31 @@ paths: $ref: "#/components/responses/ValidationError" "429": $ref: "#/components/responses/TooManyRequests" - /v1/tokenization/keys/{id}: - delete: + /v1/tokenization/keys/{name}/rotate: + post: tags: [tokenization] - summary: Delete tokenization key + summary: Rotate tokenization key security: - bearerAuth: [] parameters: - - name: id + - name: name in: path required: true schema: type: string - format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TokenizationKeyRotateRequest" responses: - "204": - description: Deleted + "201": + description: New tokenization key version created + content: + application/json: + schema: + $ref: "#/components/schemas/TokenizationKeyResponse" "401": $ref: "#/components/responses/Unauthorized" "403": diff --git a/internal/http/server.go b/internal/http/server.go index a26a79e..f3a183c 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -398,7 +398,7 @@ func (s *Server) registerTokenizationRoutes( ) // Delete tokenization key - keys.DELETE("/:id", + keys.DELETE("/:name", authHTTP.AuthorizationMiddleware(authDomain.DeleteCapability, auditLogUseCase, s.logger), tokenizationKeyHandler.DeleteHandler, ) diff --git a/internal/tokenization/http/tokenization_key_handler.go b/internal/tokenization/http/tokenization_key_handler.go index 95c20b9..107cb9b 100644 --- a/internal/tokenization/http/tokenization_key_handler.go +++ b/internal/tokenization/http/tokenization_key_handler.go @@ -7,7 +7,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/google/uuid" "github.com/allisson/secrets/internal/httputil" "github.com/allisson/secrets/internal/tokenization/http/dto" @@ -140,21 +139,21 @@ func (h *TokenizationKeyHandler) RotateHandler(c *gin.Context) { c.JSON(http.StatusCreated, response) } -// DeleteHandler soft-deletes a tokenization key by ID. -// DELETE /v1/tokenization/keys/:id - Requires DeleteCapability. +// DeleteHandler soft-deletes a tokenization key by name. +// DELETE /v1/tokenization/keys/:name - Requires DeleteCapability. // Returns 204 No Content on success. func (h *TokenizationKeyHandler) DeleteHandler(c *gin.Context) { - // Parse and validate UUID - keyID, err := uuid.Parse(c.Param("id")) - if err != nil { + // Get key name from URL parameter + name := c.Param("name") + if name == "" { httputil.HandleBadRequestGin(c, - fmt.Errorf("invalid key ID format: must be a valid UUID"), + fmt.Errorf("key name is required"), h.logger) return } // Call use case - if err := h.keyUseCase.Delete(c.Request.Context(), keyID); err != nil { + if err := h.keyUseCase.Delete(c.Request.Context(), name); err != nil { httputil.HandleErrorGin(c, err, h.logger) return } diff --git a/internal/tokenization/http/tokenization_key_handler_test.go b/internal/tokenization/http/tokenization_key_handler_test.go index 17778f0..1e93ef8 100644 --- a/internal/tokenization/http/tokenization_key_handler_test.go +++ b/internal/tokenization/http/tokenization_key_handler_test.go @@ -351,15 +351,15 @@ func TestTokenizationKeyHandler_DeleteHandler(t *testing.T) { t.Run("Success_DeleteKey", func(t *testing.T) { handler, mockUseCase := setupTestKeyHandler(t) - keyID := uuid.Must(uuid.NewV7()) + keyName := "test-key" mockUseCase.EXPECT(). - Delete(mock.Anything, keyID). + Delete(mock.Anything, keyName). Return(nil). Once() - c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/"+keyID.String(), nil) - c.Params = gin.Params{{Key: "id", Value: keyID.String()}} + c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/"+keyName, nil) + c.Params = gin.Params{{Key: "name", Value: keyName}} handler.DeleteHandler(c) @@ -367,35 +367,18 @@ func TestTokenizationKeyHandler_DeleteHandler(t *testing.T) { assert.Empty(t, w.Body.String()) }) - t.Run("Error_InvalidUUID", func(t *testing.T) { - handler, _ := setupTestKeyHandler(t) - - c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/invalid-uuid", nil) - c.Params = gin.Params{{Key: "id", Value: "invalid-uuid"}} - - handler.DeleteHandler(c) - - assert.Equal(t, http.StatusBadRequest, w.Code) - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - assert.Equal(t, "bad_request", response["error"]) - assert.Contains(t, response["message"], "invalid key ID format") - }) - t.Run("Error_KeyNotFound", func(t *testing.T) { handler, mockUseCase := setupTestKeyHandler(t) - keyID := uuid.Must(uuid.NewV7()) + keyName := "non-existent-key" mockUseCase.EXPECT(). - Delete(mock.Anything, keyID). + Delete(mock.Anything, keyName). Return(tokenizationDomain.ErrTokenizationKeyNotFound). Once() - c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/"+keyID.String(), nil) - c.Params = gin.Params{{Key: "id", Value: keyID.String()}} + c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/"+keyName, nil) + c.Params = gin.Params{{Key: "name", Value: keyName}} handler.DeleteHandler(c) @@ -405,16 +388,16 @@ func TestTokenizationKeyHandler_DeleteHandler(t *testing.T) { t.Run("Error_UseCaseError", func(t *testing.T) { handler, mockUseCase := setupTestKeyHandler(t) - keyID := uuid.Must(uuid.NewV7()) + keyName := "test-key" dbError := errors.New("database error") mockUseCase.EXPECT(). - Delete(mock.Anything, keyID). + Delete(mock.Anything, keyName). Return(dbError). Once() - c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/"+keyID.String(), nil) - c.Params = gin.Params{{Key: "id", Value: keyID.String()}} + c, w := createTestContext(http.MethodDelete, "/v1/tokenization/keys/"+keyName, nil) + c.Params = gin.Params{{Key: "name", Value: keyName}} handler.DeleteHandler(c) diff --git a/internal/tokenization/repository/mysql/mysql_repository.go b/internal/tokenization/repository/mysql/mysql_repository.go index 6265c10..568da0a 100644 --- a/internal/tokenization/repository/mysql/mysql_repository.go +++ b/internal/tokenization/repository/mysql/mysql_repository.go @@ -60,17 +60,12 @@ func (m *MySQLTokenizationKeyRepository) Create( } // Delete soft-deletes a tokenization key by setting its deleted_at timestamp. -func (m *MySQLTokenizationKeyRepository) Delete(ctx context.Context, keyID uuid.UUID) error { +func (m *MySQLTokenizationKeyRepository) Delete(ctx context.Context, name string) error { querier := database.GetTx(ctx, m.db) - query := `UPDATE tokenization_keys SET deleted_at = NOW() WHERE id = ?` + query := `UPDATE tokenization_keys SET deleted_at = NOW() WHERE name = ?` - id, err := keyID.MarshalBinary() - if err != nil { - return apperrors.Wrap(err, "failed to marshal tokenization key id") - } - - _, err = querier.ExecContext(ctx, query, id) + _, err := querier.ExecContext(ctx, query, name) if err != nil { return apperrors.Wrap(err, "failed to delete tokenization key") } diff --git a/internal/tokenization/repository/mysql/mysql_repository_test.go b/internal/tokenization/repository/mysql/mysql_repository_test.go index 1cd11f8..487891f 100644 --- a/internal/tokenization/repository/mysql/mysql_repository_test.go +++ b/internal/tokenization/repository/mysql/mysql_repository_test.go @@ -293,7 +293,7 @@ func TestMySQLTokenizationKeyRepository_Delete(t *testing.T) { require.NoError(t, err) // Delete the key - err = repo.Delete(ctx, key.ID) + err = repo.Delete(ctx, key.Name) require.NoError(t, err) // Verify soft delete - key should not be found diff --git a/internal/tokenization/repository/postgresql/postgresql_repository.go b/internal/tokenization/repository/postgresql/postgresql_repository.go index 8c607ca..18f7551 100644 --- a/internal/tokenization/repository/postgresql/postgresql_repository.go +++ b/internal/tokenization/repository/postgresql/postgresql_repository.go @@ -52,12 +52,12 @@ func (p *PostgreSQLTokenizationKeyRepository) Create( } // Delete soft-deletes a tokenization key by setting its deleted_at timestamp. -func (p *PostgreSQLTokenizationKeyRepository) Delete(ctx context.Context, keyID uuid.UUID) error { +func (p *PostgreSQLTokenizationKeyRepository) Delete(ctx context.Context, name string) error { querier := database.GetTx(ctx, p.db) - query := `UPDATE tokenization_keys SET deleted_at = NOW() WHERE id = $1` + query := `UPDATE tokenization_keys SET deleted_at = NOW() WHERE name = $1` - _, err := querier.ExecContext(ctx, query, keyID) + _, err := querier.ExecContext(ctx, query, name) if err != nil { return apperrors.Wrap(err, "failed to delete tokenization key") } diff --git a/internal/tokenization/repository/postgresql/postgresql_repository_test.go b/internal/tokenization/repository/postgresql/postgresql_repository_test.go index a39ac00..66cdb68 100644 --- a/internal/tokenization/repository/postgresql/postgresql_repository_test.go +++ b/internal/tokenization/repository/postgresql/postgresql_repository_test.go @@ -293,7 +293,7 @@ func TestPostgreSQLTokenizationKeyRepository_Delete(t *testing.T) { require.NoError(t, err) // Delete the key - err = repo.Delete(ctx, key.ID) + err = repo.Delete(ctx, key.Name) require.NoError(t, err) // Verify soft delete - key should not be found diff --git a/internal/tokenization/usecase/interface.go b/internal/tokenization/usecase/interface.go index 01682ca..6cf98f8 100644 --- a/internal/tokenization/usecase/interface.go +++ b/internal/tokenization/usecase/interface.go @@ -21,7 +21,7 @@ type DekRepository interface { // TokenizationKeyRepository defines the interface for tokenization key persistence. type TokenizationKeyRepository interface { Create(ctx context.Context, key *tokenizationDomain.TokenizationKey) error - Delete(ctx context.Context, keyID uuid.UUID) error + Delete(ctx context.Context, name string) error Get(ctx context.Context, keyID uuid.UUID) (*tokenizationDomain.TokenizationKey, error) GetByName(ctx context.Context, name string) (*tokenizationDomain.TokenizationKey, error) GetByNameAndVersion( @@ -88,8 +88,8 @@ type TokenizationKeyUseCase interface { alg cryptoDomain.Algorithm, ) (*tokenizationDomain.TokenizationKey, error) - // Delete soft deletes a tokenization key and all its versions by key ID. - Delete(ctx context.Context, keyID uuid.UUID) error + // Delete soft deletes a tokenization key and all its versions by name. + Delete(ctx context.Context, name string) error // GetByName retrieves a single tokenization key by its name. // Returns the latest version for the key. Filters out soft-deleted keys. diff --git a/internal/tokenization/usecase/mocks/mocks.go b/internal/tokenization/usecase/mocks/mocks.go index 2ae320a..fb82cc3 100644 --- a/internal/tokenization/usecase/mocks/mocks.go +++ b/internal/tokenization/usecase/mocks/mocks.go @@ -335,16 +335,16 @@ func (_c *MockTokenizationKeyRepository_Create_Call) RunAndReturn(run func(ctx c } // Delete provides a mock function for the type MockTokenizationKeyRepository -func (_mock *MockTokenizationKeyRepository) Delete(ctx context.Context, keyID uuid.UUID) error { - ret := _mock.Called(ctx, keyID) +func (_mock *MockTokenizationKeyRepository) Delete(ctx context.Context, name string) error { + ret := _mock.Called(ctx, name) if len(ret) == 0 { panic("no return value specified for Delete") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { - r0 = returnFunc(ctx, keyID) + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, name) } else { r0 = ret.Error(0) } @@ -358,20 +358,20 @@ type MockTokenizationKeyRepository_Delete_Call struct { // Delete is a helper method to define mock.On call // - ctx context.Context -// - keyID uuid.UUID -func (_e *MockTokenizationKeyRepository_Expecter) Delete(ctx interface{}, keyID interface{}) *MockTokenizationKeyRepository_Delete_Call { - return &MockTokenizationKeyRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, keyID)} +// - name string +func (_e *MockTokenizationKeyRepository_Expecter) Delete(ctx interface{}, name interface{}) *MockTokenizationKeyRepository_Delete_Call { + return &MockTokenizationKeyRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, name)} } -func (_c *MockTokenizationKeyRepository_Delete_Call) Run(run func(ctx context.Context, keyID uuid.UUID)) *MockTokenizationKeyRepository_Delete_Call { +func (_c *MockTokenizationKeyRepository_Delete_Call) Run(run func(ctx context.Context, name string)) *MockTokenizationKeyRepository_Delete_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { arg0 = args[0].(context.Context) } - var arg1 uuid.UUID + var arg1 string if args[1] != nil { - arg1 = args[1].(uuid.UUID) + arg1 = args[1].(string) } run( arg0, @@ -386,7 +386,7 @@ func (_c *MockTokenizationKeyRepository_Delete_Call) Return(err error) *MockToke return _c } -func (_c *MockTokenizationKeyRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, keyID uuid.UUID) error) *MockTokenizationKeyRepository_Delete_Call { +func (_c *MockTokenizationKeyRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, name string) error) *MockTokenizationKeyRepository_Delete_Call { _c.Call.Return(run) return _c } @@ -1276,16 +1276,16 @@ func (_c *MockTokenizationKeyUseCase_Create_Call) RunAndReturn(run func(ctx cont } // Delete provides a mock function for the type MockTokenizationKeyUseCase -func (_mock *MockTokenizationKeyUseCase) Delete(ctx context.Context, keyID uuid.UUID) error { - ret := _mock.Called(ctx, keyID) +func (_mock *MockTokenizationKeyUseCase) Delete(ctx context.Context, name string) error { + ret := _mock.Called(ctx, name) if len(ret) == 0 { panic("no return value specified for Delete") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { - r0 = returnFunc(ctx, keyID) + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, name) } else { r0 = ret.Error(0) } @@ -1299,20 +1299,20 @@ type MockTokenizationKeyUseCase_Delete_Call struct { // Delete is a helper method to define mock.On call // - ctx context.Context -// - keyID uuid.UUID -func (_e *MockTokenizationKeyUseCase_Expecter) Delete(ctx interface{}, keyID interface{}) *MockTokenizationKeyUseCase_Delete_Call { - return &MockTokenizationKeyUseCase_Delete_Call{Call: _e.mock.On("Delete", ctx, keyID)} +// - name string +func (_e *MockTokenizationKeyUseCase_Expecter) Delete(ctx interface{}, name interface{}) *MockTokenizationKeyUseCase_Delete_Call { + return &MockTokenizationKeyUseCase_Delete_Call{Call: _e.mock.On("Delete", ctx, name)} } -func (_c *MockTokenizationKeyUseCase_Delete_Call) Run(run func(ctx context.Context, keyID uuid.UUID)) *MockTokenizationKeyUseCase_Delete_Call { +func (_c *MockTokenizationKeyUseCase_Delete_Call) Run(run func(ctx context.Context, name string)) *MockTokenizationKeyUseCase_Delete_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { arg0 = args[0].(context.Context) } - var arg1 uuid.UUID + var arg1 string if args[1] != nil { - arg1 = args[1].(uuid.UUID) + arg1 = args[1].(string) } run( arg0, @@ -1327,7 +1327,7 @@ func (_c *MockTokenizationKeyUseCase_Delete_Call) Return(err error) *MockTokeniz return _c } -func (_c *MockTokenizationKeyUseCase_Delete_Call) RunAndReturn(run func(ctx context.Context, keyID uuid.UUID) error) *MockTokenizationKeyUseCase_Delete_Call { +func (_c *MockTokenizationKeyUseCase_Delete_Call) RunAndReturn(run func(ctx context.Context, name string) error) *MockTokenizationKeyUseCase_Delete_Call { _c.Call.Return(run) return _c } diff --git a/internal/tokenization/usecase/tokenization_key_metrics_decorator.go b/internal/tokenization/usecase/tokenization_key_metrics_decorator.go index 7f310d2..6ad8fa6 100644 --- a/internal/tokenization/usecase/tokenization_key_metrics_decorator.go +++ b/internal/tokenization/usecase/tokenization_key_metrics_decorator.go @@ -4,8 +4,6 @@ import ( "context" "time" - "github.com/google/uuid" - cryptoDomain "github.com/allisson/secrets/internal/crypto/domain" "github.com/allisson/secrets/internal/metrics" tokenizationDomain "github.com/allisson/secrets/internal/tokenization/domain" @@ -73,9 +71,9 @@ func (t *tokenizationKeyUseCaseWithMetrics) Rotate( } // Delete records metrics for tokenization key deletion operations. -func (t *tokenizationKeyUseCaseWithMetrics) Delete(ctx context.Context, tokenizationKeyID uuid.UUID) error { +func (t *tokenizationKeyUseCaseWithMetrics) Delete(ctx context.Context, name string) error { start := time.Now() - err := t.next.Delete(ctx, tokenizationKeyID) + err := t.next.Delete(ctx, name) status := "success" if err != nil { diff --git a/internal/tokenization/usecase/tokenization_key_metrics_decorator_test.go b/internal/tokenization/usecase/tokenization_key_metrics_decorator_test.go index 0c0fef9..fc6d843 100644 --- a/internal/tokenization/usecase/tokenization_key_metrics_decorator_test.go +++ b/internal/tokenization/usecase/tokenization_key_metrics_decorator_test.go @@ -344,16 +344,16 @@ func TestTokenizationKeyUseCaseWithMetrics_Delete(t *testing.T) { tests := []struct { name string setupMocks func(*tokenizationMocks.MockTokenizationKeyUseCase, *mockBusinessMetrics) - keyID uuid.UUID + keyName string expectedErr error expectedStatus string }{ { name: "Success_RecordsSuccessMetrics", setupMocks: func(mockUseCase *tokenizationMocks.MockTokenizationKeyUseCase, mockMetrics *mockBusinessMetrics) { - keyID := uuid.New() + keyName := "test-key" mockUseCase.EXPECT(). - Delete(mock.Anything, keyID). + Delete(mock.Anything, keyName). Return(nil). Once() mockMetrics.On("RecordOperation", mock.Anything, "tokenization", "tokenization_key_delete", "success"). @@ -361,16 +361,16 @@ func TestTokenizationKeyUseCaseWithMetrics_Delete(t *testing.T) { mockMetrics.On("RecordDuration", mock.Anything, "tokenization", "tokenization_key_delete", mock.AnythingOfType("time.Duration"), "success"). Once() }, - keyID: uuid.New(), + keyName: "test-key", expectedErr: nil, expectedStatus: "success", }, { name: "Error_RecordsErrorMetrics", setupMocks: func(mockUseCase *tokenizationMocks.MockTokenizationKeyUseCase, mockMetrics *mockBusinessMetrics) { - keyID := uuid.New() + keyName := "test-key" mockUseCase.EXPECT(). - Delete(mock.Anything, keyID). + Delete(mock.Anything, keyName). Return(errors.New("key not found")). Once() mockMetrics.On("RecordOperation", mock.Anything, "tokenization", "tokenization_key_delete", "error"). @@ -378,7 +378,7 @@ func TestTokenizationKeyUseCaseWithMetrics_Delete(t *testing.T) { mockMetrics.On("RecordDuration", mock.Anything, "tokenization", "tokenization_key_delete", mock.AnythingOfType("time.Duration"), "error"). Once() }, - keyID: uuid.New(), + keyName: "test-key", expectedErr: errors.New("key not found"), expectedStatus: "error", }, @@ -388,10 +388,10 @@ func TestTokenizationKeyUseCaseWithMetrics_Delete(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mockUseCase := tokenizationMocks.NewMockTokenizationKeyUseCase(t) mockMetrics := &mockBusinessMetrics{} - // Generate a fresh UUID for each test to pass to mock setup - testKeyID := uuid.New() + // Generate a fresh key name for each test to pass to mock setup + testKeyName := tt.keyName mockUseCase.EXPECT(). - Delete(mock.Anything, testKeyID). + Delete(mock.Anything, testKeyName). Return(tt.expectedErr). Once() mockMetrics.On("RecordOperation", mock.Anything, "tokenization", "tokenization_key_delete", tt.expectedStatus). @@ -401,7 +401,7 @@ func TestTokenizationKeyUseCaseWithMetrics_Delete(t *testing.T) { decorator := NewTokenizationKeyUseCaseWithMetrics(mockUseCase, mockMetrics) - err := decorator.Delete(context.Background(), testKeyID) + err := decorator.Delete(context.Background(), testKeyName) if tt.expectedErr != nil { assert.Error(t, err) diff --git a/internal/tokenization/usecase/tokenization_key_usecase.go b/internal/tokenization/usecase/tokenization_key_usecase.go index 45b722c..7b75fff 100644 --- a/internal/tokenization/usecase/tokenization_key_usecase.go +++ b/internal/tokenization/usecase/tokenization_key_usecase.go @@ -172,9 +172,9 @@ func (t *tokenizationKeyUseCase) Rotate( return newKey, nil } -// Delete soft-deletes a tokenization key and all its versions by setting its deleted_at timestamp. -func (t *tokenizationKeyUseCase) Delete(ctx context.Context, keyID uuid.UUID) error { - err := t.tokenizationKeyRepo.Delete(ctx, keyID) +// Delete soft deletes a tokenization key and all its versions by name. +func (t *tokenizationKeyUseCase) Delete(ctx context.Context, name string) error { + err := t.tokenizationKeyRepo.Delete(ctx, name) if err != nil { return apperrors.Wrap(err, "failed to delete tokenization key") } diff --git a/internal/tokenization/usecase/tokenization_key_usecase_test.go b/internal/tokenization/usecase/tokenization_key_usecase_test.go index 3f32dd6..ecadc61 100644 --- a/internal/tokenization/usecase/tokenization_key_usecase_test.go +++ b/internal/tokenization/usecase/tokenization_key_usecase_test.go @@ -547,11 +547,11 @@ func TestTokenizationKeyUseCase_Delete(t *testing.T) { kekChain := tokenizationTesting.CreateKekChain(masterKey) defer kekChain.Close() - keyID := uuid.Must(uuid.NewV7()) + keyName := "test-key" // Setup expectations mockTokenizationKeyRepo.EXPECT(). - Delete(ctx, keyID). + Delete(ctx, keyName). Return(nil). Once() @@ -563,7 +563,7 @@ func TestTokenizationKeyUseCase_Delete(t *testing.T) { mockKeyManager, kekChain, ) - err := uc.Delete(ctx, keyID) + err := uc.Delete(ctx, keyName) // Assert assert.NoError(t, err) @@ -581,12 +581,12 @@ func TestTokenizationKeyUseCase_Delete(t *testing.T) { kekChain := tokenizationTesting.CreateKekChain(masterKey) defer kekChain.Close() - keyID := uuid.Must(uuid.NewV7()) + keyName := "test-key" expectedError := errors.New("database error") // Setup expectations mockTokenizationKeyRepo.EXPECT(). - Delete(ctx, keyID). + Delete(ctx, keyName). Return(expectedError). Once() @@ -598,7 +598,7 @@ func TestTokenizationKeyUseCase_Delete(t *testing.T) { mockKeyManager, kekChain, ) - err := uc.Delete(ctx, keyID) + err := uc.Delete(ctx, keyName) // Assert assert.Error(t, err) diff --git a/test/integration/tokenization_flow_test.go b/test/integration/tokenization_flow_test.go index a514d09..7e5cc5a 100644 --- a/test/integration/tokenization_flow_test.go +++ b/test/integration/tokenization_flow_test.go @@ -44,7 +44,6 @@ func TestIntegration_Tokenization_CompleteFlow(t *testing.T) { tokenizationKeyName1 = "integration-test-key-uuid" tokenizationKeyName2 = "integration-test-key-deterministic" tokenizationKeyID1 uuid.UUID - tokenizationKeyID2 uuid.UUID testToken string deterministicToken1 string deterministicToken2 string @@ -229,11 +228,6 @@ func TestIntegration_Tokenization_CompleteFlow(t *testing.T) { assert.Equal(t, uint(1), response.Version) assert.Equal(t, "alphanumeric", response.FormatType) assert.True(t, response.IsDeterministic) - - // Store ID for later operations - parsedID, err := uuid.Parse(response.ID) - require.NoError(t, err) - tokenizationKeyID2 = parsedID }) // [8/12] Test POST /v1/tokenization/keys/:name/tokenize - Deterministic tokenization @@ -387,12 +381,12 @@ func TestIntegration_Tokenization_CompleteFlow(t *testing.T) { assert.Equal(t, base64.StdEncoding.EncodeToString(newPlaintext), detokenizeResponse.Plaintext) }) - // [12/12] Test DELETE /v1/tokenization/keys/:id - Delete tokenization key + // [12/12] Test DELETE /v1/tokenization/keys/:name - Delete tokenization key t.Run("12_DeleteTokenizationKey", func(t *testing.T) { resp, body := ctx.makeRequest( t, http.MethodDelete, - "/v1/tokenization/keys/"+tokenizationKeyID2.String(), + "/v1/tokenization/keys/"+tokenizationKeyName2, nil, true, )