• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mindersec / minder / 13566620812

27 Feb 2025 12:41PM UTC coverage: 57.498%. First build
13566620812

Pull #5467

github

web-flow
Merge 212a43a46 into 507713f32
Pull Request #5467: build(deps): bump github.com/go-jose/go-jose/v4 from 4.0.4 to 4.0.5

18144 of 31556 relevant lines covered (57.5%)

37.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/internal/controlplane/handlers_artifacts.go
1
// SPDX-FileCopyrightText: Copyright 2023 The Minder Authors
2
// SPDX-License-Identifier: Apache-2.0
3

4
package controlplane
5

6
import (
7
        "context"
8
        "database/sql"
9
        "errors"
10
        "fmt"
11
        "slices"
12
        "strings"
13

14
        "github.com/google/uuid"
15
        "google.golang.org/grpc/codes"
16
        "google.golang.org/grpc/status"
17
        "google.golang.org/protobuf/types/known/timestamppb"
18

19
        "github.com/mindersec/minder/internal/db"
20
        "github.com/mindersec/minder/internal/engine/engcontext"
21
        "github.com/mindersec/minder/internal/logger"
22
        "github.com/mindersec/minder/internal/util"
23
        "github.com/mindersec/minder/internal/util/ptr"
24
        pb "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
25
)
26

27
// ListArtifacts lists all artifacts for a given project and provider
28
// nolint:gocyclo
29
func (s *Server) ListArtifacts(ctx context.Context, in *pb.ListArtifactsRequest) (*pb.ListArtifactsResponse, error) {
×
30
        entityCtx := engcontext.EntityFromContext(ctx)
×
31
        projectID := entityCtx.Project.ID
×
32
        providerName := entityCtx.Provider.Name
×
33

×
34
        // Telemetry logging
×
35
        logger.BusinessRecord(ctx).Provider = providerName
×
36
        logger.BusinessRecord(ctx).Project = projectID
×
37

×
38
        artifactFilter, err := parseArtifactListFrom(s.store, in.From)
×
39
        if err != nil {
×
40
                return nil, fmt.Errorf("failed to parse artifact list from: %w", err)
×
41
        }
×
42

43
        results, err := artifactFilter.listArtifacts(ctx, providerName, projectID)
×
44
        if err != nil {
×
45
                return nil, fmt.Errorf("failed to list artifacts: %w", err)
×
46
        }
×
47

48
        return &pb.ListArtifactsResponse{Results: results}, nil
×
49
}
50

51
// GetArtifactByName gets an artifact by name
52
// nolint:gocyclo
53
func (s *Server) GetArtifactByName(ctx context.Context, in *pb.GetArtifactByNameRequest) (*pb.GetArtifactByNameResponse, error) {
×
54
        // tag and latest versions cannot be set at same time
×
55
        nameParts := strings.Split(in.Name, "/")
×
56
        if len(nameParts) < 3 {
×
57
                return nil, util.UserVisibleError(codes.InvalidArgument, "invalid artifact name user repoOwner/repoName/artifactName")
×
58
        }
×
59

60
        entityCtx := engcontext.EntityFromContext(ctx)
×
61
        projectID := entityCtx.Project.ID
×
62
        providerName := entityCtx.Provider.Name
×
63
        providerFilter := getNameFilterParam(providerName)
×
64

×
65
        logger.BusinessRecord(ctx).Provider = providerName
×
66

×
67
        // the artifact name is the rest of the parts
×
68
        artifactName := strings.Join(nameParts[2:], "/")
×
69
        artifact, err := s.store.GetArtifactByName(ctx, db.GetArtifactByNameParams{
×
70
                ArtifactName: artifactName,
×
71
                ProjectID:    projectID,
×
72
        })
×
73
        if err != nil {
×
74
                if errors.Is(err, sql.ErrNoRows) {
×
75
                        return nil, status.Errorf(codes.NotFound, "artifact not found")
×
76
                }
×
77
                return nil, status.Errorf(codes.Unknown, "failed to get artifact: %s", err)
×
78
        }
79

80
        var repoName, repoOwner string
×
81
        // TODO look for repo if any
×
82
        if artifact.RepositoryID.Valid {
×
83
                repo, err := s.store.GetRepositoryByRepoName(ctx, db.GetRepositoryByRepoNameParams{
×
84
                        Provider:  providerFilter,
×
85
                        RepoOwner: nameParts[0],
×
86
                        RepoName:  nameParts[1],
×
87
                        ProjectID: projectID,
×
88
                })
×
89
                if err != nil {
×
90
                        if errors.Is(err, sql.ErrNoRows) {
×
91
                                return nil, util.UserVisibleError(codes.NotFound, "repository not found")
×
92
                        }
×
93
                        return nil, status.Errorf(codes.Unknown, "failed to get repository: %s", err)
×
94
                }
95

96
                logger.BusinessRecord(ctx).Repository = repo.ID
×
97

×
98
                repoName = repo.RepoName
×
99
                repoOwner = repo.RepoOwner
×
100
        }
101

102
        // Telemetry logging
103
        logger.BusinessRecord(ctx).ProviderID = artifact.ProviderID
×
104
        logger.BusinessRecord(ctx).Project = artifact.ProjectID
×
105
        logger.BusinessRecord(ctx).Artifact = artifact.ID
×
106

×
107
        return &pb.GetArtifactByNameResponse{Artifact: &pb.Artifact{
×
108
                ArtifactPk: artifact.ID.String(),
×
109
                Context: &pb.Context{
×
110
                        Provider: ptr.Ptr(artifact.ProviderName),
×
111
                        Project:  ptr.Ptr(artifact.ProjectID.String()),
×
112
                },
×
113
                Owner:      repoOwner,
×
114
                Name:       artifact.ArtifactName,
×
115
                Type:       artifact.ArtifactType,
×
116
                Visibility: artifact.ArtifactVisibility,
×
117
                Repository: repoName,
×
118
                CreatedAt:  timestamppb.New(artifact.CreatedAt),
×
119
        },
×
120
                Versions: nil, // explicitly nil, will probably deprecate that field later
×
121
        }, nil
×
122
}
123

124
// GetArtifactById gets an artifact by id
125
// nolint:gocyclo
126
func (s *Server) GetArtifactById(ctx context.Context, in *pb.GetArtifactByIdRequest) (*pb.GetArtifactByIdResponse, error) {
×
127
        entityCtx := engcontext.EntityFromContext(ctx)
×
128
        projectID := entityCtx.Project.ID
×
129

×
130
        // tag and latest versions cannot be set at same time
×
131
        parsedArtifactID, err := uuid.Parse(in.Id)
×
132
        if err != nil {
×
133
                return nil, util.UserVisibleError(codes.InvalidArgument, "invalid artifact ID")
×
134
        }
×
135

136
        // retrieve artifact details
137
        artifact, err := s.store.GetArtifactByID(ctx, db.GetArtifactByIDParams{
×
138
                ID:        parsedArtifactID,
×
139
                ProjectID: projectID,
×
140
        })
×
141
        if err != nil {
×
142
                if errors.Is(err, sql.ErrNoRows) {
×
143
                        return nil, status.Errorf(codes.NotFound, "artifact not found")
×
144
                }
×
145
                return nil, status.Errorf(codes.Unknown, "failed to get artifact: %s", err)
×
146
        }
147

148
        // Telemetry logging
149
        logger.BusinessRecord(ctx).ProviderID = artifact.ProviderID
×
150
        logger.BusinessRecord(ctx).Project = artifact.ProjectID
×
151
        logger.BusinessRecord(ctx).Artifact = artifact.ID
×
152
        if artifact.RepositoryID.Valid {
×
153
                logger.BusinessRecord(ctx).Repository = artifact.RepositoryID.UUID
×
154
        }
×
155

156
        return &pb.GetArtifactByIdResponse{Artifact: &pb.Artifact{
×
157
                ArtifactPk: artifact.ID.String(),
×
158
                Context: &pb.Context{
×
159
                        Provider: ptr.Ptr(artifact.ProviderName),
×
160
                        Project:  ptr.Ptr(artifact.ProjectID.String()),
×
161
                },
×
162
                Owner:      "", // TODO re-add
×
163
                Name:       artifact.ArtifactName,
×
164
                Type:       artifact.ArtifactType,
×
165
                Visibility: artifact.ArtifactVisibility,
×
166
                Repository: "", // TODO re-add
×
167
                CreatedAt:  timestamppb.New(artifact.CreatedAt),
×
168
        },
×
169
                Versions: nil, // explicitly nil, will probably deprecate that field later
×
170
        }, nil
×
171
}
172

173
type artifactSource string
174

175
const (
176
        artifactSourceRepo artifactSource = "repository"
177
)
178

179
type artifactListFilter struct {
180
        store db.Store
181

182
        repoSlubList []string
183
        source       artifactSource
184
        filter       string
185
}
186

187
func parseArtifactListFrom(store db.Store, from string) (*artifactListFilter, error) {
×
188
        if from == "" {
×
189
                return &artifactListFilter{
×
190
                        store:  store,
×
191
                        source: artifactSourceRepo,
×
192
                }, nil
×
193
        }
×
194

195
        parts := strings.Split(from, "=")
×
196
        if len(parts) != 2 {
×
197
                return nil, util.UserVisibleError(codes.InvalidArgument, "invalid filter, use format: <source>=<filter>")
×
198
        }
×
199

200
        source := parts[0]
×
201
        filter := parts[1]
×
202

×
203
        var repoSlubList []string
×
204

×
205
        switch source {
×
206
        case string(artifactSourceRepo):
×
207
                repoSlubList = strings.Split(filter, ",")
×
208
        default:
×
209
                return nil, util.UserVisibleError(codes.InvalidArgument, "invalid filter source, only repository is supported")
×
210
        }
211

212
        return &artifactListFilter{
×
213
                store:        store,
×
214
                source:       artifactSource(source),
×
215
                filter:       filter,
×
216
                repoSlubList: repoSlubList,
×
217
        }, nil
×
218
}
219

220
func (filter *artifactListFilter) listArtifacts(ctx context.Context, provider string, project uuid.UUID) ([]*pb.Artifact, error) {
×
221
        if filter.source != artifactSourceRepo {
×
222
                // just repos are supported now and we should never get here
×
223
                // when we support more, we turn this into an if-else or a switch
×
224
                return []*pb.Artifact{}, nil
×
225
        }
×
226

227
        repositories, err := artifactListRepoFilter(ctx, filter.store, provider, project, filter.repoSlubList)
×
228
        if err != nil {
×
229
                return nil, fmt.Errorf("failed to get repositories: %w", err)
×
230
        }
×
231

232
        results := []*pb.Artifact{}
×
233
        for _, repository := range repositories {
×
234
                artifacts, err := filter.store.ListArtifactsByRepoID(ctx, uuid.NullUUID{
×
235
                        UUID:  repository.ID,
×
236
                        Valid: true,
×
237
                })
×
238
                if err != nil {
×
239
                        return nil, status.Errorf(codes.Unknown, "failed to get artifacts: %s", err)
×
240
                }
×
241

242
                for _, artifact := range artifacts {
×
243
                        results = append(results, &pb.Artifact{
×
244
                                ArtifactPk: artifact.ID.String(),
×
245
                                Context: &pb.Context{
×
246
                                        Provider: ptr.Ptr(artifact.ProviderName),
×
247
                                        Project:  ptr.Ptr(artifact.ProjectID.String()),
×
248
                                },
×
249
                                Owner:      repository.RepoOwner,
×
250
                                Name:       artifact.ArtifactName,
×
251
                                Type:       artifact.ArtifactType,
×
252
                                Visibility: artifact.ArtifactVisibility,
×
253
                                Repository: repository.RepoName,
×
254
                                CreatedAt:  timestamppb.New(artifact.CreatedAt),
×
255
                        })
×
256
                }
×
257
        }
258

259
        return results, nil
×
260
}
261

262
func artifactListRepoFilter(
263
        ctx context.Context, store db.Store, provider string, projectID uuid.UUID, repoSlubList []string,
264
) ([]*db.Repository, error) {
×
265
        providerFilter := getNameFilterParam(provider)
×
266

×
267
        repositories, err := store.ListRegisteredRepositoriesByProjectIDAndProvider(ctx,
×
268
                db.ListRegisteredRepositoriesByProjectIDAndProviderParams{Provider: providerFilter, ProjectID: projectID})
×
269
        if err != nil {
×
270
                if errors.Is(err, sql.ErrNoRows) {
×
271
                        return nil, status.Errorf(codes.NotFound, "repositories not found")
×
272
                }
×
273
                return nil, status.Errorf(codes.Unknown, "failed to get repositories: %s", err)
×
274
        }
275

276
        var filterRepositories []*db.Repository
×
277
        for _, repo := range repositories {
×
278
                if repoInSlubList(&repo, repoSlubList) {
×
279
                        filterRepositories = append(filterRepositories, &repo)
×
280
                }
×
281
        }
282

283
        return filterRepositories, nil
×
284
}
285

286
func repoInSlubList(repo *db.Repository, slubList []string) bool {
×
287
        if len(slubList) == 0 {
×
288
                return true
×
289
        }
×
290

291
        // we might want to save the repoSlub in the future into the db..
292
        repoSlub := fmt.Sprintf("%s/%s", repo.RepoOwner, repo.RepoName)
×
293
        return slices.Contains(slubList, repoSlub)
×
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc