aboutsummaryrefslogtreecommitdiff
path: root/internal/api/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api/api.go')
-rw-r--r--internal/api/api.go95
1 files changed, 65 insertions, 30 deletions
diff --git a/internal/api/api.go b/internal/api/api.go
index 87339e19..0f610faf 100644
--- a/internal/api/api.go
+++ b/internal/api/api.go
@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"net/http"
+ "slices"
"strconv"
"strings"
"time"
@@ -451,48 +452,82 @@ func trimPath(r *http.Request, prefix string) string {
// resolveModulePath determines the correct module path for a given package path and version.
// If the module path is not provided, it searches through potential candidate module paths
-// derived from the package path. If multiple valid modules contain the package, it returns
-// a list of candidates to help the user disambiguate the request.
+// derived from the package path.
+//
+// Resolution logic:
+// 1. Use internal.CandidateModulePaths(pkgPath) to get potential candidates (ordered longest first).
+// 2. Fetch UnitMeta for each candidate that exists in the data source.
+// 3. Check if um.ModulePath == mp (where mp is the candidate module path). If not, ignore it
+// (this handles the case where GetUnitMeta falls back to another module when the requested
+// module does not exist).
+// 4. Filter candidates by eliminating those that are deprecated or retracted.
+// 5. If exactly one candidate remains after filtering, return it (HTTP 200).
+// 6. If multiple candidates remain, return HTTP 400 with the list of candidates (ambiguity).
+// 7. If all candidates are eliminated (e.g., all are deprecated or retracted), fall back to
+// the longest matching candidate among those that exist (HTTP 200).
func resolveModulePath(r *http.Request, ds internal.DataSource, pkgPath, modulePath, requestedVersion string) (*internal.UnitMeta, error) {
if requestedVersion == "" {
requestedVersion = version.Latest
}
- if modulePath == "" {
- // Handle potential ambiguity if module is not specified.
- candidates := internal.CandidateModulePaths(pkgPath)
- var validCandidates []Candidate
- var foundUM *internal.UnitMeta
- for _, mp := range candidates {
- // Check if this module actually exists and contains the package at the requested version.
- if m, err := ds.GetUnitMeta(r.Context(), pkgPath, mp, requestedVersion); err == nil {
- foundUM = m
- validCandidates = append(validCandidates, Candidate{
- ModulePath: mp,
- PackagePath: pkgPath,
- })
- } else if !errors.Is(err, derrors.NotFound) {
- return nil, err
- }
+ if modulePath != "" {
+ um, err := ds.GetUnitMeta(r.Context(), pkgPath, modulePath, requestedVersion)
+ if err != nil {
+ return nil, err
}
+ return um, nil
+ }
- if len(validCandidates) > 1 {
- return nil, &Error{
- Code: http.StatusBadRequest,
- Message: "ambiguous package path",
- Candidates: validCandidates,
+ candidates := internal.CandidateModulePaths(pkgPath)
+ var validCandidates []*internal.UnitMeta
+ for _, mp := range candidates {
+ if um, err := ds.GetUnitMeta(r.Context(), pkgPath, mp, requestedVersion); err == nil {
+ // Critical check: ensure the DB actually found the candidate module we requested.
+ // GetUnitMeta falls back to the best match if the requested module doesn't exist,
+ // which could lead to false positives (e.g. google.golang.org matching because it
+ // falls back to google.golang.org/adk/agent).
+ if um.ModulePath == mp {
+ validCandidates = append(validCandidates, um)
}
+ } else if !errors.Is(err, derrors.NotFound) {
+ return nil, err
}
- if len(validCandidates) == 0 {
- return nil, derrors.NotFound
+ }
+
+ if len(validCandidates) == 0 {
+ return nil, derrors.NotFound
+ }
+
+ // Filter candidates based on signals (deprecation, retraction).
+ goodCandidates := slices.Clone(validCandidates)
+ goodCandidates = slices.DeleteFunc(goodCandidates, func(um *internal.UnitMeta) bool {
+ return um.Deprecated || um.Retracted
+ })
+
+ switch len(goodCandidates) {
+ case 1:
+ return goodCandidates[0], nil
+ case 0:
+ // If all candidates are deprecated or retracted, fall back to the longest match.
+ // Since candidates are ordered longest first, validCandidates[0] is the longest match.
+ return validCandidates[0], nil
+ default:
+ return nil, &Error{
+ Code: http.StatusBadRequest,
+ Message: "ambiguous package path",
+ Candidates: makeCandidates(goodCandidates),
}
- return foundUM, nil
}
+}
- um, err := ds.GetUnitMeta(r.Context(), pkgPath, modulePath, requestedVersion)
- if err != nil {
- return nil, err
+func makeCandidates(ums []*internal.UnitMeta) []Candidate {
+ var r []Candidate
+ for _, um := range ums {
+ r = append(r, Candidate{
+ ModulePath: um.ModulePath,
+ PackagePath: um.Path,
+ })
}
- return um, nil
+ return r
}
// Values for the Cache-Control header.