diff --git a/controllers/project_controller.go b/controllers/project_controller.go index 31889b2fc..51fab757b 100644 --- a/controllers/project_controller.go +++ b/controllers/project_controller.go @@ -19,9 +19,13 @@ import ( "encoding/json" "fmt" "io" + "log/slog" + cdx "github.com/CycloneDX/cyclonedx-go" + "github.com/google/uuid" "github.com/l3montree-dev/devguard/database/models" "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/normalize" "github.com/l3montree-dev/devguard/shared" "github.com/l3montree-dev/devguard/transformer" "github.com/l3montree-dev/devguard/utils" @@ -30,18 +34,30 @@ import ( ) type ProjectController struct { - projectRepository shared.ProjectRepository - assetRepository shared.AssetRepository - projectService shared.ProjectService - webhookRepository shared.WebhookIntegrationRepository + projectRepository shared.ProjectRepository + assetRepository shared.AssetRepository + assetVersionRepository shared.AssetVersionRepository + artifactRepository shared.ArtifactRepository + assetVersionService shared.AssetVersionService + assetService shared.AssetService + releaseService shared.ReleaseService + projectService shared.ProjectService + webhookRepository shared.WebhookIntegrationRepository + scanService shared.ScanService } -func NewProjectController(repository shared.ProjectRepository, assetRepository shared.AssetRepository, projectService shared.ProjectService, webhookRepository shared.WebhookIntegrationRepository) *ProjectController { +func NewProjectController(repository shared.ProjectRepository, assetRepository shared.AssetRepository, assetVersionRepository shared.AssetVersionRepository, artifactRepository shared.ArtifactRepository, assetVersionService shared.AssetVersionService, assetService shared.AssetService, releaseService shared.ReleaseService, projectService shared.ProjectService, webhookRepository shared.WebhookIntegrationRepository, scanService shared.ScanService) *ProjectController { return &ProjectController{ - projectRepository: repository, - assetRepository: assetRepository, - projectService: projectService, - webhookRepository: webhookRepository, + projectRepository: repository, + assetRepository: assetRepository, + assetVersionRepository: assetVersionRepository, + artifactRepository: artifactRepository, + assetVersionService: assetVersionService, + assetService: assetService, + releaseService: releaseService, + projectService: projectService, + webhookRepository: webhookRepository, + scanService: scanService, } } @@ -517,3 +533,315 @@ func (ProjectController *ProjectController) UpdateConfigFile(ctx shared.Context) } return ctx.String(200, configContent) } + +func (ProjectController *ProjectController) HandleDynamicProject(ctx shared.Context) error { + + body, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return echo.NewHTTPError(400, fmt.Sprintf("could not read request body: %s", err.Error())).WithInternal(err) + } + + var probe dtos.DynamicProjectRequestDTO + if err := json.Unmarshal(body, &probe); err != nil { + return echo.NewHTTPError(400, fmt.Sprintf("could not parse request body: %s", err.Error())).WithInternal(err) + } + + if probe.ProjectExternalEntityID == "" || probe.AssetExternalEntityID == "" { + return echo.NewHTTPError(400, "verb, projectExternalEntityId, and assetExternalEntityId are required") + } + + action := probe.Verb + projectName := probe.ProjectName + projectExternalEntityID := probe.ProjectExternalEntityID + projectDescription := probe.ProjectDescription + + subProjectExternalEntityID := probe.SubProjectExternalEntityID + subProjectName := probe.SubProjectName + subProjectDescription := probe.SubProjectDescription + + assetName := probe.AssetName + assetExternalEntityID := probe.AssetExternalEntityID + assetDescription := probe.AssetDescription + + assetVersionName := probe.AssetVersionName + artifactName := probe.Artifact + + providerID := shared.GetProviderID(ctx) + organization := shared.GetOrg(ctx) + parentProject := shared.GetProject(ctx) + userID := shared.GetSession(ctx).GetUserID() + + if action == "delete" { + parentProjectID := parentProject.ID + proExternalEntityID := projectExternalEntityID + if subProjectExternalEntityID != "" { + subProjectParent, err := ProjectController.projectRepository.GetDirectChildProjectsWithProviderIDAndExternalEntityID(ctx.Request().Context(), nil, parentProject.ID, providerID, projectExternalEntityID) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not fetch sub-projects: %s", err.Error())).WithInternal(err) + } + parentProjectID = subProjectParent.ID + proExternalEntityID = subProjectExternalEntityID + } + err := ProjectController.projectRepository.CleanupDynamicProject(ctx.Request().Context(), nil, organization.GetID(), parentProjectID, providerID, proExternalEntityID, assetExternalEntityID, assetVersionName, artifactName) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not delete project: %s", err.Error())).WithInternal(err) + } + + return ctx.JSON(200, map[string]string{"message": "project and asset deleted successfully"}) + } else if action != "update" { + return echo.NewHTTPError(400, "invalid verb, only 'update' and 'delete' are allowed") + } + + bom := new(cdx.BOM) + + if probe.Sbom == nil { + return echo.NewHTTPError(400, "sbom is required") + } + if err := json.Unmarshal(probe.Sbom, bom); err != nil { + return echo.NewHTTPError(400, fmt.Sprintf("could not parse CycloneDX BOM: %s", err.Error())).WithInternal(err) + } + + project, err := ProjectController.projectService.FindOrCreateProject(ctx, providerID, organization.GetID(), projectName, projectExternalEntityID, parentProject.ID, projectDescription) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not create project: %s", err.Error())).WithInternal(err) + } + + pID := project.ID + + if subProjectExternalEntityID != "" { + subProject, err := ProjectController.projectService.FindOrCreateProject(ctx, providerID, organization.GetID(), subProjectName, subProjectExternalEntityID, project.ID, subProjectDescription) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not create sub-project: %s", err.Error())).WithInternal(err) + } + pID = subProject.ID + } + + rbac := shared.GetRBAC(ctx) + asset, err := ProjectController.assetService.FindOrCreateAsset(ctx.Request().Context(), rbac, providerID, organization.GetID(), pID, assetName, assetExternalEntityID, userID, assetDescription) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not create asset: %s", err.Error())).WithInternal(err) + } + + assetVersion, err := ProjectController.assetVersionRepository.FindOrCreate(ctx.Request().Context(), nil, assetVersionName, asset.ID, false, nil) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not create asset version: %s", err.Error())).WithInternal(err) + } + + artifact := models.Artifact{ + ArtifactName: artifactName, + AssetVersionName: assetVersion.Name, + AssetID: asset.ID, + } + if err := ProjectController.artifactRepository.Save(ctx.Request().Context(), nil, &artifact); err != nil { + slog.Error("trivy operator: could not save artifact", "err", err) + return ctx.JSON(500, map[string]string{"error": "could not save artifact"}) + } + + release, err := ProjectController.releaseService.FindOrCreate(ctx.Request().Context(), parentProject.ID, providerID) + if err != nil { + return echo.NewHTTPError(500, fmt.Sprintf("could not create release: %s", err.Error())).WithInternal(err) + } + + //add or update release item + releaseItem := models.ReleaseItem{ + ReleaseID: release.ID, + ArtifactName: &artifact.ArtifactName, + AssetID: &asset.ID, + AssetVersionName: &assetVersion.Name, + } + + err = ProjectController.releaseService.AddItem(ctx.Request().Context(), &releaseItem) + if err != nil { + slog.Error("could not add release item", "err", err) + return ctx.JSON(500, map[string]string{"error": "could not add release item"}) + } + + normalized, err := normalize.SBOMGraphFromCycloneDX(bom, artifactName, "operator", asset.KeepOriginalSbomRootComponent) + if err != nil { + slog.Error("trivy operator: failed to normalize BOM", "err", err) + return ctx.JSON(400, map[string]string{"error": "could not normalize SBOM"}) + } + + wholeSBOM, err := ProjectController.assetVersionService.UpdateSBOM(ctx.Request().Context(), nil, organization, *project, *asset, assetVersion, artifactName, normalized) + if err != nil { + slog.Error("trivy operator: could not update SBOM", "err", err) + return ctx.JSON(500, map[string]string{"error": "could not update SBOM"}) + } + + tx := ProjectController.artifactRepository.GetDB(ctx.Request().Context(), nil).Begin() + defer tx.Rollback() + + userAgent := ctx.Request().UserAgent() + _, _, _, err = ProjectController.scanService.ScanNormalizedSBOM(ctx.Request().Context(), tx, organization, *project, *asset, assetVersion, artifact, wholeSBOM, userID, &userAgent) + if err != nil { + slog.Error("trivy operator: scan failed", "err", err) + return ctx.JSON(500, map[string]string{"error": "scan failed"}) + } + + tx.Commit() + return ctx.JSON(200, map[string]string{"message": "project and asset created, SBOM processed and scan started successfully"}) +} + +func (ProjectController *ProjectController) ListDynamicProjects(ctx shared.Context) error { + reqCtx := ctx.Request().Context() + parentProject := shared.GetProject(ctx) + providerID := shared.GetProviderID(ctx) + + // Query 1: direct child projects filtered by providerID + projects, err := ProjectController.projectRepository.GetDirectChildProjectsWithProviderID(reqCtx, nil, parentProject.ID, providerID) + if err != nil { + return echo.NewHTTPError(500, "could not list projects").WithInternal(err) + } + if len(projects) == 0 { + return ctx.JSON(200, []dtos.ProjectsAssetAssetVersionsDTO{}) + } + + projectIDs := make([]uuid.UUID, len(projects)) + for i, p := range projects { + projectIDs[i] = p.ID + } + + // Query 2: all sub-projects for all parent projects in one shot + subProjects, err := ProjectController.projectRepository.GetChildProjectsForParents(reqCtx, nil, projectIDs, providerID) + if err != nil { + return echo.NewHTTPError(500, "could not list sub-projects").WithInternal(err) + } + + subProjectIDs := make([]uuid.UUID, len(subProjects)) + for i, sp := range subProjects { + subProjectIDs[i] = sp.ID + } + + // Query 3: all assets for projects + sub-projects, filtered by providerID + allProjectIDs := append(projectIDs, subProjectIDs...) + allAssets, err := ProjectController.assetRepository.GetByProjectIDsWithProviderID(reqCtx, nil, allProjectIDs, providerID) + if err != nil { + return echo.NewHTTPError(500, "could not list assets").WithInternal(err) + } + + allAssetIDs := make([]uuid.UUID, len(allAssets)) + for i, a := range allAssets { + allAssetIDs[i] = a.ID + } + + // Query 4: all asset versions for all assets + allAssetVersions, err := ProjectController.assetVersionRepository.GetAssetVersionsByAssetIDs(reqCtx, nil, allAssetIDs) + if err != nil { + return echo.NewHTTPError(500, "could not list asset versions").WithInternal(err) + } + + // Query 5: all artifacts for all assets + allArtifacts, err := ProjectController.artifactRepository.GetByAssetIDs(reqCtx, nil, allAssetIDs) + if err != nil { + return echo.NewHTTPError(500, "could not list artifacts").WithInternal(err) + } + + // Build in-memory lookup maps + subProjectsByParentID := make(map[uuid.UUID][]models.Project) + for _, sp := range subProjects { + if sp.ParentID != nil { + subProjectsByParentID[*sp.ParentID] = append(subProjectsByParentID[*sp.ParentID], sp) + } + } + + assetsByProjectID := make(map[uuid.UUID][]models.Asset) + for _, a := range allAssets { + assetsByProjectID[a.ProjectID] = append(assetsByProjectID[a.ProjectID], a) + } + + versionsByAssetID := make(map[uuid.UUID][]models.AssetVersion) + for _, av := range allAssetVersions { + versionsByAssetID[av.AssetID] = append(versionsByAssetID[av.AssetID], av) + } + + artifactsByAssetIDAndVersion := make(map[uuid.UUID]map[string][]string) + for _, art := range allArtifacts { + if artifactsByAssetIDAndVersion[art.AssetID] == nil { + artifactsByAssetIDAndVersion[art.AssetID] = make(map[string][]string) + } + artifactsByAssetIDAndVersion[art.AssetID][art.AssetVersionName] = append( + artifactsByAssetIDAndVersion[art.AssetID][art.AssetVersionName], art.ArtifactName, + ) + } + + buildAssetEntries := func(projectID uuid.UUID) []struct { + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetVersions []struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + } `json:"assetVersions"` + } { + var entries []struct { + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetVersions []struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + } `json:"assetVersions"` + } + for _, asset := range assetsByProjectID[projectID] { + if asset.ExternalEntityID == nil { + continue + } + versions := versionsByAssetID[asset.ID] + if len(versions) == 0 { + continue + } + assetEntry := struct { + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetVersions []struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + } `json:"assetVersions"` + }{AssetExternalEntityID: *asset.ExternalEntityID, AssetName: asset.Name} + for _, av := range versions { + avEntry := struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + }{AssetVersionName: av.Name, Artifacts: artifactsByAssetIDAndVersion[asset.ID][av.Name]} + assetEntry.AssetVersions = append(assetEntry.AssetVersions, avEntry) + } + entries = append(entries, assetEntry) + } + return entries + } + + result := make([]dtos.ProjectsAssetAssetVersionsDTO, 0, len(projects)) + for _, project := range projects { + if project.ExternalEntityID == nil { + continue + } + entry := dtos.ProjectsAssetAssetVersionsDTO{ + ProjectExternalEntityID: *project.ExternalEntityID, + ProjectName: project.Name, + } + + for _, sp := range subProjectsByParentID[project.ID] { + if sp.ExternalEntityID == nil { + continue + } + spEntry := struct { + SubProjectExternalEntityID string `json:"subProjectExternalEntityId,omitempty"` + SubProjectName string `json:"subProjectName,omitempty"` + SubProjectDescription string `json:"subProjectDescription,omitempty"` + Assets []struct { + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetVersions []struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + } `json:"assetVersions"` + } `json:"assets"` + }{SubProjectExternalEntityID: *sp.ExternalEntityID, SubProjectName: sp.Name} + spEntry.Assets = buildAssetEntries(sp.ID) + entry.SubProjects = append(entry.SubProjects, spEntry) + } + + entry.Assets = buildAssetEntries(project.ID) + result = append(result, entry) + } + + return ctx.JSON(200, result) +} diff --git a/database/models/project_model.go b/database/models/project_model.go index deec3e8dd..94446cc93 100644 --- a/database/models/project_model.go +++ b/database/models/project_model.go @@ -8,9 +8,8 @@ import ( type ProjectType string const ( - ProjectTypeDefault ProjectType = "default" - ProjectTypeKubernetesNamespace ProjectType = "kubernetesNamespace" - ProjectTypeKubernetesCluster ProjectType = "kubernetesCluster" + ProjectTypeDefault ProjectType = "default" + ProjectTypeDynamic ProjectType = "dynamic" ) type ProjectState string diff --git a/database/repositories/artifact_repository.go b/database/repositories/artifact_repository.go index 9b40e7119..8994ea30f 100644 --- a/database/repositories/artifact_repository.go +++ b/database/repositories/artifact_repository.go @@ -24,6 +24,26 @@ func NewArtifactRepository(db *gorm.DB) *artifactRepository { } } +func (r *artifactRepository) GetByAssetID(ctx context.Context, tx *gorm.DB, assetID uuid.UUID) ([]models.Artifact, error) { + var artifacts []models.Artifact + err := r.GetDB(ctx, tx).Where("asset_id = ?", assetID).Find(&artifacts).Error + if err != nil { + return nil, err + } + + return artifacts, nil +} + +func (r *artifactRepository) GetByAssetIDs(ctx context.Context, tx *gorm.DB, assetIDs []uuid.UUID) ([]models.Artifact, error) { + var artifacts []models.Artifact + err := r.GetDB(ctx, tx).Where("asset_id IN ?", assetIDs).Find(&artifacts).Error + if err != nil { + return nil, err + } + + return artifacts, nil +} + func (r *artifactRepository) GetByAssetIDAndAssetVersionName(ctx context.Context, tx *gorm.DB, assetID uuid.UUID, assetVersionName string) ([]models.Artifact, error) { var artifacts []models.Artifact err := r.GetDB(ctx, tx).Where("asset_id = ? AND asset_version_name = ?", assetID, assetVersionName).Find(&artifacts).Error diff --git a/database/repositories/asset_repository.go b/database/repositories/asset_repository.go index 1319dc1a8..c7ff91a28 100644 --- a/database/repositories/asset_repository.go +++ b/database/repositories/asset_repository.go @@ -194,6 +194,15 @@ func (repository *assetRepository) GetByProjectIDs(ctx context.Context, tx *gorm return apps, nil } +func (repository *assetRepository) GetByProjectIDsWithProviderID(ctx context.Context, tx *gorm.DB, projectIDs []uuid.UUID, providerID string) ([]models.Asset, error) { + var apps []models.Asset + err := repository.GetDB(ctx, tx).Where("project_id IN ? AND external_entity_provider_id = ?", projectIDs, providerID).Find(&apps).Error + if err != nil { + return nil, err + } + return apps, nil +} + func (repository *assetRepository) ReadBySlug(ctx context.Context, tx *gorm.DB, projectID uuid.UUID, slug string) (models.Asset, error) { var t models.Asset err := repository.GetDB(ctx, tx).Where("slug = ? AND project_id = ?", slug, projectID).Preload("AssetVersions").First(&t).Error @@ -285,3 +294,36 @@ func (repository *assetRepository) GetAssetsWithVulnSharingEnabled(ctx context.C ).Preload("Project").Find(&assets).Error return assets, err } + +func (repository *assetRepository) UpsertSplit(ctx context.Context, tx *gorm.DB, externalProviderID string, assets []*models.Asset) ([]*models.Asset, []*models.Asset, error) { + var existingAssets []models.Asset + err := repository.GetDB(ctx, tx).Where("external_entity_id IN (?) AND external_entity_provider_id = ?", utils.Map(assets, func(a *models.Asset) *string { return a.ExternalEntityID }), externalProviderID).Find(&existingAssets).Error + if err != nil { + return nil, nil, err + } + + existingMap := make(map[string]bool) + for _, a := range existingAssets { + existingMap[*a.ExternalEntityID] = true + } + + err = repository.Upsert(ctx, tx, &assets, []clause.Column{ + {Name: "external_entity_provider_id"}, + {Name: "external_entity_id"}, + }, []string{"name", "description", "project_id", "avatar"}) + if err != nil { + return nil, nil, err + } + + newAssets := make([]*models.Asset, 0) + updatedAssets := make([]*models.Asset, 0) + for _, a := range assets { + if !existingMap[*a.ExternalEntityID] { + newAssets = append(newAssets, a) + } else { + updatedAssets = append(updatedAssets, a) + } + } + + return newAssets, updatedAssets, nil +} diff --git a/database/repositories/asset_version_repository.go b/database/repositories/asset_version_repository.go index 979805865..29404fb6c 100644 --- a/database/repositories/asset_version_repository.go +++ b/database/repositories/asset_version_repository.go @@ -236,6 +236,12 @@ func (repository *assetVersionRepository) GetAssetVersionsByAssetID(ctx context. return assets, err } +func (repository *assetVersionRepository) GetAssetVersionsByAssetIDs(ctx context.Context, tx *gorm.DB, assetIDs []uuid.UUID) ([]models.AssetVersion, error) { + var assets []models.AssetVersion + err := repository.GetDB(ctx, tx).Where("asset_id IN ?", assetIDs).Find(&assets).Error + return assets, err +} + func (repository *assetVersionRepository) GetAssetVersionsByAssetIDWithArtifacts(ctx context.Context, tx *gorm.DB, assetID uuid.UUID) ([]models.AssetVersion, error) { var assetVersion []models.AssetVersion err := repository.GetDB(ctx, tx).Preload("Artifacts").Where("asset_id = ?", assetID).Find(&assetVersion).Error diff --git a/database/repositories/project_repository.go b/database/repositories/project_repository.go index 6bcf1e225..fd22b8d43 100644 --- a/database/repositories/project_repository.go +++ b/database/repositories/project_repository.go @@ -459,6 +459,24 @@ func (g *projectRepository) GetDirectChildProjects(ctx context.Context, tx *gorm return projects, err } +func (g *projectRepository) GetDirectChildProjectsWithProviderIDAndExternalEntityID(ctx context.Context, tx *gorm.DB, parentID uuid.UUID, providerID string, externalEntityID string) (models.Project, error) { + var project models.Project + err := g.GetDB(ctx, tx).Debug().Where("parent_id = ? AND external_entity_provider_id = ? AND external_entity_id = ?", parentID, providerID, externalEntityID).First(&project).Error + return project, err +} + +func (g *projectRepository) GetDirectChildProjectsWithProviderID(ctx context.Context, tx *gorm.DB, parentID uuid.UUID, providerID string) ([]models.Project, error) { + var projects []models.Project + err := g.GetDB(ctx, tx).Where("parent_id = ? AND external_entity_provider_id = ?", parentID, providerID).Find(&projects).Error + return projects, err +} + +func (g *projectRepository) GetChildProjectsForParents(ctx context.Context, tx *gorm.DB, parentIDs []uuid.UUID, providerID string) ([]models.Project, error) { + var projects []models.Project + err := g.GetDB(ctx, tx).Where("parent_id IN ? AND external_entity_provider_id = ?", parentIDs, providerID).Find(&projects).Error + return projects, err +} + func (g *projectRepository) EnablePolicyForProject(ctx context.Context, tx *gorm.DB, projectID uuid.UUID, policyID uuid.UUID) error { return g.GetDB(ctx, tx).Model(&models.Project{ Model: models.Model{ @@ -624,3 +642,64 @@ func (g *projectRepository) Upsert(ctx context.Context, tx *gorm.DB, t *[]*model return g.GetDB(ctx, tx).Clauses(clause.OnConflict{UpdateAll: true, Columns: conflictingColumns}).Create(t).Error } + +func (g *projectRepository) CleanupDynamicProject(ctx context.Context, tx *gorm.DB, organizationID uuid.UUID, parentProjectID uuid.UUID, providerID string, projectExternalEntityID string, assetExternalEntityID string, assetVersionName string, artifactName string) error { + + query := ` +WITH + target AS ( + SELECT + p.id AS project_id, + a.id AS asset_id, + av.name AS asset_version_name + FROM projects p + JOIN assets a ON a.project_id = p.id AND a.external_entity_id = ? + JOIN asset_versions av ON av.asset_id = a.id AND av.name = ? + JOIN artifacts ar ON ar.asset_version_name = av.name AND ar.asset_id = a.id + WHERE p.organization_id = ? + AND p.parent_id = ? + AND p.external_entity_provider_id = ? + AND p.external_entity_id = ? + AND p.type = 'dynamic' + LIMIT 1 + ), + del_artifact AS ( + DELETE FROM artifacts + WHERE asset_id = (SELECT asset_id FROM target) + AND asset_version_name = (SELECT asset_version_name FROM target) + AND artifact_name = ? + ), + del_asset_version AS ( + DELETE FROM asset_versions + WHERE asset_id = (SELECT asset_id FROM target) + AND name = (SELECT asset_version_name FROM target) + AND NOT EXISTS ( + SELECT 1 FROM artifacts + WHERE asset_id = (SELECT asset_id FROM target) + AND asset_version_name = (SELECT asset_version_name FROM target) + ) + ), + del_asset AS ( + DELETE FROM assets + WHERE id = (SELECT asset_id FROM target) + AND NOT EXISTS ( + SELECT 1 FROM asset_versions + WHERE asset_id = (SELECT asset_id FROM target) + ) + ), + del_project AS ( + DELETE FROM projects + WHERE id = (SELECT project_id FROM target) + AND NOT EXISTS ( + SELECT 1 FROM assets + WHERE project_id = (SELECT project_id FROM target) + AND id != (SELECT asset_id FROM target) + ) + ) +SELECT 1` + + return g.GetDB(ctx, tx).Exec(query, + assetExternalEntityID, assetVersionName, + organizationID, parentProjectID, providerID, projectExternalEntityID, artifactName, + ).Error +} diff --git a/database/repositories/release_repository.go b/database/repositories/release_repository.go index 4afaf8f14..b4c32c5dd 100644 --- a/database/repositories/release_repository.go +++ b/database/repositories/release_repository.go @@ -267,3 +267,22 @@ func (r *releaseRepository) GetCandidateItemsForRelease(ctx context.Context, tx return artifacts, rels, nil } + +func (r *releaseRepository) FindOrCreate(ctx context.Context, tx *gorm.DB, projectID uuid.UUID, name string) (models.Release, error) { + var rel models.Release + err := r.GetDB(ctx, tx).Where("project_id = ? AND name = ?", projectID, name).First(&rel).Error + if err == nil { + return rel, nil + } + if err != nil && err != gorm.ErrRecordNotFound { + return models.Release{}, err + } + + rel = models.Release{ + Name: name, + ProjectID: projectID, + } + + err = r.GetDB(ctx, tx).Create(&rel).Error + return rel, err +} diff --git a/dtos/project_dto.go b/dtos/project_dto.go index a587f3f92..02f03249f 100644 --- a/dtos/project_dto.go +++ b/dtos/project_dto.go @@ -16,6 +16,7 @@ package dtos import ( + "encoding/json" "time" "github.com/google/uuid" @@ -95,3 +96,44 @@ type ProjectAssetDTO struct { SubGroupsAndAssets []ProjectAssetDTO `json:"subGroupsAndAsset" gorm:"-"` } + +type ProjectsAssetAssetVersionsDTO struct { + ProjectExternalEntityID string `json:"projectExternalEntityId"` + ProjectName string `json:"projectName"` + SubProjects []struct { + SubProjectExternalEntityID string `json:"subProjectExternalEntityId,omitempty"` + SubProjectName string `json:"subProjectName,omitempty"` + SubProjectDescription string `json:"subProjectDescription,omitempty"` + Assets []struct { + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetVersions []struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + } `json:"assetVersions"` + } `json:"assets"` + } `json:"subProjects,omitempty"` + Assets []struct { + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetVersions []struct { + AssetVersionName string `json:"assetVersionName"` + Artifacts []string `json:"artifacts"` + } `json:"assetVersions"` + } `json:"assets"` +} +type DynamicProjectRequestDTO struct { + Verb string `json:"verb"` + ProjectExternalEntityID string `json:"projectExternalEntityId"` + ProjectName string `json:"projectName"` + ProjectDescription string `json:"projectDescription"` + SubProjectExternalEntityID string `json:"subProjectExternalEntityId,omitempty"` + SubProjectName string `json:"subProjectName,omitempty"` + SubProjectDescription string `json:"subProjectDescription,omitempty"` + AssetExternalEntityID string `json:"assetExternalEntityId"` + AssetName string `json:"assetName"` + AssetDescription string `json:"assetDescription"` + AssetVersionName string `json:"assetVersionName"` + Artifact string `json:"artifact"` + Sbom json.RawMessage `json:"sbom,omitempty"` +} diff --git a/middlewares/external_entity_provider_middlewares.go b/middlewares/external_entity_provider_middlewares.go index 0e12a1b98..b4faab2ca 100644 --- a/middlewares/external_entity_provider_middlewares.go +++ b/middlewares/external_entity_provider_middlewares.go @@ -3,14 +3,36 @@ package middlewares import ( "context" "log/slog" + "strings" "sync" "time" + "github.com/l3montree-dev/devguard/integrations/gitlabint" "github.com/l3montree-dev/devguard/shared" "github.com/labstack/echo/v4" "go.opentelemetry.io/otel" ) +// ProviderIDMiddleware extracts the :providerID URL param, normalizes it, stores it in the context, +// and rejects IDs that collide with a configured GitLab integration. +func ProviderIDMiddleware(gitlabIntegrations map[string]*gitlabint.GitlabOauth2Config) shared.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(ctx shared.Context) error { + providerID := ctx.Param("providerID") + providerID = strings.TrimSuffix(providerID, "/") + providerID = "dn:" + providerID // prefix to avoid collisions with other provider IDs in the future + if providerID == "" { + return echo.NewHTTPError(400, "providerID is required") + } + if _, isGitLab := gitlabIntegrations[providerID]; isGitLab { + return echo.NewHTTPError(400, "providerID is reserved") + } + shared.SetProviderID(ctx, providerID) + return next(ctx) + } + } +} + // ExternalEntityProviderOrgSyncMiddleware returns a middleware that triggers a background org sync // for external entity providers. It rate-limits per user so the sync runs at most once every 15 minutes. func ExternalEntityProviderOrgSyncMiddleware(externalEntityProviderService shared.ExternalEntityProviderService) shared.MiddlewareFunc { diff --git a/mocks/mock_ArtifactRepository.go b/mocks/mock_ArtifactRepository.go index 4f47130fe..910da19f4 100644 --- a/mocks/mock_ArtifactRepository.go +++ b/mocks/mock_ArtifactRepository.go @@ -677,6 +677,80 @@ func (_c *ArtifactRepository_GetAllArtifactAffectedByDependencyVuln_Call) RunAnd return _c } +// GetByAssetID provides a mock function for the type ArtifactRepository +func (_mock *ArtifactRepository) GetByAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID) ([]models.Artifact, error) { + ret := _mock.Called(ctx, tx, assetID) + + if len(ret) == 0 { + panic("no return value specified for GetByAssetID") + } + + var r0 []models.Artifact + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) ([]models.Artifact, error)); ok { + return returnFunc(ctx, tx, assetID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) []models.Artifact); ok { + r0 = returnFunc(ctx, tx, assetID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Artifact) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, assetID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ArtifactRepository_GetByAssetID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByAssetID' +type ArtifactRepository_GetByAssetID_Call struct { + *mock.Call +} + +// GetByAssetID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetID uuid.UUID +func (_e *ArtifactRepository_Expecter) GetByAssetID(ctx interface{}, tx interface{}, assetID interface{}) *ArtifactRepository_GetByAssetID_Call { + return &ArtifactRepository_GetByAssetID_Call{Call: _e.mock.On("GetByAssetID", ctx, tx, assetID)} +} + +func (_c *ArtifactRepository_GetByAssetID_Call) Run(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID)) *ArtifactRepository_GetByAssetID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ArtifactRepository_GetByAssetID_Call) Return(artifacts []models.Artifact, err error) *ArtifactRepository_GetByAssetID_Call { + _c.Call.Return(artifacts, err) + return _c +} + +func (_c *ArtifactRepository_GetByAssetID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID) ([]models.Artifact, error)) *ArtifactRepository_GetByAssetID_Call { + _c.Call.Return(run) + return _c +} + // GetByAssetIDAndAssetVersionName provides a mock function for the type ArtifactRepository func (_mock *ArtifactRepository) GetByAssetIDAndAssetVersionName(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string) ([]models.Artifact, error) { ret := _mock.Called(ctx, tx, assetID, assetVersionName) @@ -757,6 +831,80 @@ func (_c *ArtifactRepository_GetByAssetIDAndAssetVersionName_Call) RunAndReturn( return _c } +// GetByAssetIDs provides a mock function for the type ArtifactRepository +func (_mock *ArtifactRepository) GetByAssetIDs(ctx context.Context, tx shared.DB, assetIDs []uuid.UUID) ([]models.Artifact, error) { + ret := _mock.Called(ctx, tx, assetIDs) + + if len(ret) == 0 { + panic("no return value specified for GetByAssetIDs") + } + + var r0 []models.Artifact + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) ([]models.Artifact, error)); ok { + return returnFunc(ctx, tx, assetIDs) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) []models.Artifact); ok { + r0 = returnFunc(ctx, tx, assetIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Artifact) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, assetIDs) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ArtifactRepository_GetByAssetIDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByAssetIDs' +type ArtifactRepository_GetByAssetIDs_Call struct { + *mock.Call +} + +// GetByAssetIDs is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetIDs []uuid.UUID +func (_e *ArtifactRepository_Expecter) GetByAssetIDs(ctx interface{}, tx interface{}, assetIDs interface{}) *ArtifactRepository_GetByAssetIDs_Call { + return &ArtifactRepository_GetByAssetIDs_Call{Call: _e.mock.On("GetByAssetIDs", ctx, tx, assetIDs)} +} + +func (_c *ArtifactRepository_GetByAssetIDs_Call) Run(run func(ctx context.Context, tx shared.DB, assetIDs []uuid.UUID)) *ArtifactRepository_GetByAssetIDs_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []uuid.UUID + if args[2] != nil { + arg2 = args[2].([]uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ArtifactRepository_GetByAssetIDs_Call) Return(artifacts []models.Artifact, err error) *ArtifactRepository_GetByAssetIDs_Call { + _c.Call.Return(artifacts, err) + return _c +} + +func (_c *ArtifactRepository_GetByAssetIDs_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetIDs []uuid.UUID) ([]models.Artifact, error)) *ArtifactRepository_GetByAssetIDs_Call { + _c.Call.Return(run) + return _c +} + // GetByAssetVersions provides a mock function for the type ArtifactRepository func (_mock *ArtifactRepository) GetByAssetVersions(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionNames []string) ([]models.Artifact, error) { ret := _mock.Called(ctx, tx, assetID, assetVersionNames) diff --git a/mocks/mock_AssetRepository.go b/mocks/mock_AssetRepository.go index c3954e3c4..bd3b8f488 100644 --- a/mocks/mock_AssetRepository.go +++ b/mocks/mock_AssetRepository.go @@ -1130,6 +1130,160 @@ func (_c *AssetRepository_GetByProjectID_Call) RunAndReturn(run func(ctx context return _c } +// GetByProjectIDs provides a mock function for the type AssetRepository +func (_mock *AssetRepository) GetByProjectIDs(ctx context.Context, tx shared.DB, projectIDs []uuid.UUID) ([]models.Asset, error) { + ret := _mock.Called(ctx, tx, projectIDs) + + if len(ret) == 0 { + panic("no return value specified for GetByProjectIDs") + } + + var r0 []models.Asset + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) ([]models.Asset, error)); ok { + return returnFunc(ctx, tx, projectIDs) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) []models.Asset); ok { + r0 = returnFunc(ctx, tx, projectIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Asset) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, projectIDs) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AssetRepository_GetByProjectIDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByProjectIDs' +type AssetRepository_GetByProjectIDs_Call struct { + *mock.Call +} + +// GetByProjectIDs is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - projectIDs []uuid.UUID +func (_e *AssetRepository_Expecter) GetByProjectIDs(ctx interface{}, tx interface{}, projectIDs interface{}) *AssetRepository_GetByProjectIDs_Call { + return &AssetRepository_GetByProjectIDs_Call{Call: _e.mock.On("GetByProjectIDs", ctx, tx, projectIDs)} +} + +func (_c *AssetRepository_GetByProjectIDs_Call) Run(run func(ctx context.Context, tx shared.DB, projectIDs []uuid.UUID)) *AssetRepository_GetByProjectIDs_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []uuid.UUID + if args[2] != nil { + arg2 = args[2].([]uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *AssetRepository_GetByProjectIDs_Call) Return(assets []models.Asset, err error) *AssetRepository_GetByProjectIDs_Call { + _c.Call.Return(assets, err) + return _c +} + +func (_c *AssetRepository_GetByProjectIDs_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, projectIDs []uuid.UUID) ([]models.Asset, error)) *AssetRepository_GetByProjectIDs_Call { + _c.Call.Return(run) + return _c +} + +// GetByProjectIDsWithProviderID provides a mock function for the type AssetRepository +func (_mock *AssetRepository) GetByProjectIDsWithProviderID(ctx context.Context, tx shared.DB, projectIDs []uuid.UUID, providerID string) ([]models.Asset, error) { + ret := _mock.Called(ctx, tx, projectIDs, providerID) + + if len(ret) == 0 { + panic("no return value specified for GetByProjectIDsWithProviderID") + } + + var r0 []models.Asset + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID, string) ([]models.Asset, error)); ok { + return returnFunc(ctx, tx, projectIDs, providerID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID, string) []models.Asset); ok { + r0 = returnFunc(ctx, tx, projectIDs, providerID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Asset) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, projectIDs, providerID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AssetRepository_GetByProjectIDsWithProviderID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByProjectIDsWithProviderID' +type AssetRepository_GetByProjectIDsWithProviderID_Call struct { + *mock.Call +} + +// GetByProjectIDsWithProviderID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - projectIDs []uuid.UUID +// - providerID string +func (_e *AssetRepository_Expecter) GetByProjectIDsWithProviderID(ctx interface{}, tx interface{}, projectIDs interface{}, providerID interface{}) *AssetRepository_GetByProjectIDsWithProviderID_Call { + return &AssetRepository_GetByProjectIDsWithProviderID_Call{Call: _e.mock.On("GetByProjectIDsWithProviderID", ctx, tx, projectIDs, providerID)} +} + +func (_c *AssetRepository_GetByProjectIDsWithProviderID_Call) Run(run func(ctx context.Context, tx shared.DB, projectIDs []uuid.UUID, providerID string)) *AssetRepository_GetByProjectIDsWithProviderID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []uuid.UUID + if args[2] != nil { + arg2 = args[2].([]uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *AssetRepository_GetByProjectIDsWithProviderID_Call) Return(assets []models.Asset, err error) *AssetRepository_GetByProjectIDsWithProviderID_Call { + _c.Call.Return(assets, err) + return _c +} + +func (_c *AssetRepository_GetByProjectIDsWithProviderID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, projectIDs []uuid.UUID, providerID string) ([]models.Asset, error)) *AssetRepository_GetByProjectIDsWithProviderID_Call { + _c.Call.Return(run) + return _c +} + // GetDB provides a mock function for the type AssetRepository func (_mock *AssetRepository) GetDB(ctx context.Context, tx shared.DB) shared.DB { ret := _mock.Called(ctx, tx) @@ -1945,8 +2099,8 @@ func (_c *AssetRepository_Update_Call) RunAndReturn(run func(ctx context.Context } // Upsert provides a mock function for the type AssetRepository -func (_mock *AssetRepository) Upsert(ctx context.Context, tx shared.DB, t *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string) error { - ret := _mock.Called(ctx, tx, t, conflictingColumns, updateOnly) +func (_mock *AssetRepository) Upsert(ctx context.Context, tx shared.DB, assets *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string) error { + ret := _mock.Called(ctx, tx, assets, conflictingColumns, updateOnly) if len(ret) == 0 { panic("no return value specified for Upsert") @@ -1954,7 +2108,7 @@ func (_mock *AssetRepository) Upsert(ctx context.Context, tx shared.DB, t *[]*mo var r0 error if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *[]*models.Asset, []clause.Column, []string) error); ok { - r0 = returnFunc(ctx, tx, t, conflictingColumns, updateOnly) + r0 = returnFunc(ctx, tx, assets, conflictingColumns, updateOnly) } else { r0 = ret.Error(0) } @@ -1969,14 +2123,14 @@ type AssetRepository_Upsert_Call struct { // Upsert is a helper method to define mock.On call // - ctx context.Context // - tx shared.DB -// - t *[]*models.Asset +// - assets *[]*models.Asset // - conflictingColumns []clause.Column // - updateOnly []string -func (_e *AssetRepository_Expecter) Upsert(ctx interface{}, tx interface{}, t interface{}, conflictingColumns interface{}, updateOnly interface{}) *AssetRepository_Upsert_Call { - return &AssetRepository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, tx, t, conflictingColumns, updateOnly)} +func (_e *AssetRepository_Expecter) Upsert(ctx interface{}, tx interface{}, assets interface{}, conflictingColumns interface{}, updateOnly interface{}) *AssetRepository_Upsert_Call { + return &AssetRepository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, tx, assets, conflictingColumns, updateOnly)} } -func (_c *AssetRepository_Upsert_Call) Run(run func(ctx context.Context, tx shared.DB, t *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string)) *AssetRepository_Upsert_Call { +func (_c *AssetRepository_Upsert_Call) Run(run func(ctx context.Context, tx shared.DB, assets *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string)) *AssetRepository_Upsert_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { @@ -2014,7 +2168,95 @@ func (_c *AssetRepository_Upsert_Call) Return(err error) *AssetRepository_Upsert return _c } -func (_c *AssetRepository_Upsert_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string) error) *AssetRepository_Upsert_Call { +func (_c *AssetRepository_Upsert_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assets *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string) error) *AssetRepository_Upsert_Call { + _c.Call.Return(run) + return _c +} + +// UpsertSplit provides a mock function for the type AssetRepository +func (_mock *AssetRepository) UpsertSplit(ctx context.Context, tx shared.DB, externalProviderID string, assets []*models.Asset) ([]*models.Asset, []*models.Asset, error) { + ret := _mock.Called(ctx, tx, externalProviderID, assets) + + if len(ret) == 0 { + panic("no return value specified for UpsertSplit") + } + + var r0 []*models.Asset + var r1 []*models.Asset + var r2 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, []*models.Asset) ([]*models.Asset, []*models.Asset, error)); ok { + return returnFunc(ctx, tx, externalProviderID, assets) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, []*models.Asset) []*models.Asset); ok { + r0 = returnFunc(ctx, tx, externalProviderID, assets) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.Asset) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, string, []*models.Asset) []*models.Asset); ok { + r1 = returnFunc(ctx, tx, externalProviderID, assets) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]*models.Asset) + } + } + if returnFunc, ok := ret.Get(2).(func(context.Context, shared.DB, string, []*models.Asset) error); ok { + r2 = returnFunc(ctx, tx, externalProviderID, assets) + } else { + r2 = ret.Error(2) + } + return r0, r1, r2 +} + +// AssetRepository_UpsertSplit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertSplit' +type AssetRepository_UpsertSplit_Call struct { + *mock.Call +} + +// UpsertSplit is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - externalProviderID string +// - assets []*models.Asset +func (_e *AssetRepository_Expecter) UpsertSplit(ctx interface{}, tx interface{}, externalProviderID interface{}, assets interface{}) *AssetRepository_UpsertSplit_Call { + return &AssetRepository_UpsertSplit_Call{Call: _e.mock.On("UpsertSplit", ctx, tx, externalProviderID, assets)} +} + +func (_c *AssetRepository_UpsertSplit_Call) Run(run func(ctx context.Context, tx shared.DB, externalProviderID string, assets []*models.Asset)) *AssetRepository_UpsertSplit_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 []*models.Asset + if args[3] != nil { + arg3 = args[3].([]*models.Asset) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *AssetRepository_UpsertSplit_Call) Return(assets1 []*models.Asset, assets2 []*models.Asset, err error) *AssetRepository_UpsertSplit_Call { + _c.Call.Return(assets1, assets2, err) + return _c +} + +func (_c *AssetRepository_UpsertSplit_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, externalProviderID string, assets []*models.Asset) ([]*models.Asset, []*models.Asset, error)) *AssetRepository_UpsertSplit_Call { _c.Call.Return(run) return _c } diff --git a/mocks/mock_AssetService.go b/mocks/mock_AssetService.go index bc9e2ec97..de8461fd2 100644 --- a/mocks/mock_AssetService.go +++ b/mocks/mock_AssetService.go @@ -183,6 +183,116 @@ func (_c *AssetService_CreateAsset_Call) RunAndReturn(run func(ctx context.Conte return _c } +// FindOrCreateAsset provides a mock function for the type AssetService +func (_mock *AssetService) FindOrCreateAsset(ctx context.Context, rbac shared.AccessControl, providerID string, orgID uuid.UUID, projectID uuid.UUID, name string, externalEntityID string, currentUser string, description string) (*models.Asset, error) { + ret := _mock.Called(ctx, rbac, providerID, orgID, projectID, name, externalEntityID, currentUser, description) + + if len(ret) == 0 { + panic("no return value specified for FindOrCreateAsset") + } + + var r0 *models.Asset + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.AccessControl, string, uuid.UUID, uuid.UUID, string, string, string, string) (*models.Asset, error)); ok { + return returnFunc(ctx, rbac, providerID, orgID, projectID, name, externalEntityID, currentUser, description) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.AccessControl, string, uuid.UUID, uuid.UUID, string, string, string, string) *models.Asset); ok { + r0 = returnFunc(ctx, rbac, providerID, orgID, projectID, name, externalEntityID, currentUser, description) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Asset) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.AccessControl, string, uuid.UUID, uuid.UUID, string, string, string, string) error); ok { + r1 = returnFunc(ctx, rbac, providerID, orgID, projectID, name, externalEntityID, currentUser, description) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AssetService_FindOrCreateAsset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindOrCreateAsset' +type AssetService_FindOrCreateAsset_Call struct { + *mock.Call +} + +// FindOrCreateAsset is a helper method to define mock.On call +// - ctx context.Context +// - rbac shared.AccessControl +// - providerID string +// - orgID uuid.UUID +// - projectID uuid.UUID +// - name string +// - externalEntityID string +// - currentUser string +// - description string +func (_e *AssetService_Expecter) FindOrCreateAsset(ctx interface{}, rbac interface{}, providerID interface{}, orgID interface{}, projectID interface{}, name interface{}, externalEntityID interface{}, currentUser interface{}, description interface{}) *AssetService_FindOrCreateAsset_Call { + return &AssetService_FindOrCreateAsset_Call{Call: _e.mock.On("FindOrCreateAsset", ctx, rbac, providerID, orgID, projectID, name, externalEntityID, currentUser, description)} +} + +func (_c *AssetService_FindOrCreateAsset_Call) Run(run func(ctx context.Context, rbac shared.AccessControl, providerID string, orgID uuid.UUID, projectID uuid.UUID, name string, externalEntityID string, currentUser string, description string)) *AssetService_FindOrCreateAsset_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.AccessControl + if args[1] != nil { + arg1 = args[1].(shared.AccessControl) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 uuid.UUID + if args[3] != nil { + arg3 = args[3].(uuid.UUID) + } + var arg4 uuid.UUID + if args[4] != nil { + arg4 = args[4].(uuid.UUID) + } + var arg5 string + if args[5] != nil { + arg5 = args[5].(string) + } + var arg6 string + if args[6] != nil { + arg6 = args[6].(string) + } + var arg7 string + if args[7] != nil { + arg7 = args[7].(string) + } + var arg8 string + if args[8] != nil { + arg8 = args[8].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + ) + }) + return _c +} + +func (_c *AssetService_FindOrCreateAsset_Call) Return(asset *models.Asset, err error) *AssetService_FindOrCreateAsset_Call { + _c.Call.Return(asset, err) + return _c +} + +func (_c *AssetService_FindOrCreateAsset_Call) RunAndReturn(run func(ctx context.Context, rbac shared.AccessControl, providerID string, orgID uuid.UUID, projectID uuid.UUID, name string, externalEntityID string, currentUser string, description string) (*models.Asset, error)) *AssetService_FindOrCreateAsset_Call { + _c.Call.Return(run) + return _c +} + // GetCVSSBadgeSVG provides a mock function for the type AssetService func (_mock *AssetService) GetCVSSBadgeSVG(ctx context.Context, latest *models.ArtifactRiskHistory) string { ret := _mock.Called(ctx, latest) diff --git a/mocks/mock_AssetVersionRepository.go b/mocks/mock_AssetVersionRepository.go index 953be143d..44edbfd3e 100644 --- a/mocks/mock_AssetVersionRepository.go +++ b/mocks/mock_AssetVersionRepository.go @@ -758,6 +758,80 @@ func (_c *AssetVersionRepository_GetAssetVersionsByAssetIDWithArtifacts_Call) Ru return _c } +// GetAssetVersionsByAssetIDs provides a mock function for the type AssetVersionRepository +func (_mock *AssetVersionRepository) GetAssetVersionsByAssetIDs(ctx context.Context, tx shared.DB, assetIDs []uuid.UUID) ([]models.AssetVersion, error) { + ret := _mock.Called(ctx, tx, assetIDs) + + if len(ret) == 0 { + panic("no return value specified for GetAssetVersionsByAssetIDs") + } + + var r0 []models.AssetVersion + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) ([]models.AssetVersion, error)); ok { + return returnFunc(ctx, tx, assetIDs) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) []models.AssetVersion); ok { + r0 = returnFunc(ctx, tx, assetIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.AssetVersion) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, assetIDs) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AssetVersionRepository_GetAssetVersionsByAssetIDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAssetVersionsByAssetIDs' +type AssetVersionRepository_GetAssetVersionsByAssetIDs_Call struct { + *mock.Call +} + +// GetAssetVersionsByAssetIDs is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetIDs []uuid.UUID +func (_e *AssetVersionRepository_Expecter) GetAssetVersionsByAssetIDs(ctx interface{}, tx interface{}, assetIDs interface{}) *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call { + return &AssetVersionRepository_GetAssetVersionsByAssetIDs_Call{Call: _e.mock.On("GetAssetVersionsByAssetIDs", ctx, tx, assetIDs)} +} + +func (_c *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call) Run(run func(ctx context.Context, tx shared.DB, assetIDs []uuid.UUID)) *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []uuid.UUID + if args[2] != nil { + arg2 = args[2].([]uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call) Return(assetVersions []models.AssetVersion, err error) *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call { + _c.Call.Return(assetVersions, err) + return _c +} + +func (_c *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetIDs []uuid.UUID) ([]models.AssetVersion, error)) *AssetVersionRepository_GetAssetVersionsByAssetIDs_Call { + _c.Call.Return(run) + return _c +} + // GetDB provides a mock function for the type AssetVersionRepository func (_mock *AssetVersionRepository) GetDB(ctx context.Context, tx shared.DB) shared.DB { ret := _mock.Called(ctx, tx) diff --git a/mocks/mock_ExternalPropertyFileReference.go b/mocks/mock_ExternalPropertyFileReference.go deleted file mode 100644 index a2163049d..000000000 --- a/mocks/mock_ExternalPropertyFileReference.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" -) - -// NewExternalPropertyFileReference creates a new instance of ExternalPropertyFileReference. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewExternalPropertyFileReference(t interface { - mock.TestingT - Cleanup(func()) -}) *ExternalPropertyFileReference { - mock := &ExternalPropertyFileReference{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// ExternalPropertyFileReference is an autogenerated mock type for the ExternalPropertyFileReference type -type ExternalPropertyFileReference struct { - mock.Mock -} - -type ExternalPropertyFileReference_Expecter struct { - mock *mock.Mock -} - -func (_m *ExternalPropertyFileReference) EXPECT() *ExternalPropertyFileReference_Expecter { - return &ExternalPropertyFileReference_Expecter{mock: &_m.Mock} -} diff --git a/mocks/mock_ProjectRepository.go b/mocks/mock_ProjectRepository.go index 73ccd679f..ae52ca988 100644 --- a/mocks/mock_ProjectRepository.go +++ b/mocks/mock_ProjectRepository.go @@ -173,6 +173,105 @@ func (_c *ProjectRepository_All_Call) RunAndReturn(run func(ctx context.Context, return _c } +// CleanupDynamicProject provides a mock function for the type ProjectRepository +func (_mock *ProjectRepository) CleanupDynamicProject(ctx context.Context, tx shared.DB, organizationID uuid.UUID, parentProjectID uuid.UUID, providerID string, projectExternalEntityID string, assetExternalEntityID string, assetVersionName string, artifactName string) error { + ret := _mock.Called(ctx, tx, organizationID, parentProjectID, providerID, projectExternalEntityID, assetExternalEntityID, assetVersionName, artifactName) + + if len(ret) == 0 { + panic("no return value specified for CleanupDynamicProject") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, uuid.UUID, string, string, string, string, string) error); ok { + r0 = returnFunc(ctx, tx, organizationID, parentProjectID, providerID, projectExternalEntityID, assetExternalEntityID, assetVersionName, artifactName) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ProjectRepository_CleanupDynamicProject_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanupDynamicProject' +type ProjectRepository_CleanupDynamicProject_Call struct { + *mock.Call +} + +// CleanupDynamicProject is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - organizationID uuid.UUID +// - parentProjectID uuid.UUID +// - providerID string +// - projectExternalEntityID string +// - assetExternalEntityID string +// - assetVersionName string +// - artifactName string +func (_e *ProjectRepository_Expecter) CleanupDynamicProject(ctx interface{}, tx interface{}, organizationID interface{}, parentProjectID interface{}, providerID interface{}, projectExternalEntityID interface{}, assetExternalEntityID interface{}, assetVersionName interface{}, artifactName interface{}) *ProjectRepository_CleanupDynamicProject_Call { + return &ProjectRepository_CleanupDynamicProject_Call{Call: _e.mock.On("CleanupDynamicProject", ctx, tx, organizationID, parentProjectID, providerID, projectExternalEntityID, assetExternalEntityID, assetVersionName, artifactName)} +} + +func (_c *ProjectRepository_CleanupDynamicProject_Call) Run(run func(ctx context.Context, tx shared.DB, organizationID uuid.UUID, parentProjectID uuid.UUID, providerID string, projectExternalEntityID string, assetExternalEntityID string, assetVersionName string, artifactName string)) *ProjectRepository_CleanupDynamicProject_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 uuid.UUID + if args[3] != nil { + arg3 = args[3].(uuid.UUID) + } + var arg4 string + if args[4] != nil { + arg4 = args[4].(string) + } + var arg5 string + if args[5] != nil { + arg5 = args[5].(string) + } + var arg6 string + if args[6] != nil { + arg6 = args[6].(string) + } + var arg7 string + if args[7] != nil { + arg7 = args[7].(string) + } + var arg8 string + if args[8] != nil { + arg8 = args[8].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + ) + }) + return _c +} + +func (_c *ProjectRepository_CleanupDynamicProject_Call) Return(err error) *ProjectRepository_CleanupDynamicProject_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ProjectRepository_CleanupDynamicProject_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, organizationID uuid.UUID, parentProjectID uuid.UUID, providerID string, projectExternalEntityID string, assetExternalEntityID string, assetVersionName string, artifactName string) error) *ProjectRepository_CleanupDynamicProject_Call { + _c.Call.Return(run) + return _c +} + // Create provides a mock function for the type ProjectRepository func (_mock *ProjectRepository) Create(ctx context.Context, tx shared.DB, project *models.Project) error { ret := _mock.Called(ctx, tx, project) @@ -648,6 +747,86 @@ func (_c *ProjectRepository_GetByProjectIDs_Call) RunAndReturn(run func(ctx cont return _c } +// GetChildProjectsForParents provides a mock function for the type ProjectRepository +func (_mock *ProjectRepository) GetChildProjectsForParents(ctx context.Context, tx shared.DB, parentIDs []uuid.UUID, providerID string) ([]models.Project, error) { + ret := _mock.Called(ctx, tx, parentIDs, providerID) + + if len(ret) == 0 { + panic("no return value specified for GetChildProjectsForParents") + } + + var r0 []models.Project + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID, string) ([]models.Project, error)); ok { + return returnFunc(ctx, tx, parentIDs, providerID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID, string) []models.Project); ok { + r0 = returnFunc(ctx, tx, parentIDs, providerID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Project) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, parentIDs, providerID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProjectRepository_GetChildProjectsForParents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetChildProjectsForParents' +type ProjectRepository_GetChildProjectsForParents_Call struct { + *mock.Call +} + +// GetChildProjectsForParents is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - parentIDs []uuid.UUID +// - providerID string +func (_e *ProjectRepository_Expecter) GetChildProjectsForParents(ctx interface{}, tx interface{}, parentIDs interface{}, providerID interface{}) *ProjectRepository_GetChildProjectsForParents_Call { + return &ProjectRepository_GetChildProjectsForParents_Call{Call: _e.mock.On("GetChildProjectsForParents", ctx, tx, parentIDs, providerID)} +} + +func (_c *ProjectRepository_GetChildProjectsForParents_Call) Run(run func(ctx context.Context, tx shared.DB, parentIDs []uuid.UUID, providerID string)) *ProjectRepository_GetChildProjectsForParents_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []uuid.UUID + if args[2] != nil { + arg2 = args[2].([]uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ProjectRepository_GetChildProjectsForParents_Call) Return(projects []models.Project, err error) *ProjectRepository_GetChildProjectsForParents_Call { + _c.Call.Return(projects, err) + return _c +} + +func (_c *ProjectRepository_GetChildProjectsForParents_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, parentIDs []uuid.UUID, providerID string) ([]models.Project, error)) *ProjectRepository_GetChildProjectsForParents_Call { + _c.Call.Return(run) + return _c +} + // GetDirectChildProjects provides a mock function for the type ProjectRepository func (_mock *ProjectRepository) GetDirectChildProjects(ctx context.Context, tx shared.DB, projectID uuid.UUID) ([]models.Project, error) { ret := _mock.Called(ctx, tx, projectID) @@ -722,6 +901,170 @@ func (_c *ProjectRepository_GetDirectChildProjects_Call) RunAndReturn(run func(c return _c } +// GetDirectChildProjectsWithProviderID provides a mock function for the type ProjectRepository +func (_mock *ProjectRepository) GetDirectChildProjectsWithProviderID(ctx context.Context, tx shared.DB, parentID uuid.UUID, providerID string) ([]models.Project, error) { + ret := _mock.Called(ctx, tx, parentID, providerID) + + if len(ret) == 0 { + panic("no return value specified for GetDirectChildProjectsWithProviderID") + } + + var r0 []models.Project + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) ([]models.Project, error)); ok { + return returnFunc(ctx, tx, parentID, providerID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) []models.Project); ok { + r0 = returnFunc(ctx, tx, parentID, providerID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Project) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, parentID, providerID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProjectRepository_GetDirectChildProjectsWithProviderID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDirectChildProjectsWithProviderID' +type ProjectRepository_GetDirectChildProjectsWithProviderID_Call struct { + *mock.Call +} + +// GetDirectChildProjectsWithProviderID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - parentID uuid.UUID +// - providerID string +func (_e *ProjectRepository_Expecter) GetDirectChildProjectsWithProviderID(ctx interface{}, tx interface{}, parentID interface{}, providerID interface{}) *ProjectRepository_GetDirectChildProjectsWithProviderID_Call { + return &ProjectRepository_GetDirectChildProjectsWithProviderID_Call{Call: _e.mock.On("GetDirectChildProjectsWithProviderID", ctx, tx, parentID, providerID)} +} + +func (_c *ProjectRepository_GetDirectChildProjectsWithProviderID_Call) Run(run func(ctx context.Context, tx shared.DB, parentID uuid.UUID, providerID string)) *ProjectRepository_GetDirectChildProjectsWithProviderID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ProjectRepository_GetDirectChildProjectsWithProviderID_Call) Return(projects []models.Project, err error) *ProjectRepository_GetDirectChildProjectsWithProviderID_Call { + _c.Call.Return(projects, err) + return _c +} + +func (_c *ProjectRepository_GetDirectChildProjectsWithProviderID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, parentID uuid.UUID, providerID string) ([]models.Project, error)) *ProjectRepository_GetDirectChildProjectsWithProviderID_Call { + _c.Call.Return(run) + return _c +} + +// GetDirectChildProjectsWithProviderIDAndExternalEntityID provides a mock function for the type ProjectRepository +func (_mock *ProjectRepository) GetDirectChildProjectsWithProviderIDAndExternalEntityID(ctx context.Context, tx shared.DB, parentID uuid.UUID, providerID string, externalEntityID string) (models.Project, error) { + ret := _mock.Called(ctx, tx, parentID, providerID, externalEntityID) + + if len(ret) == 0 { + panic("no return value specified for GetDirectChildProjectsWithProviderIDAndExternalEntityID") + } + + var r0 models.Project + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string, string) (models.Project, error)); ok { + return returnFunc(ctx, tx, parentID, providerID, externalEntityID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string, string) models.Project); ok { + r0 = returnFunc(ctx, tx, parentID, providerID, externalEntityID) + } else { + r0 = ret.Get(0).(models.Project) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string, string) error); ok { + r1 = returnFunc(ctx, tx, parentID, providerID, externalEntityID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDirectChildProjectsWithProviderIDAndExternalEntityID' +type ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call struct { + *mock.Call +} + +// GetDirectChildProjectsWithProviderIDAndExternalEntityID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - parentID uuid.UUID +// - providerID string +// - externalEntityID string +func (_e *ProjectRepository_Expecter) GetDirectChildProjectsWithProviderIDAndExternalEntityID(ctx interface{}, tx interface{}, parentID interface{}, providerID interface{}, externalEntityID interface{}) *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call { + return &ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call{Call: _e.mock.On("GetDirectChildProjectsWithProviderIDAndExternalEntityID", ctx, tx, parentID, providerID, externalEntityID)} +} + +func (_c *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call) Run(run func(ctx context.Context, tx shared.DB, parentID uuid.UUID, providerID string, externalEntityID string)) *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + var arg4 string + if args[4] != nil { + arg4 = args[4].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call) Return(project models.Project, err error) *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call { + _c.Call.Return(project, err) + return _c +} + +func (_c *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, parentID uuid.UUID, providerID string, externalEntityID string) (models.Project, error)) *ProjectRepository_GetDirectChildProjectsWithProviderIDAndExternalEntityID_Call { + _c.Call.Return(run) + return _c +} + // GetProjectByAssetID provides a mock function for the type ProjectRepository func (_mock *ProjectRepository) GetProjectByAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID) (models.Project, error) { ret := _mock.Called(ctx, tx, assetID) diff --git a/mocks/mock_ProjectService.go b/mocks/mock_ProjectService.go index 85256a5d0..35d010a8a 100644 --- a/mocks/mock_ProjectService.go +++ b/mocks/mock_ProjectService.go @@ -161,6 +161,104 @@ func (_c *ProjectService_CreateProject_Call) RunAndReturn(run func(ctx shared.Co return _c } +// FindOrCreateProject provides a mock function for the type ProjectService +func (_mock *ProjectService) FindOrCreateProject(ctx shared.Context, providerID string, orgID uuid.UUID, name string, externalEntityID string, parentID uuid.UUID, description string) (*models.Project, error) { + ret := _mock.Called(ctx, providerID, orgID, name, externalEntityID, parentID, description) + + if len(ret) == 0 { + panic("no return value specified for FindOrCreateProject") + } + + var r0 *models.Project + var r1 error + if returnFunc, ok := ret.Get(0).(func(shared.Context, string, uuid.UUID, string, string, uuid.UUID, string) (*models.Project, error)); ok { + return returnFunc(ctx, providerID, orgID, name, externalEntityID, parentID, description) + } + if returnFunc, ok := ret.Get(0).(func(shared.Context, string, uuid.UUID, string, string, uuid.UUID, string) *models.Project); ok { + r0 = returnFunc(ctx, providerID, orgID, name, externalEntityID, parentID, description) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Project) + } + } + if returnFunc, ok := ret.Get(1).(func(shared.Context, string, uuid.UUID, string, string, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, providerID, orgID, name, externalEntityID, parentID, description) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ProjectService_FindOrCreateProject_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindOrCreateProject' +type ProjectService_FindOrCreateProject_Call struct { + *mock.Call +} + +// FindOrCreateProject is a helper method to define mock.On call +// - ctx shared.Context +// - providerID string +// - orgID uuid.UUID +// - name string +// - externalEntityID string +// - parentID uuid.UUID +// - description string +func (_e *ProjectService_Expecter) FindOrCreateProject(ctx interface{}, providerID interface{}, orgID interface{}, name interface{}, externalEntityID interface{}, parentID interface{}, description interface{}) *ProjectService_FindOrCreateProject_Call { + return &ProjectService_FindOrCreateProject_Call{Call: _e.mock.On("FindOrCreateProject", ctx, providerID, orgID, name, externalEntityID, parentID, description)} +} + +func (_c *ProjectService_FindOrCreateProject_Call) Run(run func(ctx shared.Context, providerID string, orgID uuid.UUID, name string, externalEntityID string, parentID uuid.UUID, description string)) *ProjectService_FindOrCreateProject_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 shared.Context + if args[0] != nil { + arg0 = args[0].(shared.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + var arg4 string + if args[4] != nil { + arg4 = args[4].(string) + } + var arg5 uuid.UUID + if args[5] != nil { + arg5 = args[5].(uuid.UUID) + } + var arg6 string + if args[6] != nil { + arg6 = args[6].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + ) + }) + return _c +} + +func (_c *ProjectService_FindOrCreateProject_Call) Return(project *models.Project, err error) *ProjectService_FindOrCreateProject_Call { + _c.Call.Return(project, err) + return _c +} + +func (_c *ProjectService_FindOrCreateProject_Call) RunAndReturn(run func(ctx shared.Context, providerID string, orgID uuid.UUID, name string, externalEntityID string, parentID uuid.UUID, description string) (*models.Project, error)) *ProjectService_FindOrCreateProject_Call { + _c.Call.Return(run) + return _c +} + // GetDirectChildProjects provides a mock function for the type ProjectService func (_mock *ProjectService) GetDirectChildProjects(ctx context.Context, projectID uuid.UUID) ([]models.Project, error) { ret := _mock.Called(ctx, projectID) diff --git a/mocks/mock_ReleaseRepository.go b/mocks/mock_ReleaseRepository.go index c503b7ba2..10687553d 100644 --- a/mocks/mock_ReleaseRepository.go +++ b/mocks/mock_ReleaseRepository.go @@ -654,6 +654,84 @@ func (_c *ReleaseRepository_DeleteReleaseItem_Call) RunAndReturn(run func(ctx co return _c } +// FindOrCreate provides a mock function for the type ReleaseRepository +func (_mock *ReleaseRepository) FindOrCreate(ctx context.Context, tx shared.DB, projectID uuid.UUID, name string) (models.Release, error) { + ret := _mock.Called(ctx, tx, projectID, name) + + if len(ret) == 0 { + panic("no return value specified for FindOrCreate") + } + + var r0 models.Release + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) (models.Release, error)); ok { + return returnFunc(ctx, tx, projectID, name) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) models.Release); ok { + r0 = returnFunc(ctx, tx, projectID, name) + } else { + r0 = ret.Get(0).(models.Release) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, projectID, name) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ReleaseRepository_FindOrCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindOrCreate' +type ReleaseRepository_FindOrCreate_Call struct { + *mock.Call +} + +// FindOrCreate is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - projectID uuid.UUID +// - name string +func (_e *ReleaseRepository_Expecter) FindOrCreate(ctx interface{}, tx interface{}, projectID interface{}, name interface{}) *ReleaseRepository_FindOrCreate_Call { + return &ReleaseRepository_FindOrCreate_Call{Call: _e.mock.On("FindOrCreate", ctx, tx, projectID, name)} +} + +func (_c *ReleaseRepository_FindOrCreate_Call) Run(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID, name string)) *ReleaseRepository_FindOrCreate_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ReleaseRepository_FindOrCreate_Call) Return(release models.Release, err error) *ReleaseRepository_FindOrCreate_Call { + _c.Call.Return(release, err) + return _c +} + +func (_c *ReleaseRepository_FindOrCreate_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID, name string) (models.Release, error)) *ReleaseRepository_FindOrCreate_Call { + _c.Call.Return(run) + return _c +} + // GetByProjectID provides a mock function for the type ReleaseRepository func (_mock *ReleaseRepository) GetByProjectID(ctx context.Context, tx shared.DB, projectID uuid.UUID) ([]models.Release, error) { ret := _mock.Called(ctx, tx, projectID) diff --git a/mocks/mock_ReleaseService.go b/mocks/mock_ReleaseService.go index 237caf1cb..99abcd3f8 100644 --- a/mocks/mock_ReleaseService.go +++ b/mocks/mock_ReleaseService.go @@ -211,6 +211,78 @@ func (_c *ReleaseService_Delete_Call) RunAndReturn(run func(ctx context.Context, return _c } +// FindOrCreate provides a mock function for the type ReleaseService +func (_mock *ReleaseService) FindOrCreate(ctx context.Context, projectID uuid.UUID, name string) (models.Release, error) { + ret := _mock.Called(ctx, projectID, name) + + if len(ret) == 0 { + panic("no return value specified for FindOrCreate") + } + + var r0 models.Release + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, string) (models.Release, error)); ok { + return returnFunc(ctx, projectID, name) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, string) models.Release); ok { + r0 = returnFunc(ctx, projectID, name) + } else { + r0 = ret.Get(0).(models.Release) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, projectID, name) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ReleaseService_FindOrCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindOrCreate' +type ReleaseService_FindOrCreate_Call struct { + *mock.Call +} + +// FindOrCreate is a helper method to define mock.On call +// - ctx context.Context +// - projectID uuid.UUID +// - name string +func (_e *ReleaseService_Expecter) FindOrCreate(ctx interface{}, projectID interface{}, name interface{}) *ReleaseService_FindOrCreate_Call { + return &ReleaseService_FindOrCreate_Call{Call: _e.mock.On("FindOrCreate", ctx, projectID, name)} +} + +func (_c *ReleaseService_FindOrCreate_Call) Run(run func(ctx context.Context, projectID uuid.UUID, name string)) *ReleaseService_FindOrCreate_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uuid.UUID + if args[1] != nil { + arg1 = args[1].(uuid.UUID) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ReleaseService_FindOrCreate_Call) Return(release models.Release, err error) *ReleaseService_FindOrCreate_Call { + _c.Call.Return(release, err) + return _c +} + +func (_c *ReleaseService_FindOrCreate_Call) RunAndReturn(run func(ctx context.Context, projectID uuid.UUID, name string) (models.Release, error)) *ReleaseService_FindOrCreate_Call { + _c.Call.Return(run) + return _c +} + // ListByProject provides a mock function for the type ReleaseService func (_mock *ReleaseService) ListByProject(ctx context.Context, projectID uuid.UUID) ([]models.Release, error) { ret := _mock.Called(ctx, projectID) diff --git a/mocks/mock_ReportingDescriptorReference.go b/mocks/mock_ReportingDescriptorReference.go deleted file mode 100644 index e38e0efb6..000000000 --- a/mocks/mock_ReportingDescriptorReference.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" -) - -// NewReportingDescriptorReference creates a new instance of ReportingDescriptorReference. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewReportingDescriptorReference(t interface { - mock.TestingT - Cleanup(func()) -}) *ReportingDescriptorReference { - mock := &ReportingDescriptorReference{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// ReportingDescriptorReference is an autogenerated mock type for the ReportingDescriptorReference type -type ReportingDescriptorReference struct { - mock.Mock -} - -type ReportingDescriptorReference_Expecter struct { - mock *mock.Mock -} - -func (_m *ReportingDescriptorReference) EXPECT() *ReportingDescriptorReference_Expecter { - return &ReportingDescriptorReference_Expecter{mock: &_m.Mock} -} diff --git a/mocks/mock_VulnDBImportService.go b/mocks/mock_VulnDBImportService.go deleted file mode 100644 index fbc5bbcf9..000000000 --- a/mocks/mock_VulnDBImportService.go +++ /dev/null @@ -1,260 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - "context" - - mock "github.com/stretchr/testify/mock" -) - -// NewVulnDBImportService creates a new instance of VulnDBImportService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewVulnDBImportService(t interface { - mock.TestingT - Cleanup(func()) -}) *VulnDBImportService { - mock := &VulnDBImportService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// VulnDBImportService is an autogenerated mock type for the VulnDBImportService type -type VulnDBImportService struct { - mock.Mock -} - -type VulnDBImportService_Expecter struct { - mock *mock.Mock -} - -func (_m *VulnDBImportService) EXPECT() *VulnDBImportService_Expecter { - return &VulnDBImportService_Expecter{mock: &_m.Mock} -} - -// CleanupOrphanedTables provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) CleanupOrphanedTables(ctx context.Context) error { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for CleanupOrphanedTables") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = returnFunc(ctx) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_CleanupOrphanedTables_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanupOrphanedTables' -type VulnDBImportService_CleanupOrphanedTables_Call struct { - *mock.Call -} - -// CleanupOrphanedTables is a helper method to define mock.On call -// - ctx context.Context -func (_e *VulnDBImportService_Expecter) CleanupOrphanedTables(ctx interface{}) *VulnDBImportService_CleanupOrphanedTables_Call { - return &VulnDBImportService_CleanupOrphanedTables_Call{Call: _e.mock.On("CleanupOrphanedTables", ctx)} -} - -func (_c *VulnDBImportService_CleanupOrphanedTables_Call) Run(run func(ctx context.Context)) *VulnDBImportService_CleanupOrphanedTables_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *VulnDBImportService_CleanupOrphanedTables_Call) Return(err error) *VulnDBImportService_CleanupOrphanedTables_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_CleanupOrphanedTables_Call) RunAndReturn(run func(ctx context.Context) error) *VulnDBImportService_CleanupOrphanedTables_Call { - _c.Call.Return(run) - return _c -} - -// CreateTablesWithSuffix provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) CreateTablesWithSuffix(ctx context.Context, suffix string) error { - ret := _mock.Called(ctx, suffix) - - if len(ret) == 0 { - panic("no return value specified for CreateTablesWithSuffix") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = returnFunc(ctx, suffix) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_CreateTablesWithSuffix_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTablesWithSuffix' -type VulnDBImportService_CreateTablesWithSuffix_Call struct { - *mock.Call -} - -// CreateTablesWithSuffix is a helper method to define mock.On call -// - ctx context.Context -// - suffix string -func (_e *VulnDBImportService_Expecter) CreateTablesWithSuffix(ctx interface{}, suffix interface{}) *VulnDBImportService_CreateTablesWithSuffix_Call { - return &VulnDBImportService_CreateTablesWithSuffix_Call{Call: _e.mock.On("CreateTablesWithSuffix", ctx, suffix)} -} - -func (_c *VulnDBImportService_CreateTablesWithSuffix_Call) Run(run func(ctx context.Context, suffix string)) *VulnDBImportService_CreateTablesWithSuffix_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *VulnDBImportService_CreateTablesWithSuffix_Call) Return(err error) *VulnDBImportService_CreateTablesWithSuffix_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_CreateTablesWithSuffix_Call) RunAndReturn(run func(ctx context.Context, suffix string) error) *VulnDBImportService_CreateTablesWithSuffix_Call { - _c.Call.Return(run) - return _c -} - -// ExportDiffs provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) ExportDiffs(ctx context.Context, extraTableNameSuffix string) error { - ret := _mock.Called(ctx, extraTableNameSuffix) - - if len(ret) == 0 { - panic("no return value specified for ExportDiffs") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = returnFunc(ctx, extraTableNameSuffix) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_ExportDiffs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExportDiffs' -type VulnDBImportService_ExportDiffs_Call struct { - *mock.Call -} - -// ExportDiffs is a helper method to define mock.On call -// - ctx context.Context -// - extraTableNameSuffix string -func (_e *VulnDBImportService_Expecter) ExportDiffs(ctx interface{}, extraTableNameSuffix interface{}) *VulnDBImportService_ExportDiffs_Call { - return &VulnDBImportService_ExportDiffs_Call{Call: _e.mock.On("ExportDiffs", ctx, extraTableNameSuffix)} -} - -func (_c *VulnDBImportService_ExportDiffs_Call) Run(run func(ctx context.Context, extraTableNameSuffix string)) *VulnDBImportService_ExportDiffs_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *VulnDBImportService_ExportDiffs_Call) Return(err error) *VulnDBImportService_ExportDiffs_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_ExportDiffs_Call) RunAndReturn(run func(ctx context.Context, extraTableNameSuffix string) error) *VulnDBImportService_ExportDiffs_Call { - _c.Call.Return(run) - return _c -} - -// ImportFromDiff provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) ImportFromDiff(ctx context.Context, extraTableNameSuffix *string) error { - ret := _mock.Called(ctx, extraTableNameSuffix) - - if len(ret) == 0 { - panic("no return value specified for ImportFromDiff") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, *string) error); ok { - r0 = returnFunc(ctx, extraTableNameSuffix) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_ImportFromDiff_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ImportFromDiff' -type VulnDBImportService_ImportFromDiff_Call struct { - *mock.Call -} - -// ImportFromDiff is a helper method to define mock.On call -// - ctx context.Context -// - extraTableNameSuffix *string -func (_e *VulnDBImportService_Expecter) ImportFromDiff(ctx interface{}, extraTableNameSuffix interface{}) *VulnDBImportService_ImportFromDiff_Call { - return &VulnDBImportService_ImportFromDiff_Call{Call: _e.mock.On("ImportFromDiff", ctx, extraTableNameSuffix)} -} - -func (_c *VulnDBImportService_ImportFromDiff_Call) Run(run func(ctx context.Context, extraTableNameSuffix *string)) *VulnDBImportService_ImportFromDiff_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 *string - if args[1] != nil { - arg1 = args[1].(*string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *VulnDBImportService_ImportFromDiff_Call) Return(err error) *VulnDBImportService_ImportFromDiff_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_ImportFromDiff_Call) RunAndReturn(run func(ctx context.Context, extraTableNameSuffix *string) error) *VulnDBImportService_ImportFromDiff_Call { - _c.Call.Return(run) - return _c -} diff --git a/router/project_router.go b/router/project_router.go index d3f6ba6b8..e34e8ab9a 100644 --- a/router/project_router.go +++ b/router/project_router.go @@ -18,6 +18,7 @@ package router import ( "github.com/l3montree-dev/devguard/controllers" "github.com/l3montree-dev/devguard/controllers/dependencyfirewall" + "github.com/l3montree-dev/devguard/integrations/gitlabint" "github.com/l3montree-dev/devguard/middlewares" "github.com/l3montree-dev/devguard/shared" "github.com/labstack/echo/v4" @@ -39,6 +40,7 @@ func NewProjectRouter( webhookIntegration *controllers.WebhookController, projectRepository shared.ProjectRepository, componentController *controllers.ComponentController, + gitlabIntegrations map[string]*gitlabint.GitlabOauth2Config, ) ProjectRouter { /** Project scoped router @@ -68,6 +70,9 @@ func NewProjectRouter( projectRouter.GET("/releases/", releaseController.List) projectRouter.GET("/components/", componentController.SearchComponentOccurrences, projectScopedRBAC(shared.ObjectAsset, shared.ActionCreate)) + projectRouter.POST("/dynamic-project/dn/:providerID", projectController.HandleDynamicProject, middlewares.ProviderIDMiddleware(gitlabIntegrations), middlewares.NeededScope([]string{"manage"})) + projectRouter.GET("/dynamic-project/dn/:providerID", projectController.ListDynamicProjects, middlewares.ProviderIDMiddleware(gitlabIntegrations), middlewares.NeededScope([]string{"manage"})) + projectRouter.POST("/assets/", assetController.Create, middlewares.NeededScope([]string{"manage"}), projectScopedRBAC(shared.ObjectAsset, shared.ActionCreate)) projectUpdateAccessControlRequired := projectRouter.Group("", middlewares.NeededScope([]string{"manage"}), projectScopedRBAC(shared.ObjectProject, shared.ActionUpdate)) diff --git a/router/router_test.go b/router/router_test.go index f849b5c6a..f7cba3552 100644 --- a/router/router_test.go +++ b/router/router_test.go @@ -243,6 +243,7 @@ func buildSecurityTestServer(t *testing.T, ac *mocks.AccessControl) *echo.Echo { new(controllers.WebhookController), projectRepo, new(controllers.ComponentController), + nil, ) assetRouter := NewAssetRouter( diff --git a/services/asset_service.go b/services/asset_service.go index 3b91ee567..4173dbf6c 100644 --- a/services/asset_service.go +++ b/services/asset_service.go @@ -20,6 +20,7 @@ import ( "log/slog" "github.com/google/uuid" + "github.com/gosimple/slug" "github.com/l3montree-dev/devguard/database/models" "github.com/l3montree-dev/devguard/dtos" "github.com/l3montree-dev/devguard/shared" @@ -45,6 +46,31 @@ func NewAssetService(assetRepository shared.AssetRepository, dependencyVulnRepos } } +func (s *assetService) FindOrCreateAsset(ctx context.Context, rbac shared.AccessControl, providerID string, orgID uuid.UUID, projectID uuid.UUID, name string, externalEntityID string, currentUser string, description string) (*models.Asset, error) { + + asset := &models.Asset{ + Name: name, + Slug: slug.Make(name), + ProjectID: projectID, + ExternalEntityID: &externalEntityID, + ExternalEntityProviderID: &providerID, + Description: description, + } + + newAssets, _, err := s.assetRepository.UpsertSplit(ctx, nil, providerID, []*models.Asset{asset}) + if err != nil { + return nil, err + } + + if len(newAssets) > 0 { + if err := s.BootstrapAsset(ctx, rbac, asset); err != nil { + return nil, err + } + } + + return asset, nil +} + func (s *assetService) CreateAsset(ctx context.Context, rbac shared.AccessControl, currentUser string, asset models.Asset) (*models.Asset, error) { newAsset := asset if newAsset.Name == "" || newAsset.Slug == "" { diff --git a/services/project_service.go b/services/project_service.go index be2575273..fa2f4a268 100644 --- a/services/project_service.go +++ b/services/project_service.go @@ -5,6 +5,7 @@ import ( "log/slog" "github.com/google/uuid" + "github.com/gosimple/slug" "github.com/l3montree-dev/devguard/database" "github.com/l3montree-dev/devguard/database/models" "github.com/l3montree-dev/devguard/dtos" @@ -36,6 +37,33 @@ func (s *projectService) ReadBySlug(ctx shared.Context, organizationID uuid.UUID return project, nil } +func (s *projectService) FindOrCreateProject(ctx shared.Context, providerID string, orgID uuid.UUID, name string, externalEntityID string, parentID uuid.UUID, description string) (*models.Project, error) { + + project := &models.Project{ + Name: name, + Slug: slug.Make(name), + OrganizationID: orgID, + ParentID: &parentID, + Type: models.ProjectTypeDynamic, + ExternalEntityID: &externalEntityID, + ExternalEntityProviderID: &providerID, + Description: description, + } + newProjects, _, err := s.projectRepository.UpsertSplit(ctx.Request().Context(), nil, providerID, []*models.Project{project}) + if err != nil { + return nil, err + } + + if len(newProjects) > 0 { + domainRBAC := shared.GetRBAC(ctx) + if err := s.BootstrapProject(ctx.Request().Context(), domainRBAC, project); err != nil { + return nil, err + } + } + + return project, nil +} + func (s *projectService) CreateProject(ctx shared.Context, project *models.Project) error { newProject := project diff --git a/services/release_service.go b/services/release_service.go index ebb473be3..ced0aef66 100644 --- a/services/release_service.go +++ b/services/release_service.go @@ -36,6 +36,10 @@ func (s *releaseService) ReadRecursive(ctx context.Context, id uuid.UUID) (model return s.releaseRepository.ReadRecursive(ctx, nil, id) } +func (s *releaseService) FindOrCreate(ctx context.Context, projectID uuid.UUID, name string) (models.Release, error) { + return s.releaseRepository.FindOrCreate(ctx, nil, projectID, name) +} + func (s *releaseService) Create(ctx context.Context, r *models.Release) error { return s.releaseRepository.Create(ctx, nil, r) } diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index 6c1f09358..831e61896 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -66,6 +66,7 @@ type ReleaseService interface { AddItem(ctx context.Context, item *models.ReleaseItem) error RemoveItem(ctx context.Context, id uuid.UUID) error ListCandidates(ctx context.Context, projectID uuid.UUID, releaseID *uuid.UUID) ([]models.Artifact, []models.Release, error) + FindOrCreate(ctx context.Context, projectID uuid.UUID, name string) (models.Release, error) } type PersonalAccessTokenService interface { @@ -98,6 +99,8 @@ type ProjectRepository interface { Activate(ctx context.Context, tx DB, projectID uuid.UUID) error RecursivelyGetChildProjects(ctx context.Context, tx DB, projectID uuid.UUID) ([]models.Project, error) GetDirectChildProjects(ctx context.Context, tx DB, projectID uuid.UUID) ([]models.Project, error) + GetDirectChildProjectsWithProviderID(ctx context.Context, tx DB, parentID uuid.UUID, providerID string) ([]models.Project, error) + GetChildProjectsForParents(ctx context.Context, tx DB, parentIDs []uuid.UUID, providerID string) ([]models.Project, error) GetByOrgID(ctx context.Context, tx DB, organizationID uuid.UUID) ([]models.Project, error) GetProjectByAssetID(ctx context.Context, tx DB, assetID uuid.UUID) (models.Project, error) GetByProjectIDs(ctx context.Context, tx DB, projectIDs []uuid.UUID) ([]models.Project, error) @@ -111,6 +114,8 @@ type ProjectRepository interface { ListSubProjectsAndAssets(ctx context.Context, tx DB, allowedAssetIDs []string, allowedProjectIDs []uuid.UUID, parentID *uuid.UUID, orgID uuid.UUID, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[dtos.ProjectAssetDTO], error) SearchProjectsWithSubProjectsAndAssetsPaged(ctx context.Context, tx DB, allowedAssetIDs []string, allowedProjectIDs []string, parentID *uuid.UUID, orgID uuid.UUID, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[dtos.ProjectDTO], error) All(ctx context.Context, tx DB) ([]models.Project, error) + GetDirectChildProjectsWithProviderIDAndExternalEntityID(ctx context.Context, tx DB, parentID uuid.UUID, providerID string, externalEntityID string) (models.Project, error) + CleanupDynamicProject(ctx context.Context, tx DB, organizationID uuid.UUID, parentProjectID uuid.UUID, providerID string, projectExternalEntityID string, assetExternalEntityID string, assetVersionName string, artifactName string) error } type Verifier interface { @@ -152,6 +157,8 @@ type AssetRepository interface { utils.Repository[uuid.UUID, models.Asset, DB] GetAllowedAssetsByProjectID(ctx context.Context, tx DB, allowedAssetIDs []string, projectID uuid.UUID) ([]models.Asset, error) GetByProjectID(ctx context.Context, tx DB, projectID uuid.UUID) ([]models.Asset, error) + GetByProjectIDs(ctx context.Context, tx DB, projectIDs []uuid.UUID) ([]models.Asset, error) + GetByProjectIDsWithProviderID(ctx context.Context, tx DB, projectIDs []uuid.UUID, providerID string) ([]models.Asset, error) GetByOrgID(ctx context.Context, tx DB, organizationID uuid.UUID) ([]models.Asset, error) FindByName(ctx context.Context, tx DB, name string) (models.Asset, error) FindAssetByExternalProviderID(ctx context.Context, tx DB, externalEntityProviderID string, externalEntityID string) (*models.Asset, error) @@ -163,6 +170,8 @@ type AssetRepository interface { GetAllAssetsFromDB(ctx context.Context, tx DB) ([]models.Asset, error) ReadWithAssetVersions(ctx context.Context, tx DB, assetID uuid.UUID) (models.Asset, error) GetAssetsWithVulnSharingEnabled(ctx context.Context, tx DB, orgID uuid.UUID) ([]models.Asset, error) + Upsert(ctx context.Context, tx DB, assets *[]*models.Asset, conflictingColumns []clause.Column, updateOnly []string) error + UpsertSplit(ctx context.Context, tx DB, externalProviderID string, assets []*models.Asset) ([]*models.Asset, []*models.Asset, error) } type AttestationRepository interface { @@ -179,6 +188,8 @@ type ArtifactRepository interface { GetAllArtifactAffectedByDependencyVuln(ctx context.Context, tx DB, vulnID uuid.UUID) ([]models.Artifact, error) GetByAssetVersions(ctx context.Context, tx DB, assetID uuid.UUID, assetVersionNames []string) ([]models.Artifact, error) CleanupOrphanedRecords(ctx context.Context) error + GetByAssetID(ctx context.Context, tx DB, assetID uuid.UUID) ([]models.Artifact, error) + GetByAssetIDs(ctx context.Context, tx DB, assetIDs []uuid.UUID) ([]models.Artifact, error) } type ReleaseRepository interface { @@ -190,6 +201,7 @@ type ReleaseRepository interface { CreateReleaseItem(ctx context.Context, tx DB, item *models.ReleaseItem) error DeleteReleaseItem(ctx context.Context, tx DB, id uuid.UUID) error GetCandidateItemsForRelease(ctx context.Context, tx DB, projectID uuid.UUID, releaseID *uuid.UUID) ([]models.Artifact, []models.Release, error) + FindOrCreate(ctx context.Context, tx DB, projectID uuid.UUID, name string) (models.Release, error) } type CveRepository interface { @@ -382,6 +394,7 @@ type ProjectService interface { CreateProject(ctx Context, project *models.Project) error BootstrapProject(ctx context.Context, rbac AccessControl, project *models.Project) error SearchProjectsWithSubProjectsAndAssetsPaged(c Context) (Paged[dtos.ProjectDTO], error) + FindOrCreateProject(ctx Context, providerID string, orgID uuid.UUID, name string, externalEntityID string, parentID uuid.UUID, description string) (*models.Project, error) } type InTotoVerifierService interface { @@ -397,6 +410,7 @@ type AssetService interface { CreateAsset(ctx context.Context, rbac AccessControl, currentUserID string, asset models.Asset) (*models.Asset, error) BootstrapAsset(ctx context.Context, rbac AccessControl, asset *models.Asset) error UpdateAssetSlug(ctx context.Context, assetID uuid.UUID, newSlug string) error + FindOrCreateAsset(ctx context.Context, rbac AccessControl, providerID string, orgID uuid.UUID, projectID uuid.UUID, name string, externalEntityID string, currentUser string, description string) (*models.Asset, error) } type ArtifactService interface { GetArtifactsByAssetIDAndAssetVersionName(ctx context.Context, tx DB, assetID uuid.UUID, assetVersionName string) ([]models.Artifact, error) @@ -436,6 +450,7 @@ type AssetVersionRepository interface { Delete(ctx context.Context, tx DB, assetVersion *models.AssetVersion) error Save(ctx context.Context, tx DB, assetVersion *models.AssetVersion) error GetAssetVersionsByAssetID(ctx context.Context, tx DB, assetID uuid.UUID) ([]models.AssetVersion, error) + GetAssetVersionsByAssetIDs(ctx context.Context, tx DB, assetIDs []uuid.UUID) ([]models.AssetVersion, error) GetAssetVersionsByAssetIDWithArtifacts(ctx context.Context, tx DB, assetID uuid.UUID) ([]models.AssetVersion, error) GetDefaultAssetVersionsByProjectID(ctx context.Context, tx DB, projectID uuid.UUID) ([]models.AssetVersion, error) GetDefaultAssetVersionsByProjectIDs(ctx context.Context, tx DB, projectIDs []uuid.UUID) ([]models.AssetVersion, error) diff --git a/shared/context_utils.go b/shared/context_utils.go index a942f51cf..9f23c6c2e 100644 --- a/shared/context_utils.go +++ b/shared/context_utils.go @@ -367,6 +367,15 @@ func GetProject(ctx Context) models.Project { return ctx.Get("project").(models.Project) } +func SetProviderID(ctx Context, providerID string) { + ctx.Set("providerID", providerID) +} + +func GetProviderID(ctx Context) string { + v, _ := ctx.Get("providerID").(string) + return v +} + func GetRepositoryID(asset *models.Asset) (string, error) { if asset.RepositoryID != nil { return *asset.RepositoryID, nil diff --git a/transformer/project_transformer.go b/transformer/project_transformer.go index 39aafeb09..2ff4c0072 100644 --- a/transformer/project_transformer.go +++ b/transformer/project_transformer.go @@ -25,10 +25,9 @@ import ( func ProjectCreateRequestToModel(projectCreate dtos.ProjectCreateRequest) models.Project { // check if valid type projectType := projectCreate.Type - if projectType != string(models.ProjectTypeDefault) && projectType != string(models.ProjectTypeKubernetesNamespace) { + if projectType == "" { projectType = string(models.ProjectTypeDefault) } - return models.Project{ Name: projectCreate.Name, Slug: slug.Make(projectCreate.Name),