Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add organization_id condition for list api key #1373

Merged
merged 11 commits into from
Dec 13, 2024
10 changes: 10 additions & 0 deletions api-description/web-api.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,9 @@ paths:
- NAME
- CREATED_AT
- UPDATED_AT
- ROLE
- ENVIRONMENT
- STATE
default: DEFAULT
- name: orderDirection
in: query
Expand Down Expand Up @@ -962,6 +965,10 @@ paths:
items:
type: string
collectionFormat: multi
- name: organizationId
in: query
required: true
type: string
tags:
- API Key
/v1/account/my_organizations:
Expand Down Expand Up @@ -3381,6 +3388,9 @@ definitions:
- NAME
- CREATED_AT
- UPDATED_AT
- ROLE
- ENVIRONMENT
- STATE
default: DEFAULT
accountListAPIKeysRequestOrderDirection:
type: string
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.

15 changes: 15 additions & 0 deletions manifests/bucketeer/values.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,21 @@ batch-server:
- name: mau-partition-creator
jobId: MauPartitionCreator
schedule: "0 2 2 * *"
- name: feature-flag-cacher
jobId: FeatureFlagCacher
schedule: "* * * * *"
- name: segment-user-cacher
jobId: SegmentUserCacher
schedule: "* * * * *"
- name: api-key-cacher
jobId: ApiKeyCacher
schedule: "* * * * *"
- name: experiment-cacher
jobId: ExperimentCacher
schedule: "* * * * *"
- name: auto-ops-rules-cacher
jobId: AutoOpsRulesCacher
schedule: "* * * * *"


subscriber:
Expand Down
23 changes: 20 additions & 3 deletions pkg/account/api/api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,13 +519,24 @@ func (s *AccountService) ListAPIKeys(
req *proto.ListAPIKeysRequest,
) (*proto.ListAPIKeysResponse, error) {
localizer := locale.NewLocalizer(ctx)
_, err := s.checkEnvironmentRole(
ctx, proto.AccountV2_Role_Environment_VIEWER,
req.EnvironmentId, localizer)
_, err := s.checkOrganizationRole(
ctx, proto.AccountV2_Role_Organization_MEMBER,
req.OrganizationId, localizer)
if err != nil {
return nil, err
}
if req.OrganizationId == "" {
dt, err := statusInvalidListAPIKeyRequest.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Message: localizer.MustLocalizeWithTemplate(locale.RequiredFieldTemplate, "organization_id"),
})
if err != nil {
return nil, statusInternal.Err()
}
return nil, dt.Err()
}
whereParts := []mysql.WherePart{}
whereParts = append(whereParts, mysql.NewFilter("environment_v2.organization_id", "=", req.OrganizationId))
if len(req.EnvironmentIds) > 0 {
environmentIds := make([]interface{}, 0, len(req.EnvironmentIds))
for _, id := range req.EnvironmentIds {
Expand Down Expand Up @@ -615,6 +626,12 @@ func (s *AccountService) newAPIKeyListOrders(
column = "api_key.created_at"
case proto.ListAPIKeysRequest_UPDATED_AT:
column = "api_key.updated_at"
case proto.ListAPIKeysRequest_ROLE:
column = "api_key.role"
case proto.ListAPIKeysRequest_ENVIRONMENT:
column = "environment_v2.name"
case proto.ListAPIKeysRequest_STATE:
column = "api_key.disabled"
default:
dt, err := statusInvalidOrderBy.WithDetails(&errdetails.LocalizedMessage{
Locale: localizer.GetLocale(),
Expand Down
39 changes: 28 additions & 11 deletions pkg/account/api/api_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,13 @@ func TestListAPIKeysMySQL(t *testing.T) {
getExpectedErr func(localizer locale.Localizer) error
}{
{
desc: "errInvalidCursor",
context: createContextWithDefaultToken(t, true),
input: &accountproto.ListAPIKeysRequest{Cursor: "XXX"},
desc: "errInvalidCursor",
context: createContextWithDefaultToken(t, true),
input: &accountproto.ListAPIKeysRequest{
EnvironmentIds: []string{"ns0"},
OrganizationId: "org0",
Cursor: "XXX",
},
expected: nil,
getExpectedErr: func(localizer locale.Localizer) error {
return createError(localizer, statusInvalidCursor, localizer.MustLocalizeWithTemplate(locale.InvalidArgumentError, "cursor"))
Expand All @@ -686,7 +690,10 @@ func TestListAPIKeysMySQL(t *testing.T) {
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
).Return(nil, 0, int64(0), errors.New("error"))
},
input: &accountproto.ListAPIKeysRequest{},
input: &accountproto.ListAPIKeysRequest{
EnvironmentIds: []string{"ns0"},
OrganizationId: "org0",
},
expected: nil,
getExpectedErr: func(localizer locale.Localizer) error {
return createError(localizer, statusInternal, localizer.MustLocalize(locale.InternalServerError))
Expand All @@ -696,7 +703,7 @@ func TestListAPIKeysMySQL(t *testing.T) {
desc: "errPermissionDenied",
context: createContextWithDefaultToken(t, false),
setup: func(s *AccountService) {
s.accountStorage.(*accstoragemock.MockAccountStorage).EXPECT().GetAccountV2ByEnvironmentID(
s.accountStorage.(*accstoragemock.MockAccountStorage).EXPECT().GetAccountV2(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(&domain.AccountV2{
AccountV2: &accountproto.AccountV2{
Expand All @@ -711,7 +718,10 @@ func TestListAPIKeysMySQL(t *testing.T) {
},
}, nil).AnyTimes()
},
input: &accountproto.ListAPIKeysRequest{EnvironmentId: "ns0"},
input: &accountproto.ListAPIKeysRequest{
EnvironmentIds: []string{"ns0"},
OrganizationId: "org0",
},
expected: nil,
getExpectedErr: func(localizer locale.Localizer) error {
return createError(localizer, statusPermissionDenied, localizer.MustLocalize(locale.PermissionDenied))
Expand All @@ -725,22 +735,26 @@ func TestListAPIKeysMySQL(t *testing.T) {
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
).Return([]*accountproto.APIKey{}, 0, int64(0), nil)
},
input: &accountproto.ListAPIKeysRequest{PageSize: 2, Cursor: ""},
input: &accountproto.ListAPIKeysRequest{
OrganizationId: "org0",
PageSize: 2,
Cursor: "",
},
expected: &accountproto.ListAPIKeysResponse{ApiKeys: []*accountproto.APIKey{}, Cursor: "0"},
getExpectedErr: func(localizer locale.Localizer) error {
return nil
},
},
{
desc: "success with viewer account",
desc: "success with admin account",
context: createContextWithDefaultToken(t, false),
setup: func(s *AccountService) {
s.accountStorage.(*accstoragemock.MockAccountStorage).EXPECT().GetAccountV2ByEnvironmentID(
s.accountStorage.(*accstoragemock.MockAccountStorage).EXPECT().GetAccountV2(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(&domain.AccountV2{
AccountV2: &accountproto.AccountV2{
Email: "email",
OrganizationRole: accountproto.AccountV2_Role_Organization_MEMBER,
OrganizationRole: accountproto.AccountV2_Role_Organization_ADMIN,
EnvironmentRoles: []*accountproto.AccountV2_EnvironmentRole{
{
EnvironmentId: "ns0",
Expand All @@ -753,7 +767,10 @@ func TestListAPIKeysMySQL(t *testing.T) {
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
).Return([]*accountproto.APIKey{}, 0, int64(0), nil)
},
input: &accountproto.ListAPIKeysRequest{EnvironmentId: "ns0"},
input: &accountproto.ListAPIKeysRequest{
EnvironmentIds: []string{"ns0"},
OrganizationId: "org0",
},
expected: &accountproto.ListAPIKeysResponse{ApiKeys: []*accountproto.APIKey{}, Cursor: "0"},
getExpectedErr: func(localizer locale.Localizer) error {
return nil
Expand Down
5 changes: 3 additions & 2 deletions pkg/account/api/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var (
codes.InvalidArgument,
"account: search filter target type is required",
)
statusSearchFilterIDIsEmpty = gstatus.New(codes.InvalidArgument, "account: search filter ID is empty")
statusSearchFilterIDNotFound = gstatus.New(codes.InvalidArgument, "account: search filter ID not found")
statusSearchFilterIDIsEmpty = gstatus.New(codes.InvalidArgument, "account: search filter ID is empty")
statusSearchFilterIDNotFound = gstatus.New(codes.InvalidArgument, "account: search filter ID not found")
statusInvalidListAPIKeyRequest = gstatus.New(codes.InvalidArgument, "account: invalid list api key request")
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ SELECT
COUNT(1)
FROM
api_key
LEFT JOIN environment_v2 ON api_key.environment_id = environment_v2.id
%s
Binary file modified proto/account/proto_descriptor.pb
Binary file not shown.
Loading
Loading