Skip to content

Commit

Permalink
chore: initial source code setup for api
Browse files Browse the repository at this point in the history
  • Loading branch information
MR2011 committed Jun 28, 2024
1 parent 72aecea commit 72d32ac
Show file tree
Hide file tree
Showing 115 changed files with 35,473 additions and 0 deletions.
188 changes: 188 additions & 0 deletions internal/api/graphql/gqlgen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- graph/schema/*.graphqls

# Where should the generated server code go?
exec:
filename: graph/generated.go
package: graph

# Uncomment to enable federation
# federation:
# filename: graph/federation.go
# package: graph

# Where should any generated models go?
model:
filename: graph/model/models_gen.go
package: model

# Where should the resolver implementations go?
resolver:
layout: follow-schema
dir: graph/resolver
package: resolver
filename_template: "{name}.go"
# Optional: turn on to not generate template comments above resolvers
# omit_template_comment: false

# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json

# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: false

# Optional: turn on to skip generation of ComplexityRoot struct content and Complexity function
# omit_complexity: false

# Optional: turn on to not generate any file notice comments in generated files
# omit_gqlgen_file_notice: false

# Optional: turn on to exclude the gqlgen version in the generated file notice. No effect if `omit_gqlgen_file_notice` is true.
# omit_gqlgen_version_in_file_notice: false

# Optional: turn off to make struct-type struct fields not use pointers
# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing }
# struct_fields_always_pointers: true

# Optional: turn off to make resolvers return values instead of pointers for structs
# resolvers_always_return_pointers: true

# Optional: turn on to return pointers instead of values in unmarshalInput
# return_pointers_in_unmarshalinput: false

# Optional: wrap nullable input fields with Omittable
# nullable_input_omittable: true

# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true

# Optional: set to skip running `go mod tidy` when generating server code
# skip_mod_tidy: true

# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
- "github.wdf.sap.corp/cc/heureka/internal/api/graphql/graph/model"

# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
ID:
model:
- github.com/99designs/gqlgen/graphql.ID
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Int:
model:
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Issue:
fields:
issueVariants:
resolver: true
issueMatches:
resolver: true
activities:
resolver: true
componentVersions:
resolver: true
IssueMatch:
fields:
componentInstance:
resolver: true
issue:
resolver: true
evidences:
resolver: true
severity:
resolver: true
effectiveIssueVariants:
resolver: true
issueMatchChanges:
resolver: true
IssueMatchChange:
fields:
issueMatch:
resolver: true
activity:
resolver: true
Component:
fields:
componentVersions:
resolver: true
ComponentInstance:
fields:
componentVersion:
resolver: true
service:
resolver: true
issueMatches:
resolver: true
ComponentVersion:
fields:
component:
resolver: true
issues:
resolver: true
Service:
fields:
owners:
resolver: true
supportGroups:
resolver: true
issues:
resolver: true
activities:
resolver: true
issueRepositories:
resolver: true
componentInstances:
resolver: true
SupportGroup:
fields:
users:
resolver: true
services:
resolver: true
Activity:
fields:
services:
resolver: true
issues:
resolver: true
evidences:
resolver: true
issueMatchChanges:
resolver: true
Evidence:
fields:
activity:
resolver: true
author:
resolver: true
issueMatches:
resolver: true
IssueVariant:
fields:
issue:
resolver: true
issueRepository:
resolver: true
IssueRepository:
fields:
services:
resolver: true
issueVariants:
resolver: true
User:
fields:
supportGroups:
resolver: true
services:
resolver: true
129 changes: 129 additions & 0 deletions internal/api/graphql/graph/baseResolver/activity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
// SPDX-License-Identifier: Apache-2.0

package baseResolver

import (
"context"

"github.com/sirupsen/logrus"
"github.wdf.sap.corp/cc/heureka/internal/api/graphql/graph/model"
"github.wdf.sap.corp/cc/heureka/internal/app"
"github.wdf.sap.corp/cc/heureka/internal/entity"
)

func SingleActivityBaseResolver(app app.Heureka, ctx context.Context, parent *model.NodeParent) (*model.Activity, error) {

requestedFields := GetPreloads(ctx)
logrus.WithFields(logrus.Fields{
"requestedFields": requestedFields,
"parent": parent,
}).Debug("Called SingleActivityBaseResolver")

if parent == nil {
return nil, NewResolverError("SingleActivityBaseResolver", "Bad Request - No parent provided")
}

f := &entity.ActivityFilter{
Id: parent.ChildIds,
}

opt := &entity.ListOptions{}

activities, err := app.ListActivities(f, opt)

// error while fetching
if err != nil {
return nil, NewResolverError("SingleActivityBaseResolver", err.Error())
}

// unexpected number of results (should at most be 1)
if len(activities.Elements) > 1 {
return nil, NewResolverError("SingleActivityBaseResolver", "Internal Error - found multiple activities")
}

//not found
if len(activities.Elements) < 1 {
return nil, nil
}

var activityResult = activities.Elements[0]
activity := model.NewActivity(activityResult.Activity)

return &activity, nil
}

func ActivityBaseResolver(app app.Heureka, ctx context.Context, filter *model.ActivityFilter, first *int, after *string, parent *model.NodeParent) (*model.ActivityConnection, error) {
requestedFields := GetPreloads(ctx)
logrus.WithFields(logrus.Fields{
"requestedFields": requestedFields,
"parent": parent,
}).Debug("Called ActivityBaseResolver")

afterId, err := ParseCursor(after)
if err != nil {
logrus.WithField("after", after).Error("ActivityBaseResolver: Error while parsing parameter 'after'")
return nil, NewResolverError("ActivityBaseResolver", "Bad Request - unable to parse cursor 'after'")
}

var sId []*int64
var issueId []*int64
if parent != nil {
parentId := parent.Parent.GetID()
pid, err := ParseCursor(&parentId)
if err != nil {
logrus.WithField("parent", parent).Error("ActivityBaseResolver: Error while parsing propagated parent ID'")
return nil, NewResolverError("ActivityBaseResolver", "Bad Request - Error while parsing propagated ID")
}

switch parent.ParentName {
case model.ServiceNodeName:
sId = []*int64{pid}
case model.IssueNodeName:
issueId = []*int64{pid}
}
}

if filter == nil {
filter = &model.ActivityFilter{}
}

f := &entity.ActivityFilter{
Paginated: entity.Paginated{First: first, After: afterId},
ServiceName: filter.ServiceName,
ServiceId: sId,
IssueId: issueId,
}

opt := GetListOptions(requestedFields)

activities, err := app.ListActivities(f, opt)

if err != nil {
return nil, NewResolverError("ActivityBaseResolver", err.Error())
}

edges := []*model.ActivityEdge{}
for _, result := range activities.Elements {
a := model.NewActivity(result.Activity)
edge := model.ActivityEdge{
Node: &a,
Cursor: result.Cursor(),
}
edges = append(edges, &edge)
}

tc := 0
if activities.TotalCount != nil {
tc = int(*activities.TotalCount)
}

connection := model.ActivityConnection{
TotalCount: tc,
Edges: edges,
PageInfo: model.NewPageInfo(activities.PageInfo),
}

return &connection, nil

}
76 changes: 76 additions & 0 deletions internal/api/graphql/graph/baseResolver/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
// SPDX-License-Identifier: Apache-2.0

package baseResolver

import (
"context"
"fmt"
"github.com/99designs/gqlgen/graphql"
"github.com/samber/lo"
"github.wdf.sap.corp/cc/heureka/internal/entity"
"strconv"
)

type ResolverError struct {
resolver string
msg string
}

func (re *ResolverError) Error() string {
return fmt.Sprintf("%s: %s", re.resolver, re.msg)
}

func NewResolverError(resolver string, msg string) *ResolverError {
return &ResolverError{
resolver: resolver,
msg: msg,
}
}

func ParseCursor(cursor *string) (*int64, error) {

if cursor == nil {
var tmp int64 = 0
return &tmp, nil
}

id, err := strconv.ParseInt(*cursor, 10, 64)
if err != nil {
return nil, err
}

return &id, err
}

func GetPreloads(ctx context.Context) []string {
return GetNestedPreloads(
graphql.GetOperationContext(ctx),
graphql.CollectFieldsCtx(ctx, nil),
"",
)
}

func GetNestedPreloads(ctx *graphql.OperationContext, fields []graphql.CollectedField, prefix string) (preloads []string) {
for _, column := range fields {
prefixColumn := GetPreloadString(prefix, column.Name)
preloads = append(preloads, prefixColumn)
preloads = append(preloads, GetNestedPreloads(ctx, graphql.CollectFields(ctx, column.Selections, nil), prefixColumn)...)
}
return
}

func GetPreloadString(prefix, name string) string {
if len(prefix) > 0 {
return prefix + "." + name
}
return name
}

func GetListOptions(requestedFields []string) *entity.ListOptions {
return &entity.ListOptions{
ShowTotalCount: lo.Contains(requestedFields, "totalCount"),
ShowPageInfo: lo.Contains(requestedFields, "pageInfo"),
IncludeAggregations: lo.Contains(requestedFields, "edges.node.metadata"),
}
}
Loading

0 comments on commit 72d32ac

Please sign in to comment.