mirror of
https://github.com/harness/drone.git
synced 2025-05-04 23:22:42 +08:00
fix: [AH-1153]: fix download count per image and per manifest (#3609)
* fix: [AH-1153]: fix query * fix: [AH-1153]: fix download count per image and per manifest
This commit is contained in:
parent
b5b463245f
commit
c58fdbc4d0
@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS download_stat_artifact_id;
|
@ -0,0 +1 @@
|
||||
CREATE INDEX download_stat_artifact_id ON download_stats(download_stat_artifact_id);
|
@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS download_stat_artifact_id;
|
@ -0,0 +1 @@
|
||||
CREATE INDEX download_stat_artifact_id ON download_stats(download_stat_artifact_id);
|
@ -518,7 +518,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiHandler := router.APIHandlerProvider(registryRepository, upstreamProxyConfigRepository, fileManager, tagRepository, manifestRepository, cleanupPolicyRepository, imageRepository, storageDriver, spaceFinder, transactor, authenticator, provider, authorizer, auditService, artifactRepository, webhooksRepository, webhooksExecutionRepository, service2, spacePathStore, reporter10)
|
||||
apiHandler := router.APIHandlerProvider(registryRepository, upstreamProxyConfigRepository, fileManager, tagRepository, manifestRepository, cleanupPolicyRepository, imageRepository, storageDriver, spaceFinder, transactor, authenticator, provider, authorizer, auditService, artifactRepository, webhooksRepository, webhooksExecutionRepository, service2, spacePathStore, reporter10, downloadStatRepository)
|
||||
mavenDBStore := maven.DBStoreProvider(registryRepository, imageRepository, artifactRepository, spaceStore, bandwidthStatRepository, downloadStatRepository, nodesRepository, upstreamProxyConfigRepository)
|
||||
mavenLocalRegistry := maven.LocalRegistryProvider(mavenDBStore, transactor, fileManager)
|
||||
mavenController := maven.ProvideProxyController(mavenLocalRegistry, secretService, spaceFinder)
|
||||
|
@ -497,7 +497,7 @@ func GetPythonArtifactDetail(
|
||||
return *artifactDetail
|
||||
}
|
||||
|
||||
func GetArtifactSummary(artifact types.ArtifactMetadata) *artifactapi.ArtifactSummaryResponseJSONResponse {
|
||||
func GetArtifactSummary(artifact types.ImageMetadata) *artifactapi.ArtifactSummaryResponseJSONResponse {
|
||||
createdAt := GetTimeInMs(artifact.CreatedAt)
|
||||
modifiedAt := GetTimeInMs(artifact.ModifiedAt)
|
||||
artifactVersionSummary := &artifactapi.ArtifactSummary{
|
||||
@ -505,7 +505,6 @@ func GetArtifactSummary(artifact types.ArtifactMetadata) *artifactapi.ArtifactSu
|
||||
ModifiedAt: &modifiedAt,
|
||||
DownloadsCount: &artifact.DownloadCount,
|
||||
ImageName: artifact.Name,
|
||||
Labels: &artifact.Labels,
|
||||
PackageType: artifact.PackageType,
|
||||
}
|
||||
response := &artifactapi.ArtifactSummaryResponseJSONResponse{
|
||||
|
@ -49,6 +49,7 @@ type APIController struct {
|
||||
RegistryMetadataHelper RegistryMetadataHelper
|
||||
WebhookService WebhookService
|
||||
ArtifactEventReporter registryevents.Reporter
|
||||
DownloadStatRepository store.DownloadStatRepository
|
||||
}
|
||||
|
||||
func NewAPIController(
|
||||
@ -73,6 +74,7 @@ func NewAPIController(
|
||||
registryMetadataHelper RegistryMetadataHelper,
|
||||
webhookService WebhookService,
|
||||
artifactEventReporter registryevents.Reporter,
|
||||
downloadStatRepository store.DownloadStatRepository,
|
||||
) *APIController {
|
||||
return &APIController{
|
||||
fileManager: fileManager,
|
||||
@ -96,5 +98,6 @@ func NewAPIController(
|
||||
RegistryMetadataHelper: registryMetadataHelper,
|
||||
WebhookService: webhookService,
|
||||
ArtifactEventReporter: artifactEventReporter,
|
||||
DownloadStatRepository: downloadStatRepository,
|
||||
}
|
||||
}
|
||||
|
@ -75,11 +75,7 @@ func (c *APIController) GetDockerArtifactManifests(
|
||||
|
||||
image := string(r.Artifact)
|
||||
version := string(r.Version)
|
||||
artifactMetadata, err := c.TagStore.GetLatestTagMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image)
|
||||
if err != nil {
|
||||
return artifactManifestsErrorRs(err), nil
|
||||
}
|
||||
manifestDetailsList, err := c.ProcessManifest(ctx, regInfo, image, version, artifactMetadata.DownloadCount)
|
||||
manifestDetailsList, err := c.ProcessManifest(ctx, regInfo, image, version)
|
||||
if err != nil {
|
||||
return artifactManifestsErrorRs(err), nil
|
||||
}
|
||||
@ -98,7 +94,7 @@ func (c *APIController) GetDockerArtifactManifests(
|
||||
|
||||
func (c *APIController) getManifestList(
|
||||
ctx context.Context, reqManifest *ml.DeserializedManifestList, registry *types.Registry, image string,
|
||||
regInfo *RegistryRequestBaseInfo, downloadCount int64,
|
||||
regInfo *RegistryRequestBaseInfo,
|
||||
) ([]artifact.DockerManifestDetails, error) {
|
||||
manifestDetailsList := []artifact.DockerManifestDetails{}
|
||||
for _, manifestEntry := range reqManifest.Manifests {
|
||||
@ -123,7 +119,11 @@ func (c *APIController) getManifestList(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifestDetailsList = append(manifestDetailsList, getManifestDetails(referencedManifest, mConfig, downloadCount))
|
||||
md, err := c.getManifestDetails(ctx, referencedManifest, mConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifestDetailsList = append(manifestDetailsList, md)
|
||||
}
|
||||
return manifestDetailsList, nil
|
||||
}
|
||||
@ -136,22 +136,31 @@ func artifactManifestsErrorRs(err error) artifact.GetDockerArtifactManifestsResp
|
||||
}
|
||||
}
|
||||
|
||||
func getManifestDetails(
|
||||
m *types.Manifest, mConfig *manifestConfig, downloadsCount int64,
|
||||
) artifact.DockerManifestDetails {
|
||||
func (c *APIController) getManifestDetails(
|
||||
ctx context.Context,
|
||||
m *types.Manifest, mConfig *manifestConfig,
|
||||
) (artifact.DockerManifestDetails, error) {
|
||||
createdAt := GetTimeInMs(m.CreatedAt)
|
||||
size := GetSize(m.TotalSize)
|
||||
|
||||
dgst, _ := types.NewDigest(m.Digest)
|
||||
image, err := c.ImageStore.GetByName(ctx, m.RegistryID, m.ImageName)
|
||||
if err != nil {
|
||||
return artifact.DockerManifestDetails{}, err
|
||||
}
|
||||
manifestDetails := artifact.DockerManifestDetails{
|
||||
Digest: m.Digest.String(),
|
||||
CreatedAt: &createdAt,
|
||||
Size: &size,
|
||||
DownloadsCount: &downloadsCount,
|
||||
Digest: m.Digest.String(),
|
||||
CreatedAt: &createdAt,
|
||||
Size: &size,
|
||||
}
|
||||
downloadCountMap, err := c.DownloadStatRepository.GetTotalDownloadsForManifests(ctx, []string{string(dgst)}, image.ID)
|
||||
if err == nil && len(downloadCountMap) > 0 {
|
||||
downloadCount := downloadCountMap[string(dgst)]
|
||||
manifestDetails.DownloadsCount = &downloadCount
|
||||
}
|
||||
if mConfig != nil {
|
||||
manifestDetails.OsArch = fmt.Sprintf("%s/%s", mConfig.Os, mConfig.Arch)
|
||||
}
|
||||
return manifestDetails
|
||||
return manifestDetails, nil
|
||||
}
|
||||
|
||||
// ProcessManifest processes a Docker artifact manifest by retrieving the manifest details from the database,
|
||||
@ -161,7 +170,7 @@ func getManifestDetails(
|
||||
func (c *APIController) ProcessManifest(
|
||||
ctx context.Context,
|
||||
regInfo *RegistryRequestBaseInfo,
|
||||
image, version string, downloadCount int64,
|
||||
image, version string,
|
||||
) ([]artifact.DockerManifestDetails, error) {
|
||||
registry, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||
if err != nil {
|
||||
@ -186,15 +195,23 @@ func (c *APIController) ProcessManifest(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifestDetailsList = append(manifestDetailsList, getManifestDetails(m, mConfig, downloadCount))
|
||||
md, err := c.getManifestDetails(ctx, m, mConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifestDetailsList = append(manifestDetailsList, md)
|
||||
case *ocischema.DeserializedManifest:
|
||||
mConfig, err := getManifestConfig(ctx, reqManifest.Config().Digest, regInfo.RootIdentifier, c.StorageDriver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifestDetailsList = append(manifestDetailsList, getManifestDetails(m, mConfig, downloadCount))
|
||||
md, err := c.getManifestDetails(ctx, m, mConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifestDetailsList = append(manifestDetailsList, md)
|
||||
case *ml.DeserializedManifestList:
|
||||
manifestDetailsList, err = c.getManifestList(ctx, reqManifest, registry, image, regInfo, downloadCount)
|
||||
manifestDetailsList, err = c.getManifestList(ctx, reqManifest, registry, image, regInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -74,29 +74,54 @@ func (c *APIController) GetArtifactSummary(
|
||||
}, nil
|
||||
}
|
||||
|
||||
var metadata *types.ArtifactMetadata
|
||||
if registry.PackageType == artifact.PackageTypeDOCKER || registry.PackageType == artifact.PackageTypeHELM {
|
||||
metadata, err = c.TagStore.GetLatestTagMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image)
|
||||
|
||||
if err != nil {
|
||||
return artifact.GetArtifactSummary500JSONResponse{
|
||||
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
metadata, err = c.ArtifactStore.GetLatestArtifactMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image)
|
||||
|
||||
if err != nil {
|
||||
return artifact.GetArtifactSummary500JSONResponse{
|
||||
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
metadata, err := c.getImageMetadata(ctx, registry, image)
|
||||
if err != nil {
|
||||
return artifact.GetArtifactSummary500JSONResponse{
|
||||
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
return artifact.GetArtifactSummary200JSONResponse{
|
||||
ArtifactSummaryResponseJSONResponse: *GetArtifactSummary(*metadata),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *APIController) getImageMetadata(
|
||||
ctx context.Context,
|
||||
registry *types.Registry,
|
||||
image string,
|
||||
) (*types.ImageMetadata, error) {
|
||||
img, err := c.ImageStore.GetByName(ctx, registry.ID, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
downloadCount, err := c.DownloadStatRepository.GetTotalDownloadsForImage(ctx, img.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgMetadata := &types.ImageMetadata{
|
||||
Name: image,
|
||||
DownloadCount: downloadCount,
|
||||
RepoName: registry.Name,
|
||||
PackageType: registry.PackageType,
|
||||
CreatedAt: img.CreatedAt,
|
||||
}
|
||||
|
||||
if registry.PackageType == artifact.PackageTypeDOCKER || registry.PackageType == artifact.PackageTypeHELM {
|
||||
latestTag, err := c.TagStore.GetLatestTag(ctx, registry.ID, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgMetadata.LatestVersion = latestTag.Name
|
||||
imgMetadata.ModifiedAt = latestTag.UpdatedAt
|
||||
} else {
|
||||
latestArtifact, err := c.ArtifactStore.GetLatestByImageID(ctx, img.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgMetadata.LatestVersion = latestArtifact.Version
|
||||
imgMetadata.ModifiedAt = latestArtifact.UpdatedAt
|
||||
}
|
||||
return imgMetadata, nil
|
||||
}
|
||||
|
@ -82,14 +82,38 @@ func (c *APIController) GetAllArtifactVersions(
|
||||
if err != nil {
|
||||
return throw500Error(err)
|
||||
}
|
||||
|
||||
img, err := c.ImageStore.GetByName(ctx, registry.ID, image)
|
||||
if err != nil {
|
||||
return throw500Error(err)
|
||||
}
|
||||
//nolint:nestif
|
||||
if registry.PackageType == artifact.PackageTypeDOCKER || registry.PackageType == artifact.PackageTypeHELM {
|
||||
tags, err := c.TagStore.GetAllTagsByRepoAndImage(
|
||||
ctx, regInfo.parentID, regInfo.RegistryIdentifier,
|
||||
image, regInfo.sortByField, regInfo.sortByOrder, regInfo.limit, regInfo.offset, regInfo.searchTerm,
|
||||
)
|
||||
if err != nil {
|
||||
return throw500Error(err)
|
||||
}
|
||||
|
||||
count, _ := c.TagStore.CountAllTagsByRepoAndImage(
|
||||
var digests []string
|
||||
for _, tag := range *tags {
|
||||
if tag.Digest != "" {
|
||||
digests = append(digests, tag.Digest)
|
||||
}
|
||||
}
|
||||
|
||||
counts, err := c.DownloadStatRepository.GetTotalDownloadsForManifests(ctx, digests, img.ID)
|
||||
if err != nil {
|
||||
return throw500Error(err)
|
||||
}
|
||||
|
||||
for i, tag := range *tags {
|
||||
if tag.Digest != "" {
|
||||
(*tags)[i].DownloadCount = counts[tag.Digest]
|
||||
}
|
||||
}
|
||||
count, err := c.TagStore.CountAllTagsByRepoAndImage(
|
||||
ctx, regInfo.parentID, regInfo.RegistryIdentifier,
|
||||
image, regInfo.searchTerm,
|
||||
)
|
||||
|
@ -47,8 +47,14 @@ func (c *APIController) GetAllRegistries(
|
||||
registryIDsParam: nil,
|
||||
recursive: r.Params.Recursive != nil && bool(*r.Params.Recursive), // default is false
|
||||
}
|
||||
regInfo, _ := c.GetRegistryRequestInfo(ctx, *registryRequestParams)
|
||||
|
||||
regInfo, err := c.GetRegistryRequestInfo(ctx, *registryRequestParams)
|
||||
if err != nil {
|
||||
return artifact.GetAllRegistries400JSONResponse{
|
||||
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
space, err := c.SpaceFinder.FindByRef(ctx, regInfo.ParentRef)
|
||||
if err != nil {
|
||||
return artifact.GetAllRegistries400JSONResponse{
|
||||
|
@ -94,6 +94,7 @@ func (c *APIController) UpdateArtifactLabels(
|
||||
return throwModifyArtifact400Error(err), nil
|
||||
}
|
||||
|
||||
// TODO: use the correct way to get download count if this endpoint is used
|
||||
tag, err := c.TagStore.GetLatestTagMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, a)
|
||||
|
||||
if err != nil {
|
||||
|
@ -76,6 +76,7 @@ func NewAPIHandler(
|
||||
webhookService registrywebhook.Service,
|
||||
spacePathStore corestore.SpacePathStore,
|
||||
artifactEventReporter registryevents.Reporter,
|
||||
downloadStatRepository store.DownloadStatRepository,
|
||||
) APIHandler {
|
||||
r := chi.NewRouter()
|
||||
r.Use(audit.Middleware())
|
||||
@ -104,6 +105,7 @@ func NewAPIHandler(
|
||||
registryMetadataHelper,
|
||||
&webhookService,
|
||||
artifactEventReporter,
|
||||
downloadStatRepository,
|
||||
)
|
||||
|
||||
handler := artifact.NewStrictHandler(apiController, []artifact.StrictMiddlewareFunc{})
|
||||
|
@ -74,6 +74,7 @@ func APIHandlerProvider(
|
||||
webhookService *registrywebhook.Service,
|
||||
spacePathStore corestore.SpacePathStore,
|
||||
artifactEventReporter *registryevents.Reporter,
|
||||
downloadStatRepository store.DownloadStatRepository,
|
||||
) harness.APIHandler {
|
||||
return harness.NewAPIHandler(
|
||||
repoDao,
|
||||
@ -97,6 +98,7 @@ func APIHandlerProvider(
|
||||
*webhookService,
|
||||
spacePathStore,
|
||||
*artifactEventReporter,
|
||||
downloadStatRepository,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -486,10 +486,17 @@ type ArtifactRepository interface {
|
||||
*[]types.Artifact,
|
||||
error,
|
||||
)
|
||||
GetLatestByImageID(ctx context.Context, imageID int64) (*types.Artifact, error)
|
||||
}
|
||||
|
||||
type DownloadStatRepository interface {
|
||||
Create(ctx context.Context, downloadStat *types.DownloadStat) error
|
||||
GetTotalDownloadsForImage(ctx context.Context, imageID int64) (int64, error)
|
||||
GetTotalDownloadsForManifests(
|
||||
ctx context.Context,
|
||||
artifactVersion []string,
|
||||
imageID int64,
|
||||
) (map[string]int64, error)
|
||||
}
|
||||
|
||||
type BandwidthStatRepository interface {
|
||||
|
@ -110,6 +110,25 @@ func (a ArtifactDao) GetByRegistryIDAndImage(ctx context.Context, registryID int
|
||||
return &artifacts, nil
|
||||
}
|
||||
|
||||
func (a ArtifactDao) GetLatestByImageID(ctx context.Context, imageID int64) (*types.Artifact, error) {
|
||||
q := databaseg.Builder.Select(util.ArrToStringByDelimiter(util.GetDBTagsFromStruct(artifactDB{}), ",")).
|
||||
From("artifacts").
|
||||
Where("artifact_image_id = ?", imageID).OrderBy("artifact_updated_at DESC").Limit(1)
|
||||
|
||||
sql, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, a.db)
|
||||
|
||||
dst := new(artifactDB)
|
||||
if err = db.GetContext(ctx, dst, sql, args...); err != nil {
|
||||
return nil, databaseg.ProcessSQLErrorf(ctx, err, "Failed to get artifact")
|
||||
}
|
||||
return a.mapToArtifact(ctx, dst)
|
||||
}
|
||||
|
||||
func (a ArtifactDao) CreateOrUpdate(ctx context.Context, artifact *types.Artifact) error {
|
||||
const sqlQuery = `
|
||||
INSERT INTO artifacts (
|
||||
|
@ -17,16 +17,19 @@ package database
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/registry/app/store"
|
||||
"github.com/harness/gitness/registry/app/store/database/util"
|
||||
"github.com/harness/gitness/registry/types"
|
||||
databaseg "github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type DownloadStatDao struct {
|
||||
@ -81,8 +84,69 @@ func (d DownloadStatDao) Create(ctx context.Context, downloadStat *types.Downloa
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d DownloadStatDao) mapToInternalDownloadStat(ctx context.Context,
|
||||
in *types.DownloadStat) *downloadStatDB {
|
||||
func (d DownloadStatDao) GetTotalDownloadsForImage(ctx context.Context, imageID int64) (int64, error) {
|
||||
q := databaseg.Builder.Select(`count(*)`).
|
||||
From("artifacts art").Where("art.artifact_image_id = ?", imageID).
|
||||
Join("download_stats ds ON ds.download_stat_artifact_id = art.artifact_id")
|
||||
|
||||
sql, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
// Log the final sql query
|
||||
finalQuery := util.FormatQuery(sql, args)
|
||||
log.Ctx(ctx).Debug().Str("sql", finalQuery).Msg("Executing GetTotalDownloadsForImage query")
|
||||
// Execute query
|
||||
db := dbtx.GetAccessor(ctx, d.db)
|
||||
|
||||
var count int64
|
||||
err = db.QueryRowContext(ctx, sql, args...).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, databaseg.ProcessSQLErrorf(ctx, err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (d DownloadStatDao) GetTotalDownloadsForManifests(
|
||||
ctx context.Context,
|
||||
artifactVersions []string,
|
||||
imageID int64,
|
||||
) (map[string]int64, error) {
|
||||
q := databaseg.Builder.Select(`art.artifact_version, count(*)`).
|
||||
From("artifacts art").
|
||||
Join("download_stats ds ON ds.download_stat_artifact_id = art.artifact_id").Where(sq.And{
|
||||
sq.Eq{"artifact_image_id": imageID},
|
||||
sq.Eq{"artifact_version": artifactVersions},
|
||||
}, "art").GroupBy("art.artifact_version")
|
||||
|
||||
sql, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
// Log the final sql query
|
||||
finalQuery := util.FormatQuery(sql, args)
|
||||
log.Ctx(ctx).Debug().Str("sql", finalQuery).Msg("Executing GetTotalDownloadsForManifests query")
|
||||
// Execute query
|
||||
db := dbtx.GetAccessor(ctx, d.db)
|
||||
|
||||
dst := []*versionsCountDB{}
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, databaseg.ProcessSQLErrorf(ctx, err, "Failed executing query")
|
||||
}
|
||||
|
||||
// Convert the slice to a map
|
||||
result := make(map[string]int64)
|
||||
for _, v := range dst {
|
||||
result[v.Version] = v.Count
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d DownloadStatDao) mapToInternalDownloadStat(
|
||||
ctx context.Context,
|
||||
in *types.DownloadStat,
|
||||
) *downloadStatDB {
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
if in.CreatedAt.IsZero() {
|
||||
in.CreatedAt = time.Now()
|
||||
@ -104,3 +168,8 @@ func (d DownloadStatDao) mapToInternalDownloadStat(ctx context.Context,
|
||||
UpdatedBy: session.Principal.ID,
|
||||
}
|
||||
}
|
||||
|
||||
type versionsCountDB struct {
|
||||
Version string `db:"artifact_version"`
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
@ -253,8 +253,10 @@ func (i ImageDao) CreateOrUpdate(ctx context.Context, image *types.Image) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i ImageDao) GetLabelsByParentIDAndRepo(ctx context.Context, parentID int64, repo string,
|
||||
limit int, offset int, search string) (labels []string, err error) {
|
||||
func (i ImageDao) GetLabelsByParentIDAndRepo(
|
||||
ctx context.Context, parentID int64, repo string,
|
||||
limit int, offset int, search string,
|
||||
) (labels []string, err error) {
|
||||
q := databaseg.Builder.Select("a.image_labels as labels").
|
||||
From("images a").
|
||||
Join("registries r ON r.registry_id = a.image_registry_id").
|
||||
@ -264,7 +266,8 @@ func (i ImageDao) GetLabelsByParentIDAndRepo(ctx context.Context, parentID int64
|
||||
q = q.Where("a.image_labels LIKE ?", "%"+search+"%")
|
||||
}
|
||||
|
||||
q = q.OrderBy("a.image_labels ASC").Limit(uint64(limit)).Offset(uint64(offset))
|
||||
q = q.OrderBy("a.image_labels ASC").
|
||||
Limit(util.SafeIntToUInt64(limit)).Offset(util.SafeIntToUInt64(offset))
|
||||
|
||||
sql, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
@ -282,8 +285,10 @@ func (i ImageDao) GetLabelsByParentIDAndRepo(ctx context.Context, parentID int64
|
||||
return i.mapToImageLabels(dst), nil
|
||||
}
|
||||
|
||||
func (i ImageDao) CountLabelsByParentIDAndRepo(ctx context.Context, parentID int64, repo,
|
||||
search string) (count int64, err error) {
|
||||
func (i ImageDao) CountLabelsByParentIDAndRepo(
|
||||
ctx context.Context, parentID int64, repo,
|
||||
search string,
|
||||
) (count int64, err error) {
|
||||
q := databaseg.Builder.Select("a.image_labels as labels").
|
||||
From("images a").
|
||||
Join("registries r ON r.registry_id = a.image_registry_id").
|
||||
@ -309,8 +314,10 @@ func (i ImageDao) CountLabelsByParentIDAndRepo(ctx context.Context, parentID int
|
||||
return int64(len(dst)), nil
|
||||
}
|
||||
|
||||
func (i ImageDao) GetByRepoAndName(ctx context.Context, parentID int64,
|
||||
repo string, name string) (*types.Image, error) {
|
||||
func (i ImageDao) GetByRepoAndName(
|
||||
ctx context.Context, parentID int64,
|
||||
repo string, name string,
|
||||
) (*types.Image, error) {
|
||||
q := databaseg.Builder.Select("a.image_id, a.image_name, "+
|
||||
" a.image_registry_id, a.image_labels, a.image_created_at, "+
|
||||
" a.image_updated_at, a.image_created_by, a.image_updated_by").
|
||||
@ -449,8 +456,10 @@ func (i ImageDao) mapToImageLabels(dst []*imageLabelDB) []string {
|
||||
return res
|
||||
}
|
||||
|
||||
func (i ImageDao) mapToImageLabel(elements map[string]bool, res []string,
|
||||
dst *imageLabelDB) (map[string]bool, []string) {
|
||||
func (i ImageDao) mapToImageLabel(
|
||||
elements map[string]bool, res []string,
|
||||
dst *imageLabelDB,
|
||||
) (map[string]bool, []string) {
|
||||
if dst == nil {
|
||||
return elements, res
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ type tagMetadataDB struct {
|
||||
NonConformant bool `db:"manifest_non_conformant"`
|
||||
Payload []byte `db:"manifest_payload"`
|
||||
MediaType string `db:"mt_media_type"`
|
||||
Digest []byte `db:"manifest_digest"`
|
||||
DownloadCount int64 `db:"download_count"`
|
||||
}
|
||||
|
||||
@ -969,38 +970,16 @@ func (t tagDao) GetAllTagsByRepoAndImage(
|
||||
image string, sortByField string, sortByOrder string, limit int, offset int,
|
||||
search string,
|
||||
) (*[]types.TagMetadata, error) {
|
||||
// Define download count subquery
|
||||
downloadCountSubquery := `
|
||||
SELECT
|
||||
a.artifact_image_id,
|
||||
COUNT(d.download_stat_id) AS download_count,
|
||||
i.image_name,
|
||||
i.image_registry_id
|
||||
FROM artifacts a
|
||||
JOIN download_stats d ON d.download_stat_artifact_id = a.artifact_id
|
||||
JOIN images i ON i.image_id = a.artifact_image_id
|
||||
GROUP BY a.artifact_image_id, i.image_name, i.image_registry_id
|
||||
`
|
||||
|
||||
// Build the main query
|
||||
q := databaseg.Builder.
|
||||
Select(`
|
||||
t.tag_name AS name,
|
||||
m.manifest_total_size AS size,
|
||||
r.registry_package_type AS package_type,
|
||||
t.tag_updated_at AS modified_at,
|
||||
m.manifest_schema_version,
|
||||
m.manifest_non_conformant,
|
||||
m.manifest_payload,
|
||||
mt.mt_media_type,
|
||||
COALESCE(dc.download_count, 0) AS download_count
|
||||
`).
|
||||
q := databaseg.Builder.Select(
|
||||
`t.tag_name as name, m.manifest_total_size as size,
|
||||
r.registry_package_type as package_type, t.tag_updated_at as modified_at,
|
||||
m.manifest_schema_version, m.manifest_non_conformant, m.manifest_payload,
|
||||
mt.mt_media_type, m.manifest_digest`,
|
||||
).
|
||||
From("tags t").
|
||||
Join("registries r ON t.tag_registry_id = r.registry_id").
|
||||
Join("manifests m ON t.tag_manifest_id = m.manifest_id").
|
||||
Join("media_types mt ON mt.mt_id = m.manifest_media_type_id").
|
||||
LeftJoin(fmt.Sprintf("(%s) AS dc ON t.tag_image_name = dc.image_name "+
|
||||
"AND t.tag_registry_id = dc.image_registry_id", downloadCountSubquery)).
|
||||
Where(
|
||||
"r.registry_parent_id = ? AND r.registry_name = ? AND t.tag_image_name = ?",
|
||||
parentID, repoKey, image,
|
||||
@ -1009,11 +988,7 @@ func (t tagDao) GetAllTagsByRepoAndImage(
|
||||
if search != "" {
|
||||
q = q.Where("tag_name LIKE ?", sqlPartialMatch(search))
|
||||
}
|
||||
sortField := "tag_" + sortByField
|
||||
if sortByField == downloadCount {
|
||||
sortField = downloadCount
|
||||
}
|
||||
q = q.OrderBy(sortField + " " + sortByOrder).Limit(uint64(limit)).Offset(uint64(offset)) //nolint:gosec
|
||||
q = q.OrderBy("t.tag_" + sortByField + " " + sortByOrder).Limit(uint64(limit)).Offset(uint64(offset)) //nolint:gosec
|
||||
|
||||
sql, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
@ -1039,7 +1014,7 @@ func (t tagDao) CountAllTagsByRepoAndImage(
|
||||
Join("manifests ON tag_manifest_id = manifest_id").
|
||||
Where(
|
||||
"registry_parent_id = ? AND registry_name = ?"+
|
||||
"AND tag_image_name = ?", parentID, repoKey, image,
|
||||
" AND tag_image_name = ?", parentID, repoKey, image,
|
||||
)
|
||||
|
||||
if search != "" {
|
||||
@ -1222,7 +1197,7 @@ func (t tagDao) mapToTagMetadata(
|
||||
_ context.Context,
|
||||
dst *tagMetadataDB,
|
||||
) (*types.TagMetadata, error) {
|
||||
return &types.TagMetadata{
|
||||
tagMetadata := &types.TagMetadata{
|
||||
Name: dst.Name,
|
||||
Size: dst.Size,
|
||||
PackageType: dst.PackageType,
|
||||
@ -1233,7 +1208,13 @@ func (t tagDao) mapToTagMetadata(
|
||||
MediaType: dst.MediaType,
|
||||
Payload: dst.Payload,
|
||||
DownloadCount: dst.DownloadCount,
|
||||
}, nil
|
||||
}
|
||||
if dst.Digest != nil {
|
||||
dgst := types.Digest(util.GetHexEncodedString(dst.Digest))
|
||||
tagMetadata.Digest = string(dgst)
|
||||
}
|
||||
|
||||
return tagMetadata, nil
|
||||
}
|
||||
|
||||
func (t tagDao) mapToTagDetail(
|
||||
|
@ -45,6 +45,16 @@ type ArtifactMetadata struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
type ImageMetadata struct {
|
||||
Name string
|
||||
RepoName string
|
||||
DownloadCount int64
|
||||
PackageType artifact.PackageType
|
||||
LatestVersion string
|
||||
CreatedAt time.Time
|
||||
ModifiedAt time.Time
|
||||
}
|
||||
|
||||
type TagMetadata struct {
|
||||
Name string
|
||||
Size string
|
||||
@ -55,6 +65,7 @@ type TagMetadata struct {
|
||||
NonConformant bool
|
||||
Payload Payload
|
||||
MediaType string
|
||||
Digest string
|
||||
DownloadCount int64
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user