github.com/google/osv-scalibr@v0.4.1/enricher/packagedeprecation/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 packagedeprecation
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  
    22  	grpcpb "deps.dev/api/v3alpha"
    23  	"github.com/google/osv-scalibr/log"
    24  )
    25  
    26  const (
    27  	maxBatchSize = 5000 // Per deps.dev API documentation.
    28  )
    29  
    30  // Client is the interface for the deps.dev client.
    31  type Client interface {
    32  	GetVersionBatch(ctx context.Context, req Request) (Response, error)
    33  }
    34  
    35  // Request is the request for the deps.dev client.
    36  type Request struct {
    37  	VersionKeys []VersionKey
    38  }
    39  
    40  // Response is the response for the deps.dev client.
    41  type Response struct {
    42  	Results map[VersionKey]bool
    43  }
    44  
    45  // VersionKey contains the components to query a package version on deps.dev.
    46  type VersionKey struct {
    47  	System  grpcpb.System
    48  	Name    string
    49  	Version string
    50  }
    51  
    52  // GRPCClient is the GRPC client for deps.dev.
    53  type GRPCClient struct {
    54  	client grpcpb.InsightsClient
    55  }
    56  
    57  // NewClient returns a new GRPCClient for deps.dev
    58  func NewClient(c grpcpb.InsightsClient) *GRPCClient {
    59  	return &GRPCClient{client: c}
    60  }
    61  
    62  // GetVersionBatch queries deps.dev for deprecation status for the given versions.
    63  // It handles chunking requests to deps.dev based on maxDepsdevBatchSize, and pagination of results
    64  // within each chunk.
    65  func (c *GRPCClient) GetVersionBatch(ctx context.Context, req Request) (Response, error) {
    66  	if c.client == nil {
    67  		return Response{}, errors.New("deps.dev gRPC client not initialized")
    68  	}
    69  
    70  	results := make(map[VersionKey]bool)
    71  	vers := req.VersionKeys
    72  
    73  	for i := 0; i < len(vers); i += maxBatchSize {
    74  		// Splitting list of package versions into chunks of 5000 (maxBatchSize).
    75  		end := min(i+maxBatchSize, len(vers))
    76  		chunk := vers[i:end]
    77  
    78  		batchReq := makeBatchReq(chunk)
    79  
    80  		// Handle pagination (if any) of the batch response.
    81  		for {
    82  			batchResp, err := c.client.GetVersionBatch(ctx, batchReq)
    83  			if err != nil {
    84  				return Response{}, fmt.Errorf("depsdev.GetVersionBatch failed: %w", err)
    85  			}
    86  
    87  			for _, resp := range batchResp.GetResponses() {
    88  				// Version not found in deps.dev
    89  				if resp.GetVersion() == nil {
    90  					continue
    91  				}
    92  
    93  				// Using the version key from the request (instead of from responses.version) because the
    94  				// package and version names might be canonicalized by deps.dev.
    95  				reqVer := resp.GetRequest().GetVersionKey()
    96  				ver := VersionKey{
    97  					System:  reqVer.GetSystem(),
    98  					Name:    reqVer.GetName(),
    99  					Version: reqVer.GetVersion(),
   100  				}
   101  				results[ver] = resp.GetVersion().GetIsDeprecated()
   102  			}
   103  
   104  			if batchResp.GetNextPageToken() == "" {
   105  				break
   106  			}
   107  
   108  			updateBatchReq(batchReq, batchResp.GetNextPageToken())
   109  		}
   110  	}
   111  
   112  	log.Debugf("Package deprecation enricher: Finished querying deps.dev for deprecation status. Number of results: %d", len(results))
   113  
   114  	return Response{Results: results}, nil
   115  }