web/satellite: API keys paged backend (#2839)

This commit is contained in:
Bogdan Artemenko 2019-09-12 17:19:30 +03:00 committed by GitHub
parent 0c1798407c
commit bb6086aeab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 413 additions and 71 deletions

View File

@ -14,8 +14,8 @@ import (
// //
// architecture: Database // architecture: Database
type APIKeys interface { type APIKeys interface {
// GetByProjectID retrieves list of APIKeys for given projectID // GetPagedByProjectID is a method for querying API keys from the database by projectID and cursor
GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]APIKeyInfo, error) GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor APIKeyCursor) (akp *APIKeyPage, err error)
// Get retrieves APIKeyInfo with given ID // Get retrieves APIKeyInfo with given ID
Get(ctx context.Context, id uuid.UUID) (*APIKeyInfo, error) Get(ctx context.Context, id uuid.UUID) (*APIKeyInfo, error)
// GetByHead retrieves APIKeyInfo for given key head // GetByHead retrieves APIKeyInfo for given key head
@ -37,3 +37,37 @@ type APIKeyInfo struct {
Secret []byte `json:"-"` Secret []byte `json:"-"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
} }
// APIKeyCursor holds info for api keys cursor pagination
type APIKeyCursor struct {
Search string
Limit uint
Page uint
Order APIKeyOrder
OrderDirection OrderDirection
}
// APIKeyPage represent api key page result
type APIKeyPage struct {
APIKeys []APIKeyInfo
Search string
Limit uint
Order APIKeyOrder
OrderDirection OrderDirection
Offset uint64
PageCount uint
CurrentPage uint
TotalCount uint64
}
// APIKeyOrder is used for querying api keys in specified order
type APIKeyOrder uint8
const (
// KeyName indicates that we should order by key name
KeyName APIKeyOrder = 1
// CreationDate indicates that we should order by creation date
CreationDate APIKeyOrder = 2
)

View File

@ -48,34 +48,65 @@ func TestApiKeysRepository(t *testing.T) {
} }
}) })
t.Run("GetByProjectID success", func(t *testing.T) { t.Run("GetPagedByProjectID success", func(t *testing.T) {
keys, err := apikeys.GetByProjectID(ctx, project.ID) cursor := console.APIKeyCursor{
assert.NotNil(t, keys) Page: 1,
assert.Equal(t, len(keys), 10) Limit: 10,
Search: "",
}
page, err := apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.NotNil(t, page)
assert.Equal(t, len(page.APIKeys), 10)
assert.NoError(t, err)
})
t.Run("GetPagedByProjectID with limit success", func(t *testing.T) {
cursor := console.APIKeyCursor{
Page: 1,
Limit: 2,
Search: "",
}
page, err := apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.NotNil(t, page)
assert.Equal(t, len(page.APIKeys), 2)
assert.Equal(t, page.PageCount, uint(5))
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("Get By ID success", func(t *testing.T) { t.Run("Get By ID success", func(t *testing.T) {
keys, err := apikeys.GetByProjectID(ctx, project.ID) cursor := console.APIKeyCursor{
assert.NotNil(t, keys) Page: 1,
assert.Equal(t, len(keys), 10) Limit: 10,
Search: "",
}
page, err := apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.NotNil(t, page)
assert.Equal(t, len(page.APIKeys), 10)
assert.NoError(t, err) assert.NoError(t, err)
key, err := apikeys.Get(ctx, keys[0].ID) key, err := apikeys.Get(ctx, page.APIKeys[0].ID)
assert.NotNil(t, key) assert.NotNil(t, key)
assert.Equal(t, keys[0].ID, key.ID) assert.Equal(t, page.APIKeys[0].ID, key.ID)
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("Update success", func(t *testing.T) { t.Run("Update success", func(t *testing.T) {
keys, err := apikeys.GetByProjectID(ctx, project.ID) cursor := console.APIKeyCursor{
assert.NotNil(t, keys) Page: 1,
assert.Equal(t, len(keys), 10) Limit: 10,
Search: "",
}
page, err := apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.NotNil(t, page)
assert.Equal(t, len(page.APIKeys), 10)
assert.NoError(t, err) assert.NoError(t, err)
key, err := apikeys.Get(ctx, keys[0].ID) key, err := apikeys.Get(ctx, page.APIKeys[0].ID)
assert.NotNil(t, key) assert.NotNil(t, key)
assert.Equal(t, keys[0].ID, key.ID) assert.Equal(t, page.APIKeys[0].ID, key.ID)
assert.NoError(t, err) assert.NoError(t, err)
key.Name = "some new name" key.Name = "some new name"
@ -83,21 +114,26 @@ func TestApiKeysRepository(t *testing.T) {
err = apikeys.Update(ctx, *key) err = apikeys.Update(ctx, *key)
assert.NoError(t, err) assert.NoError(t, err)
updatedKey, err := apikeys.Get(ctx, keys[0].ID) updatedKey, err := apikeys.Get(ctx, page.APIKeys[0].ID)
assert.NotNil(t, key) assert.NotNil(t, key)
assert.Equal(t, key.Name, updatedKey.Name) assert.Equal(t, key.Name, updatedKey.Name)
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("Delete success", func(t *testing.T) { t.Run("Delete success", func(t *testing.T) {
keys, err := apikeys.GetByProjectID(ctx, project.ID) cursor := console.APIKeyCursor{
assert.NotNil(t, keys) Page: 1,
assert.Equal(t, len(keys), 10) Limit: 10,
Search: "",
}
page, err := apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.NotNil(t, page)
assert.Equal(t, len(page.APIKeys), 10)
assert.NoError(t, err) assert.NoError(t, err)
key, err := apikeys.Get(ctx, keys[0].ID) key, err := apikeys.Get(ctx, page.APIKeys[0].ID)
assert.NotNil(t, key) assert.NotNil(t, key)
assert.Equal(t, keys[0].ID, key.ID) assert.Equal(t, page.APIKeys[0].ID, key.ID)
assert.NoError(t, err) assert.NoError(t, err)
key.Name = "some new name" key.Name = "some new name"
@ -105,10 +141,23 @@ func TestApiKeysRepository(t *testing.T) {
err = apikeys.Delete(ctx, key.ID) err = apikeys.Delete(ctx, key.ID)
assert.NoError(t, err) assert.NoError(t, err)
keys, err = apikeys.GetByProjectID(ctx, project.ID) page, err = apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.NotNil(t, keys) assert.NotNil(t, page)
assert.Equal(t, len(keys), 9) assert.Equal(t, len(page.APIKeys), 9)
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("GetPageByProjectID with 0 page error", func(t *testing.T) {
cursor := console.APIKeyCursor{
Page: 0,
Limit: 10,
Search: "",
}
page, err := apikeys.GetPagedByProjectID(ctx, project.ID, cursor)
assert.Nil(t, page)
assert.Error(t, err)
})
}) })
} }

View File

@ -58,8 +58,80 @@ func graphqlCreateAPIKey(types *TypeCreator) *graphql.Object {
}) })
} }
func graphqlAPIKeysCursor() *graphql.InputObject {
return graphql.NewInputObject(graphql.InputObjectConfig{
Name: APIKeysCursorInputType,
Fields: graphql.InputObjectConfigFieldMap{
SearchArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
LimitArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
PageArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
OrderArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
OrderDirectionArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
},
})
}
func graphqlAPIKeysPage(types *TypeCreator) *graphql.Object {
return graphql.NewObject(graphql.ObjectConfig{
Name: APIKeysPageType,
Fields: graphql.Fields{
FieldAPIKeys: &graphql.Field{
Type: graphql.NewList(types.apiKeyInfo),
},
SearchArg: &graphql.Field{
Type: graphql.String,
},
LimitArg: &graphql.Field{
Type: graphql.Int,
},
OrderArg: &graphql.Field{
Type: graphql.Int,
},
OrderDirectionArg: &graphql.Field{
Type: graphql.Int,
},
OffsetArg: &graphql.Field{
Type: graphql.Int,
},
FieldPageCount: &graphql.Field{
Type: graphql.Int,
},
FieldCurrentPage: &graphql.Field{
Type: graphql.Int,
},
FieldTotalCount: &graphql.Field{
Type: graphql.Int,
},
},
})
}
// createAPIKey holds macaroon.APIKey and console.APIKeyInfo // createAPIKey holds macaroon.APIKey and console.APIKeyInfo
type createAPIKey struct { type createAPIKey struct {
Key string Key string
KeyInfo *console.APIKeyInfo KeyInfo *console.APIKeyInfo
} }
type apiKeysPage struct {
APIKeys []console.APIKeyInfo
Search string
Limit uint
Order int
OrderDirection int
Offset uint64
PageCount uint
CurrentPage uint
TotalCount uint64
}

View File

@ -31,6 +31,10 @@ const (
ProjectMembersPageType = "projectMembersPage" ProjectMembersPageType = "projectMembersPage"
// ProjectMembersCursorInputType is a graphql type name for project members // ProjectMembersCursorInputType is a graphql type name for project members
ProjectMembersCursorInputType = "projectMembersCursor" ProjectMembersCursorInputType = "projectMembersCursor"
// APIKeysPageType is a field name for api keys page
APIKeysPageType = "apiKeysPage"
// APIKeysCursorInputType is a graphql type name for api keys
APIKeysCursorInputType = "apiKeysCursor"
// FieldName is a field name for "name" // FieldName is a field name for "name"
FieldName = "name" FieldName = "name"
// FieldBucketName is a field name for "bucket name" // FieldBucketName is a field name for "bucket name"
@ -155,11 +159,39 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
}, },
}, },
FieldAPIKeys: &graphql.Field{ FieldAPIKeys: &graphql.Field{
Type: graphql.NewList(types.apiKeyInfo), Type: types.apiKeyPage,
Args: graphql.FieldConfigArgument{
CursorArg: &graphql.ArgumentConfig{
Type: graphql.NewNonNull(types.apiKeysCursor),
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
project, _ := p.Source.(*console.Project) project, _ := p.Source.(*console.Project)
return service.GetAPIKeysInfoByProjectID(p.Context, project.ID) _, err := console.GetAuth(p.Context)
if err != nil {
return nil, err
}
cursor := cursorArgsToAPIKeysCursor(p.Args[CursorArg].(map[string]interface{}))
page, err := service.GetAPIKeys(p.Context, project.ID, cursor)
if err != nil {
return nil, err
}
apiKeysPage := apiKeysPage{
APIKeys: page.APIKeys,
TotalCount: page.TotalCount,
Offset: page.Offset,
Limit: page.Limit,
Order: int(page.Order),
OrderDirection: int(page.OrderDirection),
Search: page.Search,
CurrentPage: page.CurrentPage,
PageCount: page.PageCount,
}
return apiKeysPage, err
}, },
}, },
FieldUsage: &graphql.Field{ FieldUsage: &graphql.Field{
@ -442,7 +474,24 @@ func cursorArgsToProjectMembersCursor(args map[string]interface{}) console.Proje
cursor.Limit = uint(limit) cursor.Limit = uint(limit)
cursor.Page = uint(page) cursor.Page = uint(page)
cursor.Order = console.ProjectMemberOrder(order) cursor.Order = console.ProjectMemberOrder(order)
cursor.OrderDirection = console.ProjectMemberOrderDirection(orderDirection) cursor.OrderDirection = console.OrderDirection(orderDirection)
cursor.Search, _ = args[SearchArg].(string)
return cursor
}
func cursorArgsToAPIKeysCursor(args map[string]interface{}) console.APIKeyCursor {
limit, _ := args[LimitArg].(int)
page, _ := args[PageArg].(int)
order, _ := args[OrderArg].(int)
orderDirection, _ := args[OrderDirectionArg].(int)
var cursor console.APIKeyCursor
cursor.Limit = uint(limit)
cursor.Page = uint(page)
cursor.Order = console.APIKeyOrder(order)
cursor.OrderDirection = console.OrderDirection(orderDirection)
cursor.Search, _ = args[SearchArg].(string) cursor.Search, _ = args[SearchArg].(string)
return cursor return cursor

View File

@ -301,17 +301,22 @@ func TestGraphqlQuery(t *testing.T) {
t.Run("Project query api keys", func(t *testing.T) { t.Run("Project query api keys", func(t *testing.T) {
query := fmt.Sprintf( query := fmt.Sprintf(
"query {project(id:\"%s\"){apiKeys{name,id,createdAt,projectID}}}", "query {project(id: \"%s\") {apiKeys( cursor: { limit: %d, search: \"%s\", page: %d, order: %d, orderDirection: %d } ) { apiKeys { id, name, createdAt, projectID }, search, limit, order, offset, pageCount, currentPage, totalCount } } }",
createdProject.ID.String(), createdProject.ID.String(),
) 5,
"",
1,
1,
2)
result := testQuery(t, query) result := testQuery(t, query)
data := result.(map[string]interface{}) data := result.(map[string]interface{})
project := data[consoleql.ProjectQuery].(map[string]interface{}) project := data[consoleql.ProjectQuery].(map[string]interface{})
keys := project[consoleql.FieldAPIKeys].([]interface{}) keys := project[consoleql.FieldAPIKeys].(map[string]interface{})
apiKeys := keys[consoleql.FieldAPIKeys].([]interface{})
assert.Equal(t, 2, len(keys)) assert.Equal(t, 2, len(apiKeys))
testAPIKey := func(t *testing.T, actual map[string]interface{}, expected *console.APIKeyInfo) { testAPIKey := func(t *testing.T, actual map[string]interface{}, expected *console.APIKeyInfo) {
assert.Equal(t, expected.Name, actual[consoleql.FieldName]) assert.Equal(t, expected.Name, actual[consoleql.FieldName])
@ -326,7 +331,7 @@ func TestGraphqlQuery(t *testing.T) {
var foundKey1, foundKey2 bool var foundKey1, foundKey2 bool
for _, entry := range keys { for _, entry := range apiKeys {
key := entry.(map[string]interface{}) key := entry.(map[string]interface{})
id := key[consoleql.FieldID].(string) id := key[consoleql.FieldID].(string)

View File

@ -28,6 +28,7 @@ type TypeCreator struct {
paymentMethod *graphql.Object paymentMethod *graphql.Object
projectMember *graphql.Object projectMember *graphql.Object
projectMemberPage *graphql.Object projectMemberPage *graphql.Object
apiKeyPage *graphql.Object
apiKeyInfo *graphql.Object apiKeyInfo *graphql.Object
createAPIKey *graphql.Object createAPIKey *graphql.Object
@ -35,6 +36,7 @@ type TypeCreator struct {
projectInput *graphql.InputObject projectInput *graphql.InputObject
bucketUsageCursor *graphql.InputObject bucketUsageCursor *graphql.InputObject
projectMembersCursor *graphql.InputObject projectMembersCursor *graphql.InputObject
apiKeysCursor *graphql.InputObject
} }
// Create create types and check for error // Create create types and check for error
@ -60,6 +62,11 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ
return err return err
} }
c.apiKeysCursor = graphqlAPIKeysCursor()
if err := c.apiKeysCursor.Error(); err != nil {
return err
}
// entities // entities
c.user = graphqlUser() c.user = graphqlUser()
if err := c.user.Error(); err != nil { if err := c.user.Error(); err != nil {
@ -116,6 +123,11 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ
return err return err
} }
c.apiKeyPage = graphqlAPIKeysPage(c)
if err := c.apiKeyPage.Error(); err != nil {
return err
}
c.project = graphqlProject(service, c) c.project = graphqlProject(service, c)
if err := c.project.Error(); err != nil { if err := c.project.Error(); err != nil {
return err return err

View File

@ -40,7 +40,7 @@ type ProjectMembersCursor struct {
Limit uint Limit uint
Page uint Page uint
Order ProjectMemberOrder Order ProjectMemberOrder
OrderDirection ProjectMemberOrderDirection OrderDirection OrderDirection
} }
// ProjectMembersPage represent project members page result // ProjectMembersPage represent project members page result
@ -50,7 +50,7 @@ type ProjectMembersPage struct {
Search string Search string
Limit uint Limit uint
Order ProjectMemberOrder Order ProjectMemberOrder
OrderDirection ProjectMemberOrderDirection OrderDirection OrderDirection
Offset uint64 Offset uint64
PageCount uint PageCount uint
@ -69,13 +69,3 @@ const (
// Created indicates that we should order by created date // Created indicates that we should order by created date
Created ProjectMemberOrder = 3 Created ProjectMemberOrder = 3
) )
// ProjectMemberOrderDirection is used for querying project members in specific order direction
type ProjectMemberOrderDirection uint8
const (
// Ascending indicates that we should order ascending
Ascending ProjectMemberOrderDirection = 1
// Descending indicates that we should order descending
Descending ProjectMemberOrderDirection = 2
)

View File

@ -1053,9 +1053,10 @@ func (s *Service) DeleteAPIKeys(ctx context.Context, ids []uuid.UUID) (err error
return nil return nil
} }
// GetAPIKeysInfoByProjectID retrieves all api keys for a given project // GetAPIKeys returns paged api key list for given Project
func (s *Service) GetAPIKeysInfoByProjectID(ctx context.Context, projectID uuid.UUID) (info []APIKeyInfo, err error) { func (s *Service) GetAPIKeys(ctx context.Context, projectID uuid.UUID, cursor APIKeyCursor) (page *APIKeyPage, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
auth, err := GetAuth(ctx) auth, err := GetAuth(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1066,12 +1067,16 @@ func (s *Service) GetAPIKeysInfoByProjectID(ctx context.Context, projectID uuid.
return nil, ErrUnauthorized.Wrap(err) return nil, ErrUnauthorized.Wrap(err)
} }
info, err = s.store.APIKeys().GetByProjectID(ctx, projectID) if cursor.Limit > maxLimit {
cursor.Limit = maxLimit
}
page, err = s.store.APIKeys().GetPagedByProjectID(ctx, projectID, cursor)
if err != nil { if err != nil {
return nil, ErrConsoleInternal.Wrap(err) return nil, ErrConsoleInternal.Wrap(err)
} }
return info, nil return
} }
// GetProjectUsage retrieves project usage for a given period // GetProjectUsage retrieves project usage for a given period

View File

@ -0,0 +1,14 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package console
// OrderDirection is used for members in specific order direction
type OrderDirection uint8
const (
// Ascending indicates that we should order ascending
Ascending OrderDirection = 1
// Descending indicates that we should order descending
Descending OrderDirection = 2
)

View File

@ -5,6 +5,7 @@ package satellitedb
import ( import (
"context" "context"
"strings"
"github.com/skyrings/skyring-common/tools/uuid" "github.com/skyrings/skyring-common/tools/uuid"
"github.com/zeebo/errs" "github.com/zeebo/errs"
@ -15,41 +16,123 @@ import (
// apikeys is an implementation of satellite.APIKeys // apikeys is an implementation of satellite.APIKeys
type apikeys struct { type apikeys struct {
db dbx.Methods methods dbx.Methods
db *dbx.DB
} }
// GetByProjectID implements satellite.APIKeys ordered by name func (keys *apikeys) GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor console.APIKeyCursor) (akp *console.APIKeyPage, err error) {
func (keys *apikeys) GetByProjectID(ctx context.Context, projectID uuid.UUID) (_ []console.APIKeyInfo, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
dbKeys, err := keys.db.All_ApiKey_By_ProjectId_OrderBy_Asc_Name(ctx, dbx.ApiKey_ProjectId(projectID[:]))
search := "%" + strings.Replace(cursor.Search, " ", "%", -1) + "%"
if cursor.Limit > 50 {
cursor.Limit = 50
}
if cursor.Page == 0 {
return nil, errs.New("page cannot be 0")
}
page := &console.APIKeyPage{
Search: cursor.Search,
Limit: cursor.Limit,
Offset: uint64((cursor.Page - 1) * cursor.Limit),
Order: cursor.Order,
OrderDirection: cursor.OrderDirection,
}
countQuery := keys.db.Rebind(`
SELECT COUNT(*)
FROM api_keys ak
WHERE ak.project_id = ?
AND ak.name LIKE ?
`)
countRow := keys.db.QueryRowContext(ctx,
countQuery,
projectID[:],
search)
err = countRow.Scan(&page.TotalCount)
if err != nil {
return nil, err
}
if page.TotalCount == 0 {
return page, nil
}
if page.Offset > page.TotalCount-1 {
return nil, errs.New("page is out of range")
}
repoundQuery := keys.db.Rebind(`
SELECT ak.id, ak.project_id, ak.name, ak.partner_id, ak.created_at
FROM api_keys ak
WHERE ak.project_id = ?
AND ak.name LIKE ?
ORDER BY ` + sanitizedAPIKeyOrderColumnName(cursor.Order) + `
` + sanitizeOrderDirectionName(page.OrderDirection) + `
LIMIT ? OFFSET ?`)
rows, err := keys.db.QueryContext(ctx,
repoundQuery,
projectID[:],
search,
page.Limit,
page.Offset)
defer func() {
err = errs.Combine(err, rows.Close())
}()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var apiKeys []console.APIKeyInfo var apiKeys []console.APIKeyInfo
var parseErr errs.Group for rows.Next() {
ak := console.APIKeyInfo{}
var partnerIDBytes []uint8
var partnerID uuid.UUID
for _, key := range dbKeys { err = rows.Scan(&uuidScan{&ak.ID}, &uuidScan{&ak.ProjectID}, &ak.Name, &partnerIDBytes, &ak.CreatedAt)
info, err := fromDBXAPIKey(ctx, key)
if err != nil { if err != nil {
parseErr.Add(err) return nil, err
continue
} }
apiKeys = append(apiKeys, *info) if partnerIDBytes != nil {
partnerID, err = bytesToUUID(partnerIDBytes)
if err != nil {
return nil, err
}
}
ak.PartnerID = partnerID
apiKeys = append(apiKeys, ak)
} }
if err := parseErr.Err(); err != nil { page.APIKeys = apiKeys
page.Order = cursor.Order
page.PageCount = uint(page.TotalCount / uint64(cursor.Limit))
if page.TotalCount%uint64(cursor.Limit) != 0 {
page.PageCount++
}
page.CurrentPage = cursor.Page
err = rows.Err()
if err != nil {
return nil, err return nil, err
} }
return apiKeys, nil return page, err
} }
// Get implements satellite.APIKeys // Get implements satellite.APIKeys
func (keys *apikeys) Get(ctx context.Context, id uuid.UUID) (_ *console.APIKeyInfo, err error) { func (keys *apikeys) Get(ctx context.Context, id uuid.UUID) (_ *console.APIKeyInfo, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
dbKey, err := keys.db.Get_ApiKey_By_Id(ctx, dbx.ApiKey_Id(id[:])) dbKey, err := keys.methods.Get_ApiKey_By_Id(ctx, dbx.ApiKey_Id(id[:]))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -60,7 +143,7 @@ func (keys *apikeys) Get(ctx context.Context, id uuid.UUID) (_ *console.APIKeyIn
// GetByHead implements satellite.APIKeys // GetByHead implements satellite.APIKeys
func (keys *apikeys) GetByHead(ctx context.Context, head []byte) (_ *console.APIKeyInfo, err error) { func (keys *apikeys) GetByHead(ctx context.Context, head []byte) (_ *console.APIKeyInfo, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
dbKey, err := keys.db.Get_ApiKey_By_Head(ctx, dbx.ApiKey_Head(head)) dbKey, err := keys.methods.Get_ApiKey_By_Head(ctx, dbx.ApiKey_Head(head))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -81,7 +164,7 @@ func (keys *apikeys) Create(ctx context.Context, head []byte, info console.APIKe
optional.PartnerId = dbx.ApiKey_PartnerId(info.PartnerID[:]) optional.PartnerId = dbx.ApiKey_PartnerId(info.PartnerID[:])
} }
dbKey, err := keys.db.Create_ApiKey( dbKey, err := keys.methods.Create_ApiKey(
ctx, ctx,
dbx.ApiKey_Id(id[:]), dbx.ApiKey_Id(id[:]),
dbx.ApiKey_ProjectId(info.ProjectID[:]), dbx.ApiKey_ProjectId(info.ProjectID[:]),
@ -101,7 +184,7 @@ func (keys *apikeys) Create(ctx context.Context, head []byte, info console.APIKe
// Update implements satellite.APIKeys // Update implements satellite.APIKeys
func (keys *apikeys) Update(ctx context.Context, key console.APIKeyInfo) (err error) { func (keys *apikeys) Update(ctx context.Context, key console.APIKeyInfo) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
_, err = keys.db.Update_ApiKey_By_Id( _, err = keys.methods.Update_ApiKey_By_Id(
ctx, ctx,
dbx.ApiKey_Id(key.ID[:]), dbx.ApiKey_Id(key.ID[:]),
dbx.ApiKey_Update_Fields{ dbx.ApiKey_Update_Fields{
@ -115,7 +198,7 @@ func (keys *apikeys) Update(ctx context.Context, key console.APIKeyInfo) (err er
// Delete implements satellite.APIKeys // Delete implements satellite.APIKeys
func (keys *apikeys) Delete(ctx context.Context, id uuid.UUID) (err error) { func (keys *apikeys) Delete(ctx context.Context, id uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
_, err = keys.db.Delete_ApiKey_By_Id(ctx, dbx.ApiKey_Id(id[:])) _, err = keys.methods.Delete_ApiKey_By_Id(ctx, dbx.ApiKey_Id(id[:]))
return err return err
} }
@ -149,3 +232,12 @@ func fromDBXAPIKey(ctx context.Context, key *dbx.ApiKey) (_ *console.APIKeyInfo,
return result, nil return result, nil
} }
// sanitizedAPIKeyOrderColumnName return valid order by column
func sanitizedAPIKeyOrderColumnName(pmo console.APIKeyOrder) string {
if pmo == 2 {
return "ak.created_at"
}
return "ak.name"
}

View File

@ -38,7 +38,7 @@ func (db *ConsoleDB) ProjectMembers() console.ProjectMembers {
// APIKeys is a getter for APIKeys repository // APIKeys is a getter for APIKeys repository
func (db *ConsoleDB) APIKeys() console.APIKeys { func (db *ConsoleDB) APIKeys() console.APIKeys {
return &apikeys{db.methods} return &apikeys{db.methods, db.db}
} }
// BucketUsage is a getter for accounting.BucketUsage repository // BucketUsage is a getter for accounting.BucketUsage repository

View File

@ -184,11 +184,11 @@ func (m *lockedAPIKeys) GetByHead(ctx context.Context, head []byte) (*console.AP
return m.db.GetByHead(ctx, head) return m.db.GetByHead(ctx, head)
} }
// GetByProjectID retrieves list of APIKeys for given projectID // GetPagedByProjectID is a method for querying API keys from the database by projectID and cursor
func (m *lockedAPIKeys) GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]console.APIKeyInfo, error) { func (m *lockedAPIKeys) GetPagedByProjectID(ctx context.Context, projectID uuid.UUID, cursor console.APIKeyCursor) (akp *console.APIKeyPage, err error) {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
return m.db.GetByProjectID(ctx, projectID) return m.db.GetPagedByProjectID(ctx, projectID, cursor)
} }
// Update updates APIKeyInfo in store // Update updates APIKeyInfo in store

View File

@ -211,7 +211,7 @@ func sanitizedOrderColumnName(pmo console.ProjectMemberOrder) string {
} }
} }
func sanitizeOrderDirectionName(pmo console.ProjectMemberOrderDirection) string { func sanitizeOrderDirectionName(pmo console.OrderDirection) string {
if pmo == 2 { if pmo == 2 {
return "DESC" return "DESC"
} }

View File

@ -65,3 +65,23 @@ func (nodes postgresNodeIDList) Value() (driver.Value, error) {
return out, nil return out, nil
} }
// uuidScan used to represent uuid scan struct
type uuidScan struct {
uuid *uuid.UUID
}
// Scan is used to wrap logic of db scan with uuid conversion
func (s *uuidScan) Scan(src interface{}) (err error) {
b, ok := src.([]byte)
if !ok {
return Error.New("unexpected type %T for uuid", src)
}
*s.uuid, err = bytesToUUID(b)
if err != nil {
return Error.Wrap(err)
}
return nil
}