Skip to content

Commit

Permalink
perf: improve get environment api key latency
Browse files Browse the repository at this point in the history
Signed-off-by: Alessandro Yuichi Okimoto <[email protected]>
  • Loading branch information
cre8ivejp committed Dec 13, 2024
1 parent 01b31d0 commit 2b6abad
Show file tree
Hide file tree
Showing 27 changed files with 2,313 additions and 1,169 deletions.
52 changes: 52 additions & 0 deletions api-description/web-api.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,53 @@ paths:
tags:
- API Key
/v1/account/get_environment_api_key:
get:
summary: Get Environment API Key
description: Get an environment API Key.
operationId: web.v1.account.get_environment_api_key
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/accountGetEnvironmentAPIKeyResponse'
"400":
description: Returned for bad requests that may have failed validation.
schema:
$ref: '#/definitions/rpcStatus'
examples:
application/json:
code: 3
details: []
message: invalid arguments error
"401":
description: Request could not be authenticated (authentication required).
schema:
$ref: '#/definitions/rpcStatus'
examples:
application/json:
code: 16
details: []
message: not authenticated
"503":
description: Returned for internal errors.
schema:
$ref: '#/definitions/rpcStatus'
examples:
application/json:
code: 13
details: []
message: internal
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/rpcStatus'
parameters:
- name: apiKey
in: query
required: false
type: string
tags:
- API Key
post:
summary: Get Environment API Key
description: Get an environment API Key.
Expand Down Expand Up @@ -3350,6 +3397,11 @@ definitions:
properties:
account:
$ref: '#/definitions/accountAccountV2'
accountGetEnvironmentAPIKeyResponse:
type: object
properties:
environmentApiKey:
$ref: '#/definitions/accountEnvironmentAPIKey'
accountGetMeRequest:
type: object
properties:
Expand Down
2 changes: 1 addition & 1 deletion manifests/bucketeer/charts/api/values.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion manifests/bucketeer/charts/web/values.yaml

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions pkg/account/api/api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,62 @@ func (s *AccountService) GetAPIKeyBySearchingAllEnvironments(
return nil, dt.Err()
}

func (s *AccountService) GetEnvironmentAPIKey(
ctx context.Context,
req *proto.GetEnvironmentAPIKeyRequest,
) (*proto.GetEnvironmentAPIKeyResponse, error) {
localizer := locale.NewLocalizer(ctx)
_, err := s.checkSystemAdminRole(ctx, localizer)
if err != nil {
return nil, err
}
if req.ApiKey == "" {
dt, err := statusMissingAPIKeyID.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalizeWithTemplate(locale.RequiredFieldTemplate, "api_key_id"),
})
if err != nil {
return nil, statusInternal.Err()
}
return nil, dt.Err()
}
envAPIKey, err := s.accountStorage.GetEnvironmentAPIKey(ctx, req.ApiKey)
if err != nil {
if errors.Is(err, v2as.ErrAPIKeyNotFound) {
dt, err := statusNotFound.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalize(locale.NotFoundError),
})
if err != nil {
return nil, statusInternal.Err()
}
return nil, dt.Err()
}
s.logger.Error(
"Failed to get environment api key",
log.FieldsFromImcomingContext(ctx).AddFields(
zap.Error(err),
zap.String("apiKey", req.ApiKey),
)...,
)
dt, err := statusInternal.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalize(locale.InternalServerError),
})
if err != nil {
return nil, statusInternal.Err()
}
return nil, dt.Err()
}
// for security, obfuscate the returned key
shadowLen := int(float64(len(envAPIKey.ApiKey.ApiKey)) * apiKeyShadowPercentage)
envAPIKey.ApiKey.ApiKey = envAPIKey.ApiKey.ApiKey[shadowLen:]

return &proto.GetEnvironmentAPIKeyResponse{
EnvironmentApiKey: envAPIKey.EnvironmentAPIKey,
}, nil
}

func (s *AccountService) UpdateAPIKey(
ctx context.Context,
req *proto.UpdateAPIKeyRequest,
Expand Down
20 changes: 20 additions & 0 deletions pkg/account/client/mock/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/account/domain/api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ type APIKey struct {
*proto.APIKey
}

type EnvironmentAPIKey struct {
*proto.EnvironmentAPIKey
}

func NewAPIKey(
name string,
role proto.APIKey_Role,
Expand Down
115 changes: 115 additions & 0 deletions pkg/account/storage/v2/api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/bucketeer-io/bucketeer/pkg/account/domain"
"github.com/bucketeer-io/bucketeer/pkg/storage/v2/mysql"
proto "github.com/bucketeer-io/bucketeer/proto/account"
envproto "github.com/bucketeer-io/bucketeer/proto/environment"
)

var (
Expand All @@ -42,6 +43,10 @@ var (
selectAPIKeyV2CountSQLQuery string
//go:embed sql/api_key_v2/select_api_key_v2_by_api_key.sql
selectAPIKeyV2ByAPIKeySQLQuery string
//go:embed sql/api_key_v2/select_environment_api_key_v2.sql
selectEnvironmentAPIKeySQLQuery string
//go:embed sql/api_key_v2/select_all_environment_api_keys_v2.sql
selectAllEnvironmentAPIKeysSQLQuery string
//go:embed sql/api_key_v2/select_api_key_v2_by_id.sql
selectAPIKeyV2ByIDSQLQuery string
)
Expand Down Expand Up @@ -158,6 +163,116 @@ func (s *accountStorage) GetAPIKeyByAPIKey(
return &domain.APIKey{APIKey: &apiKeyDB}, nil
}

func (s *accountStorage) ListAllEnvironmentAPIKeys(
ctx context.Context,
) ([]*domain.EnvironmentAPIKey, error) {
rows, err := s.qe(ctx).QueryContext(ctx, selectAllEnvironmentAPIKeysSQLQuery)
if err != nil {
return nil, err
}
defer rows.Close()
envApiKeys := []*domain.EnvironmentAPIKey{}
for rows.Next() {
apiKeyDB := proto.APIKey{}
envDB := envproto.EnvironmentV2{}
envApiKeyDB := proto.EnvironmentAPIKey{}
var role int32
err := rows.Scan(
// API Key columns
&apiKeyDB.Id,
&apiKeyDB.Name,
&role,
&apiKeyDB.Disabled,
&apiKeyDB.CreatedAt,
&apiKeyDB.UpdatedAt,
&apiKeyDB.Description,
&apiKeyDB.ApiKey,
&apiKeyDB.Maintainer,

// Environment columns
&envDB.Id,
&envDB.Name,
&envDB.UrlCode,
&envDB.Description,
&envDB.ProjectId,
&envDB.OrganizationId,
&envDB.Archived,
&envDB.RequireComment,
&envDB.CreatedAt,
&envDB.UpdatedAt,

// Project columns
&envApiKeyDB.ProjectId,
&envApiKeyDB.ProjectUrlCode,
&envApiKeyDB.EnvironmentDisabled,
)
if err != nil {
return nil, err
}
envApiKeyDB.ApiKey = &apiKeyDB
envApiKeyDB.ApiKey.Role = proto.APIKey_Role(role)
envApiKeyDB.Environment = &envDB
envApiKeys = append(envApiKeys, &domain.EnvironmentAPIKey{EnvironmentAPIKey: &envApiKeyDB})
}
if rows.Err() != nil {
return nil, err
}
return envApiKeys, nil
}

func (s *accountStorage) GetEnvironmentAPIKey(
ctx context.Context,
apiKey string,
) (*domain.EnvironmentAPIKey, error) {
apiKeyDB := proto.APIKey{}
envDB := envproto.EnvironmentV2{}
envApiKeyDB := proto.EnvironmentAPIKey{}
var role int32
err := s.qe(ctx).QueryRowContext(
ctx,
selectEnvironmentAPIKeySQLQuery,
apiKey,
).Scan(
// API Key columns
&apiKeyDB.Id,
&apiKeyDB.Name,
&role,
&apiKeyDB.Disabled,
&apiKeyDB.CreatedAt,
&apiKeyDB.UpdatedAt,
&apiKeyDB.Description,
&apiKeyDB.ApiKey,
&apiKeyDB.Maintainer,

// Environment columns
&envDB.Id,
&envDB.Name,
&envDB.UrlCode,
&envDB.Description,
&envDB.ProjectId,
&envDB.OrganizationId,
&envDB.Archived,
&envDB.RequireComment,
&envDB.CreatedAt,
&envDB.UpdatedAt,

// Project columns
&envApiKeyDB.ProjectId,
&envApiKeyDB.ProjectUrlCode,
&envApiKeyDB.EnvironmentDisabled,
)
if err != nil {
if errors.Is(err, mysql.ErrNoRows) {
return nil, ErrAPIKeyNotFound
}
return nil, err
}
envApiKeyDB.ApiKey = &apiKeyDB
envApiKeyDB.ApiKey.Role = proto.APIKey_Role(role)
envApiKeyDB.Environment = &envDB
return &domain.EnvironmentAPIKey{EnvironmentAPIKey: &envApiKeyDB}, nil
}

func (s *accountStorage) ListAPIKeys(
ctx context.Context,
whereParts []mysql.WherePart,
Expand Down
30 changes: 30 additions & 0 deletions pkg/account/storage/v2/mock/storage.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
SELECT
ak.id AS api_key_id,
ak.name AS api_key_name,
ak.role AS api_key_role,
ak.disabled AS api_key_disabled,
ak.created_at AS api_key_created_at,
ak.updated_at AS api_key_updated_at,
ak.description AS api_key_description,
ak.api_key AS api_key_key,
ak.maintainer AS api_key_maintainer,

env.id AS environment_id,
env.name AS environment_name,
env.url_code AS environment_url_code,
env.description AS environment_description,
env.project_id AS environment_project_id,
env.organization_id AS environment_organization_id,
env.archived AS environment_archived,
env.require_comment AS environment_require_comment,
env.created_at AS environment_created_at,
env.updated_at AS environment_updated_at,

proj.id AS project_id,
proj.url_code AS project_url_code,
proj.disabled AS project_disabled
FROM api_key ak
JOIN environment_v2 env ON ak.environment_id = env.id
JOIN project proj ON env.project_id = proj.id
Loading

0 comments on commit 2b6abad

Please sign in to comment.