Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions internal/tokenization/domain/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,10 @@ var (
errors.ErrInvalidInput,
"tokenization key DEK ID cannot be nil",
)

// ErrTokenizationKeyCreatedAtInvalid indicates the creation timestamp is invalid (zero time).
ErrTokenizationKeyCreatedAtInvalid = errors.Wrap(
errors.ErrInvalidInput,
"tokenization key creation timestamp cannot be zero",
)
)
2 changes: 1 addition & 1 deletion internal/tokenization/domain/tokenization_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (tk *TokenizationKey) Validate() error {
return ErrTokenizationKeyDekIDInvalid
}
if tk.CreatedAt.IsZero() {
return ErrInvalidFormatType // Using existing error for now
return ErrTokenizationKeyCreatedAtInvalid
}
return nil
}
73 changes: 0 additions & 73 deletions internal/tokenization/repository/mysql/mysql_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,79 +221,6 @@ func (m *MySQLTokenizationKeyRepository) GetByNameAndVersion(
return &key, nil
}

// List retrieves tokenization keys ordered by name ascending with pagination.
// Returns the latest version for each key.
func (m *MySQLTokenizationKeyRepository) List(
ctx context.Context,
offset, limit int,
) ([]*tokenizationDomain.TokenizationKey, error) {
querier := database.GetTx(ctx, m.db)

query := `
SELECT tk.id, tk.name, tk.version, tk.format_type, tk.is_deterministic, tk.salt, tk.dek_id, tk.created_at, tk.deleted_at
FROM tokenization_keys tk
INNER JOIN (
SELECT name, MAX(version) as max_version
FROM tokenization_keys
WHERE deleted_at IS NULL
GROUP BY name
ORDER BY name ASC
LIMIT ? OFFSET ?
) latest ON tk.name = latest.name AND tk.version = latest.max_version
ORDER BY tk.name ASC`

rows, err := querier.QueryContext(ctx, query, limit, offset)
if err != nil {
return nil, apperrors.Wrap(err, "failed to list tokenization keys")
}
defer func() {
_ = rows.Close()
}()

var keys []*tokenizationDomain.TokenizationKey
for rows.Next() {
var key tokenizationDomain.TokenizationKey
var id, dekID []byte
var formatType string

err := rows.Scan(
&id,
&key.Name,
&key.Version,
&formatType,
&key.IsDeterministic,
&key.Salt,
&dekID,
&key.CreatedAt,
&key.DeletedAt,
)
if err != nil {
return nil, apperrors.Wrap(err, "failed to scan tokenization key")
}

if err := key.ID.UnmarshalBinary(id); err != nil {
return nil, apperrors.Wrap(err, "failed to unmarshal tokenization key id")
}

if err := key.DekID.UnmarshalBinary(dekID); err != nil {
return nil, apperrors.Wrap(err, "failed to unmarshal dek id")
}

key.FormatType = tokenizationDomain.FormatType(formatType)
keys = append(keys, &key)
}

if err := rows.Err(); err != nil {
return nil, apperrors.Wrap(err, "error iterating tokenization keys")
}

if keys == nil {
keys = make([]*tokenizationDomain.TokenizationKey, 0)
}

return keys, nil
}

// ListCursor retrieves tokenization keys ordered by name ascending using cursor-based pagination.
// Returns the latest version for each key.
func (m *MySQLTokenizationKeyRepository) ListCursor(
Expand Down
90 changes: 90 additions & 0 deletions internal/tokenization/repository/mysql/mysql_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,96 @@ func TestMySQLTokenizationKeyRepository_GetByName(t *testing.T) {
assert.Equal(t, uint(2), retrieved.Version)
}

func TestMySQLTokenizationKeyRepository_Get(t *testing.T) {
db := testutil.SetupMySQLDB(t)
defer testutil.TeardownDB(t, db)
defer testutil.CleanupMySQLDB(t, db)

repo := NewMySQLTokenizationKeyRepository(db)
ctx := context.Background()

// Create DEK dependency
_, dekID := createKekAndDekMySQL(t, db)

key := &tokenizationDomain.TokenizationKey{
ID: uuid.Must(uuid.NewV7()),
Name: "get-test",
Version: 1,
FormatType: tokenizationDomain.FormatUUID,
IsDeterministic: false,
Salt: []byte("test-salt-32-bytes-long-12345678"),
DekID: dekID,
CreatedAt: time.Now().UTC(),
DeletedAt: nil,
}

err := repo.Create(ctx, key)
require.NoError(t, err)

// Test Get
retrieved, err := repo.Get(ctx, key.ID)
require.NoError(t, err)
assert.Equal(t, key.ID, retrieved.ID)
assert.Equal(t, key.Name, retrieved.Name)

// Test Get NotFound
_, err = repo.Get(ctx, uuid.Must(uuid.NewV7()))
assert.Error(t, err)
assert.ErrorIs(t, err, tokenizationDomain.ErrTokenizationKeyNotFound)
}

func TestMySQLTokenizationKeyRepository_GetByNameAndVersion(t *testing.T) {
db := testutil.SetupMySQLDB(t)
defer testutil.TeardownDB(t, db)
defer testutil.CleanupMySQLDB(t, db)

repo := NewMySQLTokenizationKeyRepository(db)
ctx := context.Background()

// Create DEK dependency
_, dekID := createKekAndDekMySQL(t, db)

key1 := &tokenizationDomain.TokenizationKey{
ID: uuid.Must(uuid.NewV7()),
Name: "version-test",
Version: 1,
FormatType: tokenizationDomain.FormatUUID,
IsDeterministic: false,
Salt: []byte("test-salt-32-bytes-long-12345678"),
DekID: dekID,
CreatedAt: time.Now().UTC(),
DeletedAt: nil,
}
require.NoError(t, repo.Create(ctx, key1))

key2 := &tokenizationDomain.TokenizationKey{
ID: uuid.Must(uuid.NewV7()),
Name: "version-test",
Version: 2,
FormatType: tokenizationDomain.FormatUUID,
IsDeterministic: false,
Salt: []byte("test-salt-32-bytes-long-12345678"),
DekID: dekID,
CreatedAt: time.Now().UTC(),
DeletedAt: nil,
}
require.NoError(t, repo.Create(ctx, key2))

// Test GetByNameAndVersion
retrieved, err := repo.GetByNameAndVersion(ctx, "version-test", 1)
require.NoError(t, err)
assert.Equal(t, key1.ID, retrieved.ID)

retrieved, err = repo.GetByNameAndVersion(ctx, "version-test", 2)
require.NoError(t, err)
assert.Equal(t, key2.ID, retrieved.ID)

// Test NotFound
_, err = repo.GetByNameAndVersion(ctx, "version-test", 3)
assert.Error(t, err)
assert.ErrorIs(t, err, tokenizationDomain.ErrTokenizationKeyNotFound)
}

func TestMySQLTokenizationKeyRepository_Delete(t *testing.T) {
db := testutil.SetupMySQLDB(t)
defer testutil.TeardownDB(t, db)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,70 +176,6 @@ func (p *PostgreSQLTokenizationKeyRepository) GetByNameAndVersion(
return &key, nil
}

// List retrieves tokenization keys ordered by name ascending with pagination.
// Returns the latest version for each key.
func (p *PostgreSQLTokenizationKeyRepository) List(
ctx context.Context,
offset, limit int,
) ([]*tokenizationDomain.TokenizationKey, error) {
querier := database.GetTx(ctx, p.db)

query := `
SELECT tk.id, tk.name, tk.version, tk.format_type, tk.is_deterministic, tk.salt, tk.dek_id, tk.created_at, tk.deleted_at
FROM tokenization_keys tk
INNER JOIN (
SELECT name, MAX(version) as max_version
FROM tokenization_keys
WHERE deleted_at IS NULL
GROUP BY name
ORDER BY name ASC
LIMIT $1 OFFSET $2
) latest ON tk.name = latest.name AND tk.version = latest.max_version
ORDER BY tk.name ASC`

rows, err := querier.QueryContext(ctx, query, limit, offset)
if err != nil {
return nil, apperrors.Wrap(err, "failed to list tokenization keys")
}
defer func() {
_ = rows.Close()
}()

var keys []*tokenizationDomain.TokenizationKey
for rows.Next() {
var key tokenizationDomain.TokenizationKey
var formatType string

err := rows.Scan(
&key.ID,
&key.Name,
&key.Version,
&formatType,
&key.IsDeterministic,
&key.Salt,
&key.DekID,
&key.CreatedAt,
&key.DeletedAt,
)
if err != nil {
return nil, apperrors.Wrap(err, "failed to scan tokenization key")
}

key.FormatType = tokenizationDomain.FormatType(formatType)
keys = append(keys, &key)
}

if err := rows.Err(); err != nil {
return nil, apperrors.Wrap(err, "error iterating tokenization keys")
}

if keys == nil {
keys = make([]*tokenizationDomain.TokenizationKey, 0)
}

return keys, nil
}

// ListCursor retrieves tokenization keys ordered by name ascending using cursor-based pagination.
// Returns the latest version for each key.
func (p *PostgreSQLTokenizationKeyRepository) ListCursor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,96 @@ func TestPostgreSQLTokenizationKeyRepository_GetByName(t *testing.T) {
assert.Equal(t, uint(2), retrieved.Version)
}

func TestPostgreSQLTokenizationKeyRepository_Get(t *testing.T) {
db := testutil.SetupPostgresDB(t)
defer testutil.TeardownDB(t, db)
defer testutil.CleanupPostgresDB(t, db)

repo := NewPostgreSQLTokenizationKeyRepository(db)
ctx := context.Background()

// Create DEK dependency
_, dekID := createKekAndDek(t, db)

key := &tokenizationDomain.TokenizationKey{
ID: uuid.Must(uuid.NewV7()),
Name: "get-test",
Version: 1,
FormatType: tokenizationDomain.FormatUUID,
IsDeterministic: false,
Salt: []byte("test-salt-32-bytes-long-12345678"),
DekID: dekID,
CreatedAt: time.Now().UTC(),
DeletedAt: nil,
}

err := repo.Create(ctx, key)
require.NoError(t, err)

// Test Get
retrieved, err := repo.Get(ctx, key.ID)
require.NoError(t, err)
assert.Equal(t, key.ID, retrieved.ID)
assert.Equal(t, key.Name, retrieved.Name)

// Test Get NotFound
_, err = repo.Get(ctx, uuid.Must(uuid.NewV7()))
assert.Error(t, err)
assert.ErrorIs(t, err, tokenizationDomain.ErrTokenizationKeyNotFound)
}

func TestPostgreSQLTokenizationKeyRepository_GetByNameAndVersion(t *testing.T) {
db := testutil.SetupPostgresDB(t)
defer testutil.TeardownDB(t, db)
defer testutil.CleanupPostgresDB(t, db)

repo := NewPostgreSQLTokenizationKeyRepository(db)
ctx := context.Background()

// Create DEK dependency
_, dekID := createKekAndDek(t, db)

key1 := &tokenizationDomain.TokenizationKey{
ID: uuid.Must(uuid.NewV7()),
Name: "version-test",
Version: 1,
FormatType: tokenizationDomain.FormatUUID,
IsDeterministic: false,
Salt: []byte("test-salt-32-bytes-long-12345678"),
DekID: dekID,
CreatedAt: time.Now().UTC(),
DeletedAt: nil,
}
require.NoError(t, repo.Create(ctx, key1))

key2 := &tokenizationDomain.TokenizationKey{
ID: uuid.Must(uuid.NewV7()),
Name: "version-test",
Version: 2,
FormatType: tokenizationDomain.FormatUUID,
IsDeterministic: false,
Salt: []byte("test-salt-32-bytes-long-12345678"),
DekID: dekID,
CreatedAt: time.Now().UTC(),
DeletedAt: nil,
}
require.NoError(t, repo.Create(ctx, key2))

// Test GetByNameAndVersion
retrieved, err := repo.GetByNameAndVersion(ctx, "version-test", 1)
require.NoError(t, err)
assert.Equal(t, key1.ID, retrieved.ID)

retrieved, err = repo.GetByNameAndVersion(ctx, "version-test", 2)
require.NoError(t, err)
assert.Equal(t, key2.ID, retrieved.ID)

// Test NotFound
_, err = repo.GetByNameAndVersion(ctx, "version-test", 3)
assert.Error(t, err)
assert.ErrorIs(t, err, tokenizationDomain.ErrTokenizationKeyNotFound)
}

func TestPostgreSQLTokenizationKeyRepository_Delete(t *testing.T) {
db := testutil.SetupPostgresDB(t)
defer testutil.TeardownDB(t, db)
Expand Down
Loading
Loading