github.com/google/osv-scalibr@v0.4.1/clients/resolution/combined_native_client.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 resolution 16 17 import ( 18 "context" 19 "fmt" 20 "sync" 21 22 "deps.dev/util/resolve" 23 "github.com/google/osv-scalibr/clients/datasource" 24 ) 25 26 // CombinedNativeClient is a ResolutionClient that combines all the native clients: 27 // MavenRegistryClient, NPMRegistryClient, PyPIRegistryClient. 28 // Individual clients are lazy-initialized when needed. 29 type CombinedNativeClient struct { 30 opts CombinedNativeClientOptions 31 32 mu sync.Mutex 33 mavenRegistryClient *MavenRegistryClient 34 npmRegistryClient *NPMRegistryClient 35 pypiRegistryClient *PyPIRegistryClient 36 } 37 38 // CombinedNativeClientOptions contains the options each client in the CombinedNativeClient. 39 type CombinedNativeClientOptions struct { 40 ProjectDir string // The project directory to use, currently only used for NPM to find .npmrc files. 41 LocalRegistry string // The local directory to store the downloaded manifests during resolution. 42 MavenRegistry string // The default Maven registry to use. 43 PyPIRegistry string // The default PyPI registry to use. 44 MavenClient *datasource.MavenRegistryAPIClient // The Maven registry client to use, if nil, a new client will be created. 45 DisableGoogleAuth bool // If true, do not try to create google.DefaultClient for Artifact Registry. 46 } 47 48 // NewCombinedNativeClient makes a new CombinedNativeClient. 49 func NewCombinedNativeClient(opts CombinedNativeClientOptions) (*CombinedNativeClient, error) { 50 client := &CombinedNativeClient{opts: opts} 51 if opts.MavenClient != nil { 52 client.mavenRegistryClient = NewMavenRegistryClientWithAPI(opts.MavenClient) 53 } 54 return client, nil 55 } 56 57 // Version returns metadata of a version specified by the VersionKey. 58 func (c *CombinedNativeClient) Version(ctx context.Context, vk resolve.VersionKey) (resolve.Version, error) { 59 client, err := c.clientForSystem(ctx, vk.System) 60 if err != nil { 61 return resolve.Version{}, err 62 } 63 return client.Version(ctx, vk) 64 } 65 66 // Versions returns all the available versions of the package specified by the given PackageKey. 67 func (c *CombinedNativeClient) Versions(ctx context.Context, pk resolve.PackageKey) ([]resolve.Version, error) { 68 client, err := c.clientForSystem(ctx, pk.System) 69 if err != nil { 70 return nil, err 71 } 72 return client.Versions(ctx, pk) 73 } 74 75 // Requirements returns requirements of a version specified by the VersionKey. 76 func (c *CombinedNativeClient) Requirements(ctx context.Context, vk resolve.VersionKey) ([]resolve.RequirementVersion, error) { 77 client, err := c.clientForSystem(ctx, vk.System) 78 if err != nil { 79 return nil, err 80 } 81 return client.Requirements(ctx, vk) 82 } 83 84 // MatchingVersions returns versions matching the requirement specified by the VersionKey. 85 func (c *CombinedNativeClient) MatchingVersions(ctx context.Context, vk resolve.VersionKey) ([]resolve.Version, error) { 86 client, err := c.clientForSystem(ctx, vk.System) 87 if err != nil { 88 return nil, err 89 } 90 return client.MatchingVersions(ctx, vk) 91 } 92 93 // AddRegistries adds registries to the MavenRegistryClient. 94 func (c *CombinedNativeClient) AddRegistries(ctx context.Context, registries []Registry) error { 95 // TODO(#541): Currently only MavenRegistryClient supports adding registries. 96 // We might need to add support for PyPIRegistryClient. 97 // But this AddRegistries method should take a system as input, 98 // so that we can add registries to the corresponding client. 99 client, err := c.clientForSystem(ctx, resolve.Maven) 100 if err != nil { 101 return err 102 } 103 regCl, ok := client.(ClientWithRegistries) 104 if !ok { 105 // Currently should not happen. 106 return nil 107 } 108 return regCl.AddRegistries(ctx, registries) 109 } 110 111 func (c *CombinedNativeClient) clientForSystem(ctx context.Context, sys resolve.System) (resolve.Client, error) { 112 c.mu.Lock() 113 defer c.mu.Unlock() 114 115 var err error 116 switch sys { 117 case resolve.Maven: 118 if c.mavenRegistryClient == nil { 119 c.mavenRegistryClient, err = NewMavenRegistryClient(ctx, c.opts.MavenRegistry, c.opts.LocalRegistry, c.opts.DisableGoogleAuth) 120 if err != nil { 121 return nil, err 122 } 123 } 124 return c.mavenRegistryClient, nil 125 case resolve.NPM: 126 if c.npmRegistryClient == nil { 127 c.npmRegistryClient, err = NewNPMRegistryClient(c.opts.ProjectDir) 128 if err != nil { 129 return nil, err 130 } 131 } 132 return c.npmRegistryClient, nil 133 case resolve.PyPI: 134 if c.pypiRegistryClient == nil { 135 c.pypiRegistryClient = NewPyPIRegistryClient(c.opts.PyPIRegistry, c.opts.LocalRegistry) 136 } 137 return c.pypiRegistryClient, nil 138 case resolve.UnknownSystem: 139 fallthrough 140 default: 141 return nil, fmt.Errorf("unsupported system: %v", sys) 142 } 143 }