/
resolver.go
178 lines (159 loc) · 5.24 KB
/
resolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package graph
import (
"context"
"fmt"
"slices"
"cloud.google.com/go/pubsub"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/google/uuid"
"github.com/nais/api/internal/auditlogger"
"github.com/nais/api/internal/database"
"github.com/nais/api/internal/database/gensql"
"github.com/nais/api/internal/database/teamsearch"
"github.com/nais/api/internal/graph/apierror"
"github.com/nais/api/internal/graph/gengql"
"github.com/nais/api/internal/graph/model"
"github.com/nais/api/internal/k8s"
"github.com/nais/api/internal/resourceusage"
"github.com/nais/api/internal/search"
"github.com/nais/api/internal/sqlinstance"
"github.com/nais/api/internal/thirdparty/dependencytrack"
"github.com/nais/api/internal/thirdparty/hookd"
"github.com/nais/api/internal/usersync"
"github.com/ravilushqa/otelgqlgen"
"github.com/sirupsen/logrus"
"go.opentelemetry.io/otel"
)
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type ClusterInfo struct {
GCP bool
}
type ClusterList map[string]ClusterInfo
func (c ClusterList) GCPClusters() []string {
if c == nil {
return nil
}
var ret []string
for cluster, info := range c {
if info.GCP {
ret = append(ret, cluster)
}
}
return ret
}
func (c ClusterList) Names() []string {
if c == nil {
return nil
}
var ret []string
for cluster := range c {
ret = append(ret, cluster)
}
slices.SortFunc(ret, func(i, j string) int {
if i < j {
return -1
}
return 1
})
return ret
}
type HookdClient interface {
Deployments(ctx context.Context, opts ...hookd.RequestOption) ([]hookd.Deploy, error)
ChangeDeployKey(ctx context.Context, team string) (*hookd.DeployKey, error)
DeployKey(ctx context.Context, team string) (*hookd.DeployKey, error)
}
type DependencytrackClient interface {
VulnerabilitySummary(ctx context.Context, app *dependencytrack.AppInstance) (*model.Vulnerability, error)
GetVulnerabilities(ctx context.Context, apps []*dependencytrack.AppInstance, filters ...dependencytrack.Filter) ([]*model.Vulnerability, error)
}
type Resolver struct {
hookdClient HookdClient
k8sClient *k8s.Client
dependencyTrackClient DependencytrackClient
resourceUsageClient resourceusage.Client
searcher *search.Searcher
log logrus.FieldLogger
clusters ClusterList
database database.Database
tenantDomain string
userSync chan<- uuid.UUID
auditLogger auditlogger.AuditLogger
userSyncRuns *usersync.RunsHandler
pubsubTopic *pubsub.Topic
sqlInstanceClient *sqlinstance.Client
}
// NewResolver creates a new GraphQL resolver with the given dependencies
func NewResolver(
hookdClient HookdClient,
k8sClient *k8s.Client,
dependencyTrackClient DependencytrackClient,
resourceUsageClient resourceusage.Client,
db database.Database,
tenantDomain string,
userSync chan<- uuid.UUID,
auditLogger auditlogger.AuditLogger,
clusters ClusterList,
userSyncRuns *usersync.RunsHandler,
pubsubTopic *pubsub.Topic,
log logrus.FieldLogger,
sqlInstanceClient *sqlinstance.Client,
) *Resolver {
return &Resolver{
hookdClient: hookdClient,
k8sClient: k8sClient,
dependencyTrackClient: dependencyTrackClient,
resourceUsageClient: resourceUsageClient,
tenantDomain: tenantDomain,
userSync: userSync,
auditLogger: auditLogger,
searcher: search.New(teamsearch.New(db), k8sClient),
log: log,
database: db,
userSyncRuns: userSyncRuns,
clusters: clusters,
pubsubTopic: pubsubTopic,
sqlInstanceClient: sqlInstanceClient,
}
}
// NewHandler creates and returns a new GraphQL handler with the given configuration
func NewHandler(config gengql.Config, log logrus.FieldLogger) (*handler.Server, error) {
meter := otel.Meter("graph")
metricsMiddleware, err := NewMetrics(meter)
if err != nil {
return nil, fmt.Errorf("create metrics middleware: %w", err)
}
schema := gengql.NewExecutableSchema(config)
graphHandler := handler.New(schema)
graphHandler.Use(metricsMiddleware)
graphHandler.AddTransport(SSE{}) // Support subscriptions
graphHandler.AddTransport(transport.Options{})
graphHandler.AddTransport(transport.POST{})
graphHandler.SetQueryCache(lru.New(1000))
graphHandler.Use(extension.Introspection{})
graphHandler.Use(extension.AutomaticPersistedQuery{
Cache: lru.New(100),
})
graphHandler.SetErrorPresenter(apierror.GetErrorPresenter(log))
graphHandler.Use(otelgqlgen.Middleware(
otelgqlgen.WithoutVariables(),
otelgqlgen.WithCreateSpanFromFields(func(ctx *graphql.FieldContext) bool {
return ctx.IsResolver
}),
))
return graphHandler, nil
}
func gensqlRoleFromTeamRole(teamRole model.TeamRole) (gensql.RoleName, error) {
switch teamRole {
case model.TeamRoleMember:
return gensql.RoleNameTeammember, nil
case model.TeamRoleOwner:
return gensql.RoleNameTeamowner, nil
}
return "", fmt.Errorf("invalid team role: %v", teamRole)
}