github.com/google/osv-scalibr@v0.4.1/clients/datasource/insights.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package datasource provides clients to fetch data from different APIs. 16 package datasource 17 18 import ( 19 "context" 20 "crypto/x509" 21 "fmt" 22 "sync" 23 "time" 24 25 pb "deps.dev/api/v3" 26 "google.golang.org/grpc" 27 "google.golang.org/grpc/credentials" 28 ) 29 30 // CachedInsightsClient is a wrapper for InsightsClient that caches requests. 31 type CachedInsightsClient struct { 32 pb.InsightsClient 33 34 // cache fields 35 mu sync.Mutex 36 cacheTimestamp *time.Time 37 packageCache *RequestCache[packageKey, *pb.Package] 38 versionCache *RequestCache[versionKey, *pb.Version] 39 requirementsCache *RequestCache[versionKey, *pb.Requirements] 40 } 41 42 // Comparable types to use as map keys for cache. 43 type packageKey struct { 44 System pb.System 45 Name string 46 } 47 48 func makePackageKey(k *pb.PackageKey) packageKey { 49 return packageKey{ 50 System: k.GetSystem(), 51 Name: k.GetName(), 52 } 53 } 54 55 type versionKey struct { 56 System pb.System 57 Name string 58 Version string 59 } 60 61 func makeVersionKey(k *pb.VersionKey) versionKey { 62 return versionKey{ 63 System: k.GetSystem(), 64 Name: k.GetName(), 65 Version: k.GetVersion(), 66 } 67 } 68 69 // NewCachedInsightsClient creates a CachedInsightsClient. 70 func NewCachedInsightsClient(addr string, userAgent string) (*CachedInsightsClient, error) { 71 certPool, err := x509.SystemCertPool() 72 if err != nil { 73 return nil, fmt.Errorf("getting system cert pool: %w", err) 74 } 75 creds := credentials.NewClientTLSFromCert(certPool, "") 76 dialOpts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} 77 78 if userAgent != "" { 79 dialOpts = append(dialOpts, grpc.WithUserAgent(userAgent)) 80 } 81 82 conn, err := grpc.NewClient(addr, dialOpts...) 83 if err != nil { 84 return nil, fmt.Errorf("dialling %q: %w", addr, err) 85 } 86 87 return &CachedInsightsClient{ 88 InsightsClient: pb.NewInsightsClient(conn), 89 packageCache: NewRequestCache[packageKey, *pb.Package](), 90 versionCache: NewRequestCache[versionKey, *pb.Version](), 91 requirementsCache: NewRequestCache[versionKey, *pb.Requirements](), 92 }, nil 93 } 94 95 // GetPackage returns metadata about a package by querying deps.dev API. 96 func (c *CachedInsightsClient) GetPackage(ctx context.Context, in *pb.GetPackageRequest, opts ...grpc.CallOption) (*pb.Package, error) { 97 return c.packageCache.Get(makePackageKey(in.GetPackageKey()), func() (*pb.Package, error) { 98 return c.InsightsClient.GetPackage(ctx, in, opts...) 99 }) 100 } 101 102 // GetVersion returns metadata about a version by querying deps.dev API. 103 func (c *CachedInsightsClient) GetVersion(ctx context.Context, in *pb.GetVersionRequest, opts ...grpc.CallOption) (*pb.Version, error) { 104 return c.versionCache.Get(makeVersionKey(in.GetVersionKey()), func() (*pb.Version, error) { 105 return c.InsightsClient.GetVersion(ctx, in, opts...) 106 }) 107 } 108 109 // GetRequirements returns requirements of the given version by querying deps.dev API. 110 func (c *CachedInsightsClient) GetRequirements(ctx context.Context, in *pb.GetRequirementsRequest, opts ...grpc.CallOption) (*pb.Requirements, error) { 111 return c.requirementsCache.Get(makeVersionKey(in.GetVersionKey()), func() (*pb.Requirements, error) { 112 return c.InsightsClient.GetRequirements(ctx, in, opts...) 113 }) 114 }