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

goto / guardian / 17816287169

18 Sep 2025 02:37AM UTC coverage: 69.124% (-0.3%) from 69.44%
17816287169

push

github

web-flow
feat: introduce create resource API (#222)

* docs: add tips for generating proto from a local file

* feat: introduce create resource API

* chore: update proto

* chore: add more validations

* chore: wrap error with more message detail

* feat: validate resource conflict on creation

* chore: update proto

* chore: update proto

0 of 84 new or added lines in 5 files covered. (0.0%)

191 existing lines in 8 files now uncovered.

11373 of 16453 relevant lines covered (69.12%)

4.64 hits per line

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

82.4
/api/handler/v1beta1/resource.go
1
package v1beta1
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "strings"
8

9
        "golang.org/x/sync/errgroup"
10

11
        "github.com/google/uuid"
12
        guardianv1beta1 "github.com/goto/guardian/api/proto/gotocompany/guardian/v1beta1"
13
        "github.com/goto/guardian/core/provider"
14
        "github.com/goto/guardian/core/resource"
15
        "github.com/goto/guardian/domain"
16
        "google.golang.org/grpc/codes"
17
        "google.golang.org/grpc/status"
18
)
19

NEW
20
func (s *GRPCServer) CreateResource(ctx context.Context, req *guardianv1beta1.CreateResourceRequest) (*guardianv1beta1.CreateResourceResponse, error) {
×
NEW
21
        r := s.adapter.FromResourceProto(req.GetResource())
×
NEW
22

×
NEW
23
        if err := s.providerService.CreateResource(ctx, r); err != nil {
×
NEW
24
                switch {
×
NEW
25
                case errors.Is(err, provider.ErrRecordNotFound):
×
NEW
26
                        return nil, status.Error(codes.NotFound, fmt.Sprintf("provider with type %q and urn %q does not exist", r.ProviderType, r.ProviderURN))
×
27
                case errors.Is(err, provider.ErrInvalidResourceType),
28
                        errors.Is(err, provider.ErrInvalidResource),
NEW
29
                        errors.Is(err, resource.ErrInvalidResource):
×
NEW
30
                        return nil, status.Error(codes.InvalidArgument, err.Error())
×
NEW
31
                case errors.Is(err, resource.ErrResourceAlreadyExists):
×
NEW
32
                        return nil, status.Error(codes.AlreadyExists, err.Error())
×
NEW
33
                default:
×
NEW
34
                        return nil, s.internalError(ctx, err.Error())
×
35
                }
36
        }
37

NEW
38
        resourceProto, err := s.adapter.ToResourceProto(r)
×
NEW
39
        if err != nil {
×
NEW
40
                return nil, s.internalError(ctx, "failed to convert to resource proto: %v", err)
×
NEW
41
        }
×
42

NEW
43
        return &guardianv1beta1.CreateResourceResponse{
×
NEW
44
                Resource: resourceProto,
×
NEW
45
        }, nil
×
46
}
47

48
func (s *GRPCServer) ListResources(ctx context.Context, req *guardianv1beta1.ListResourcesRequest) (*guardianv1beta1.ListResourcesResponse, error) {
3✔
49
        var details map[string]string
3✔
50
        if req.GetDetails() != nil {
4✔
51
                details = map[string]string{}
1✔
52
                for _, d := range req.GetDetails() {
3✔
53
                        filter := strings.Split(d, ":")
2✔
54
                        if len(filter) == 2 {
4✔
55
                                path := filter[0]
2✔
56
                                value := filter[1]
2✔
57
                                details[path] = value
2✔
58
                        }
2✔
59
                }
60
        }
61
        filter := domain.ListResourcesFilter{
3✔
62
                IsDeleted:    req.GetIsDeleted(),
3✔
63
                ResourceType: req.GetType(),
3✔
64
                ResourceURN:  req.GetUrn(),
3✔
65
                ProviderType: req.GetProviderType(),
3✔
66
                ProviderURN:  req.GetProviderUrn(),
3✔
67
                Name:         req.GetName(),
3✔
68
                Details:      details,
3✔
69
                Size:         req.GetSize(),
3✔
70
                Offset:       req.GetOffset(),
3✔
71
                OrderBy:      req.GetOrderBy(),
3✔
72
                Q:            req.GetQ(),
3✔
73
        }
3✔
74

3✔
75
        resources, total, err := s.listResources(ctx, filter)
3✔
76
        if err != nil {
5✔
77
                return nil, err
2✔
78
        }
2✔
79

80
        return &guardianv1beta1.ListResourcesResponse{
1✔
81
                Resources: resources,
1✔
82
                Total:     uint32(total),
1✔
83
        }, nil
1✔
84
}
85

86
func (s *GRPCServer) listResources(ctx context.Context, filter domain.ListResourcesFilter) ([]*guardianv1beta1.Resource, int64, error) {
3✔
87
        eg, ctx := errgroup.WithContext(ctx)
3✔
88
        var resources []*domain.Resource
3✔
89
        var total int64
3✔
90

3✔
91
        eg.Go(func() error {
6✔
92
                resourceRecords, err := s.resourceService.Find(ctx, filter)
3✔
93
                if err != nil {
4✔
94
                        return s.internalError(ctx, "failed to get resource list: %s", err)
1✔
95
                }
1✔
96
                resources = resourceRecords
2✔
97
                return nil
2✔
98
        })
99
        eg.Go(func() error {
6✔
100
                totalRecord, err := s.resourceService.GetResourcesTotalCount(ctx, filter)
3✔
101
                if err != nil {
3✔
102
                        return s.internalError(ctx, "failed to get resource total count: %s", err)
×
103
                }
×
104
                total = totalRecord
3✔
105
                return nil
3✔
106
        })
107

108
        if err := eg.Wait(); err != nil {
4✔
109
                return nil, 0, err
1✔
110
        }
1✔
111
        var resourceProtos []*guardianv1beta1.Resource
2✔
112
        for i, r := range resources {
4✔
113
                resourceProto, err := s.adapter.ToResourceProto(resources[i])
2✔
114
                if err != nil {
3✔
115
                        return nil, 0, s.internalError(ctx, "failed to parse resource %v: %v", r.Name, err)
1✔
116
                }
1✔
117
                resourceProtos = append(resourceProtos, resourceProto)
1✔
118
        }
119

120
        return resourceProtos, total, nil
1✔
121
}
122

123
func (s *GRPCServer) GetResource(ctx context.Context, req *guardianv1beta1.GetResourceRequest) (*guardianv1beta1.GetResourceResponse, error) {
4✔
124
        r, err := s.resourceService.GetOne(ctx, req.GetId())
4✔
125
        if err != nil {
6✔
126
                switch err {
2✔
127
                case resource.ErrRecordNotFound:
1✔
128
                        return nil, status.Error(codes.NotFound, "resource not found")
1✔
129
                default:
1✔
130
                        return nil, s.internalError(ctx, "failed to retrieve resource: %v", err)
1✔
131
                }
132
        }
133

134
        resourceProto, err := s.adapter.ToResourceProto(r)
2✔
135
        if err != nil {
3✔
136
                return nil, s.internalError(ctx, "failed to parse resource: %v", err)
1✔
137
        }
1✔
138

139
        return &guardianv1beta1.GetResourceResponse{
1✔
140
                Resource: resourceProto,
1✔
141
        }, nil
1✔
142
}
143

144
func (s *GRPCServer) UpdateResource(ctx context.Context, req *guardianv1beta1.UpdateResourceRequest) (*guardianv1beta1.UpdateResourceResponse, error) {
4✔
145
        r := s.adapter.FromResourceProto(req.GetResource())
4✔
146

4✔
147
        if _, err := uuid.Parse(req.GetId()); err != nil {
7✔
148
                r.GlobalURN = req.GetId()
3✔
149
        } else {
4✔
150
                r.ID = req.GetId()
1✔
151
        }
1✔
152

153
        if err := s.resourceService.Update(ctx, r); err != nil {
6✔
154
                if errors.Is(err, resource.ErrRecordNotFound) {
3✔
155
                        return nil, status.Error(codes.NotFound, "resource not found")
1✔
156
                }
1✔
157
                return nil, s.internalError(ctx, "failed to update resource: %v", err)
1✔
158
        }
159

160
        resourceProto, err := s.adapter.ToResourceProto(r)
2✔
161
        if err != nil {
3✔
162
                return nil, s.internalError(ctx, "failed to parse resource: %v", err)
1✔
163
        }
1✔
164

165
        return &guardianv1beta1.UpdateResourceResponse{
1✔
166
                Resource: resourceProto,
1✔
167
        }, nil
1✔
168
}
169

170
func (s *GRPCServer) DeleteResource(ctx context.Context, req *guardianv1beta1.DeleteResourceRequest) (*guardianv1beta1.DeleteResourceResponse, error) {
3✔
171
        if err := s.resourceService.Delete(ctx, req.GetId()); err != nil {
5✔
172
                if errors.Is(err, resource.ErrRecordNotFound) {
3✔
173
                        return nil, status.Errorf(codes.NotFound, "resource not found")
1✔
174
                }
1✔
175
                return nil, s.internalError(ctx, "failed to update resource: %v", err)
1✔
176
        }
177

178
        return &guardianv1beta1.DeleteResourceResponse{}, nil
1✔
179
}
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

© 2026 Coveralls, Inc