in registry/handlers/repositories.go [205:326]
func (h *repositoryHandler) HandleGetRepository(w http.ResponseWriter, r *http.Request) {
l := log.GetLogger(log.WithContext(h)).WithFields(log.Fields{"path": h.Repository.Named().Name()})
l.Debug("HandleGetRepository")
var withSize bool
sizeVal := sizeQueryParamValue(r)
if sizeVal != "" {
if !isQueryParamValueValid(sizeVal, sizeQueryParamValidValues) {
detail := v1.InvalidQueryParamValueErrorDetail(sizeQueryParamKey, sizeQueryParamValidValues)
h.Errors = append(h.Errors, v1.ErrorCodeInvalidQueryParamValue.WithDetail(detail))
return
}
withSize = true
}
var opts []datastore.RepositoryStoreOption
if h.GetRepoCache() != nil {
opts = append(opts, datastore.WithRepositoryCache(h.GetRepoCache()))
}
store := datastore.NewRepositoryStore(h.db.Primary(), opts...)
repo, err := store.FindByPath(h.Context, h.Repository.Named().Name())
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
if repo == nil {
// If the caller is requesting the aggregated size of repository `foo/bar` including descendants, it might be
// the case that `foo/bar` does not exist but there is an e.g. `foo/bar/car`. In such case we should not raise a
// 404 if the base repository (`foo/bar`) does not exist. This is required to allow retrieving the Project level
// usage when there is no "root" repository for such project but there is at least one sub-repository.
if !withSize || sizeVal != sizeQueryParamSelfWithDescendantsValue {
h.Errors = append(h.Errors, v2.ErrorCodeNameUnknown)
return
}
// If this is the case, we need to find the corresponding top-level namespace. That must exist. If not we
// throw a 404 Not Found here.
repo = &models.Repository{Path: h.Repository.Named().Name()}
ns := datastore.NewNamespaceStore(h.db.Primary())
n, err := ns.FindByName(h.Context, repo.TopLevelPathSegment())
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
if n == nil {
h.Errors = append(h.Errors, v2.ErrorCodeNameUnknown)
return
}
// path and namespace ID are the two required parameters for the queries in repositoryStore.SizeWithDescendants,
// so we must fill those. We also fill the name for consistency on the response.
repo.NamespaceID = n.ID
repo.Name = repo.Path[strings.LastIndex(repo.Path, "/")+1:]
}
resp := RepositoryAPIResponse{
Name: repo.Name,
Path: repo.Path,
}
if !repo.CreatedAt.IsZero() {
resp.CreatedAt = timeToString(repo.CreatedAt)
}
if repo.UpdatedAt.Valid {
resp.UpdatedAt = timeToString(repo.UpdatedAt.Time)
}
if repo.LastPublishedAt.Valid {
resp.LastPublishedAt = timeToString(repo.LastPublishedAt.Time)
}
if withSize {
var size datastore.RepositorySize
precision := sizePrecisionDefault
t := time.Now()
ctx := h.Context.Context
db := h.db.UpToDateReplica(ctx, repo)
l := l.WithFields(log.Fields{"db_host_type": h.db.TypeOf(db), "db_host_addr": db.Address()})
store := datastore.NewRepositoryStore(db, opts...)
switch sizeVal {
case sizeQueryParamSelfValue:
size, err = store.Size(ctx, repo)
case sizeQueryParamSelfWithDescendantsValue:
size, err = store.SizeWithDescendants(ctx, repo)
if err != nil {
var pgErr *pgconn.PgError
// if this same query has timed out in the last 24h OR times out now, fallback to estimation
// nolint: revive // max-control-nesting
if errors.Is(err, datastore.ErrSizeHasTimedOut) || (errors.As(err, &pgErr) && pgErr.Code == pgerrcode.QueryCanceled) {
size, err = store.EstimatedSizeWithDescendants(ctx, repo)
precision = sizePrecisionUntagged
}
}
}
l.WithError(err).WithFields(log.Fields{
"size_bytes": size.Bytes(),
"size_cached": size.Cached(),
"size_type": sizeVal,
"duration_ms": time.Since(t).Milliseconds(),
"is_top_level": repo.IsTopLevel(),
"root_repo": repo.TopLevelPathSegment(),
"precision": precision,
"failure": err != nil,
}).Info("repository size measurement")
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
b := size.Bytes()
resp.Size = &b
resp.SizePrecision = precision
}
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
if err := enc.Encode(resp); err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
}