satellite/console: Add graphql query for owned projects

Change-Id: If47183d46cb7552ecdddbb3e536c36d958fad6d0
This commit is contained in:
Moby von Briesen 2021-01-21 13:19:37 -05:00 committed by Maximillian von Briesen
parent c139cbd76b
commit 8263f18321
5 changed files with 157 additions and 1 deletions

View File

@ -19,6 +19,10 @@ const (
ProjectInputType = "projectInput"
// ProjectUsageType is a graphql type name for project usage.
ProjectUsageType = "projectUsage"
// ProjectsCursorInputType is a graphql input type name for projects cursor.
ProjectsCursorInputType = "projectsCursor"
// ProjectsPageType is a graphql type name for projects page.
ProjectsPageType = "projectsPage"
// BucketUsageCursorInputType is a graphql input
// type name for bucket usage cursor.
BucketUsageCursorInputType = "bucketUsageCursor"
@ -62,6 +66,10 @@ const (
FieldCurrentPage = "currentPage"
// FieldTotalCount is a field name for bucket usage count total.
FieldTotalCount = "totalCount"
// FieldMemberCount is a field name for number of project members.
FieldMemberCount = "memberCount"
// FieldProjects is a field name for projects.
FieldProjects = "projects"
// FieldProjectMembers is a field name for project members.
FieldProjectMembers = "projectMembers"
// CursorArg is an argument name for cursor.
@ -104,6 +112,9 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec
FieldCreatedAt: &graphql.Field{
Type: graphql.DateTime,
},
FieldMemberCount: &graphql.Field{
Type: graphql.Int,
},
FieldMembers: &graphql.Field{
Type: types.projectMemberPage,
Args: graphql.FieldConfigArgument{
@ -255,6 +266,21 @@ func graphqlProjectInput() *graphql.InputObject {
})
}
// graphqlBucketUsageCursor creates bucket usage cursor graphql input type.
func graphqlProjectsCursor() *graphql.InputObject {
return graphql.NewInputObject(graphql.InputObjectConfig{
Name: ProjectsCursorInputType,
Fields: graphql.InputObjectConfigFieldMap{
LimitArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
PageArg: &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.Int),
},
},
})
}
// graphqlBucketUsageCursor creates bucket usage cursor graphql input type.
func graphqlBucketUsageCursor() *graphql.InputObject {
return graphql.NewInputObject(graphql.InputObjectConfig{
@ -300,6 +326,33 @@ func graphqlBucketUsage() *graphql.Object {
})
}
// graphqlProjectsPage creates a projects page graphql object.
func graphqlProjectsPage(types *TypeCreator) *graphql.Object {
return graphql.NewObject(graphql.ObjectConfig{
Name: ProjectsPageType,
Fields: graphql.Fields{
FieldProjects: &graphql.Field{
Type: graphql.NewList(types.project),
},
LimitArg: &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,
},
},
})
}
// graphqlBucketUsagePage creates bucket usage page graphql object.
func graphqlBucketUsagePage(types *TypeCreator) *graphql.Object {
return graphql.NewObject(graphql.ObjectConfig{
@ -362,7 +415,14 @@ func fromMapProjectInfo(args map[string]interface{}) (project console.ProjectInf
return
}
// fromMapBucketUsageCursor creates console.BucketUsageCursor from input args.
// fromMapProjectsCursor creates console.ProjectsCursor from input args.
func fromMapProjectsCursor(args map[string]interface{}) (cursor console.ProjectsCursor) {
cursor.Limit = args[LimitArg].(int)
cursor.Page = args[PageArg].(int)
return
}
// fromMapBucketUsageCursor creates accounting.BucketUsageCursor from input args.
func fromMapBucketUsageCursor(args map[string]interface{}) (cursor accounting.BucketUsageCursor) {
limit, _ := args[LimitArg].(int)
page, _ := args[PageArg].(int)

View File

@ -17,6 +17,8 @@ const (
Query = "query"
// ProjectQuery is a query name for project.
ProjectQuery = "project"
// OwnedProjectsQuery is a query name for projects owned by an account.
OwnedProjectsQuery = "ownedProjects"
// MyProjectsQuery is a query name for projects related to account.
MyProjectsQuery = "myProjects"
// ActiveRewardQuery is a query name for current active reward offer.
@ -53,6 +55,19 @@ func rootQuery(service *console.Service, mailService *mailservice.Service, types
return project, nil
},
},
OwnedProjectsQuery: &graphql.Field{
Type: types.projectsPage,
Args: graphql.FieldConfigArgument{
CursorArg: &graphql.ArgumentConfig{
Type: graphql.NewNonNull(types.projectsCursor),
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
cursor := fromMapProjectsCursor(p.Args[CursorArg].(map[string]interface{}))
page, err := service.GetUsersOwnedProjectsPage(p.Context, cursor)
return page, err
},
},
MyProjectsQuery: &graphql.Field{
Type: graphql.NewList(types.project),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {

View File

@ -409,6 +409,59 @@ func TestGraphqlQuery(t *testing.T) {
}
}
assert.True(t, foundProj1)
assert.True(t, foundProj2)
})
t.Run("OwnedProjects query", func(t *testing.T) {
query := fmt.Sprintf(
"query {ownedProjects( cursor: { limit: %d, page: %d } ) {projects{id, name, ownerId, description, createdAt, memberCount}, limit, offset, pageCount, currentPage, totalCount } }",
5,
1,
)
result := testQuery(t, query)
data := result.(map[string]interface{})
projectsPage := data[consoleql.OwnedProjectsQuery].(map[string]interface{})
projectsList := projectsPage[consoleql.FieldProjects].([]interface{})
assert.Len(t, projectsList, 2)
assert.EqualValues(t, 1, projectsPage[consoleql.FieldCurrentPage])
assert.EqualValues(t, 0, projectsPage[consoleql.OffsetArg])
assert.EqualValues(t, 5, projectsPage[consoleql.LimitArg])
assert.EqualValues(t, 1, projectsPage[consoleql.FieldPageCount])
assert.EqualValues(t, 2, projectsPage[consoleql.FieldTotalCount])
testProject := func(t *testing.T, actual map[string]interface{}, expected *console.Project, expectedNumMembers int) {
assert.Equal(t, expected.Name, actual[consoleql.FieldName])
assert.Equal(t, expected.Description, actual[consoleql.FieldDescription])
createdAt := time.Time{}
err := createdAt.UnmarshalText([]byte(actual[consoleql.FieldCreatedAt].(string)))
assert.NoError(t, err)
assert.True(t, expected.CreatedAt.Equal(createdAt))
assert.EqualValues(t, expectedNumMembers, actual[consoleql.FieldMemberCount])
}
var foundProj1, foundProj2 bool
for _, entry := range projectsList {
project := entry.(map[string]interface{})
id := project[consoleql.FieldID].(string)
switch id {
case createdProject.ID.String():
foundProj1 = true
testProject(t, project, createdProject, 3)
case project2.ID.String():
foundProj2 = true
testProject(t, project, project2, 1)
}
}
assert.True(t, foundProj1)
assert.True(t, foundProj2)
})

View File

@ -21,6 +21,7 @@ type TypeCreator struct {
creditUsage *graphql.Object
project *graphql.Object
projectUsage *graphql.Object
projectsPage *graphql.Object
bucketUsage *graphql.Object
bucketUsagePage *graphql.Object
projectMember *graphql.Object
@ -31,6 +32,7 @@ type TypeCreator struct {
userInput *graphql.InputObject
projectInput *graphql.InputObject
projectsCursor *graphql.InputObject
bucketUsageCursor *graphql.InputObject
projectMembersCursor *graphql.InputObject
apiKeysCursor *graphql.InputObject
@ -125,6 +127,16 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ
return err
}
c.projectsCursor = graphqlProjectsCursor()
if err := c.projectsCursor.Error(); err != nil {
return err
}
c.projectsPage = graphqlProjectsPage(c)
if err := c.projectsPage.Error(); err != nil {
return err
}
// root objects
c.query = rootQuery(service, mailService, c)
if err := c.query.Error(); err != nil {

View File

@ -988,6 +988,22 @@ func (s *Service) GetUsersProjects(ctx context.Context) (ps []Project, err error
return
}
// GetUsersOwnedProjectsPage is a method for querying paged projects.
func (s *Service) GetUsersOwnedProjectsPage(ctx context.Context, cursor ProjectsCursor) (_ ProjectsPage, err error) {
defer mon.Task()(&ctx)(&err)
auth, err := s.getAuthAndAuditLog(ctx, "get user's owned projects page")
if err != nil {
return ProjectsPage{}, Error.Wrap(err)
}
projects, err := s.store.Projects().ListByOwnerID(ctx, auth.User.ID, cursor)
if err != nil {
return ProjectsPage{}, Error.Wrap(err)
}
return projects, nil
}
// GetCurrentRewardByType is a method for querying current active reward offer based on its type.
func (s *Service) GetCurrentRewardByType(ctx context.Context, offerType rewards.OfferType) (offer *rewards.Offer, err error) {
defer mon.Task()(&ctx)(&err)